1 /*
2 * PROJECT: VFAT Filesystem
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: File information routines
5 * COPYRIGHT: Copyright 1998 Jason Filby <jasonfilby@yahoo.com>
6 * Copyright 2005 Hervé Poussineau <hpoussin@reactos.org>
7 * Copyright 2008-2018 Pierre Schweitzer <pierre@reactos.org>
8 */
9
10 /* INCLUDES *****************************************************************/
11
12 #include "vfat.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 #define NASSERTS_RENAME
18
19 /* GLOBALS ******************************************************************/
20
21 const char* FileInformationClassNames[] =
22 {
23 "??????",
24 "FileDirectoryInformation",
25 "FileFullDirectoryInformation",
26 "FileBothDirectoryInformation",
27 "FileBasicInformation",
28 "FileStandardInformation",
29 "FileInternalInformation",
30 "FileEaInformation",
31 "FileAccessInformation",
32 "FileNameInformation",
33 "FileRenameInformation",
34 "FileLinkInformation",
35 "FileNamesInformation",
36 "FileDispositionInformation",
37 "FilePositionInformation",
38 "FileFullEaInformation",
39 "FileModeInformation",
40 "FileAlignmentInformation",
41 "FileAllInformation",
42 "FileAllocationInformation",
43 "FileEndOfFileInformation",
44 "FileAlternateNameInformation",
45 "FileStreamInformation",
46 "FilePipeInformation",
47 "FilePipeLocalInformation",
48 "FilePipeRemoteInformation",
49 "FileMailslotQueryInformation",
50 "FileMailslotSetInformation",
51 "FileCompressionInformation",
52 "FileObjectIdInformation",
53 "FileCompletionInformation",
54 "FileMoveClusterInformation",
55 "FileQuotaInformation",
56 "FileReparsePointInformation",
57 "FileNetworkOpenInformation",
58 "FileAttributeTagInformation",
59 "FileTrackingInformation",
60 "FileIdBothDirectoryInformation",
61 "FileIdFullDirectoryInformation",
62 "FileValidDataLengthInformation",
63 "FileShortNameInformation",
64 "FileMaximumInformation"
65 };
66
67 /* FUNCTIONS ****************************************************************/
68
69 /*
70 * FUNCTION: Retrieve the standard file information
71 */
72 NTSTATUS
VfatGetStandardInformation(PVFATFCB FCB,PFILE_STANDARD_INFORMATION StandardInfo,PULONG BufferLength)73 VfatGetStandardInformation(
74 PVFATFCB FCB,
75 PFILE_STANDARD_INFORMATION StandardInfo,
76 PULONG BufferLength)
77 {
78 if (*BufferLength < sizeof(FILE_STANDARD_INFORMATION))
79 return STATUS_BUFFER_OVERFLOW;
80
81 /* PRECONDITION */
82 ASSERT(StandardInfo != NULL);
83 ASSERT(FCB != NULL);
84
85 if (vfatFCBIsDirectory(FCB))
86 {
87 StandardInfo->AllocationSize.QuadPart = 0;
88 StandardInfo->EndOfFile.QuadPart = 0;
89 StandardInfo->Directory = TRUE;
90 }
91 else
92 {
93 StandardInfo->AllocationSize = FCB->RFCB.AllocationSize;
94 StandardInfo->EndOfFile = FCB->RFCB.FileSize;
95 StandardInfo->Directory = FALSE;
96 }
97 StandardInfo->NumberOfLinks = 1;
98 StandardInfo->DeletePending = BooleanFlagOn(FCB->Flags, FCB_DELETE_PENDING);
99
100 *BufferLength -= sizeof(FILE_STANDARD_INFORMATION);
101 return STATUS_SUCCESS;
102 }
103
104 static
105 NTSTATUS
VfatSetPositionInformation(PFILE_OBJECT FileObject,PFILE_POSITION_INFORMATION PositionInfo)106 VfatSetPositionInformation(
107 PFILE_OBJECT FileObject,
108 PFILE_POSITION_INFORMATION PositionInfo)
109 {
110 DPRINT("FsdSetPositionInformation()\n");
111
112 DPRINT("PositionInfo %p\n", PositionInfo);
113 DPRINT("Setting position %u\n", PositionInfo->CurrentByteOffset.u.LowPart);
114
115 FileObject->CurrentByteOffset.QuadPart =
116 PositionInfo->CurrentByteOffset.QuadPart;
117
118 return STATUS_SUCCESS;
119 }
120
121 static
122 NTSTATUS
VfatGetPositionInformation(PFILE_OBJECT FileObject,PVFATFCB FCB,PDEVICE_EXTENSION DeviceExt,PFILE_POSITION_INFORMATION PositionInfo,PULONG BufferLength)123 VfatGetPositionInformation(
124 PFILE_OBJECT FileObject,
125 PVFATFCB FCB,
126 PDEVICE_EXTENSION DeviceExt,
127 PFILE_POSITION_INFORMATION PositionInfo,
128 PULONG BufferLength)
129 {
130 UNREFERENCED_PARAMETER(FileObject);
131 UNREFERENCED_PARAMETER(FCB);
132 UNREFERENCED_PARAMETER(DeviceExt);
133
134 DPRINT("VfatGetPositionInformation()\n");
135
136 if (*BufferLength < sizeof(FILE_POSITION_INFORMATION))
137 return STATUS_BUFFER_OVERFLOW;
138
139 PositionInfo->CurrentByteOffset.QuadPart =
140 FileObject->CurrentByteOffset.QuadPart;
141
142 DPRINT("Getting position %I64x\n",
143 PositionInfo->CurrentByteOffset.QuadPart);
144
145 *BufferLength -= sizeof(FILE_POSITION_INFORMATION);
146 return STATUS_SUCCESS;
147 }
148
149 static
150 NTSTATUS
VfatSetBasicInformation(PFILE_OBJECT FileObject,PVFATFCB FCB,PDEVICE_EXTENSION DeviceExt,PFILE_BASIC_INFORMATION BasicInfo)151 VfatSetBasicInformation(
152 PFILE_OBJECT FileObject,
153 PVFATFCB FCB,
154 PDEVICE_EXTENSION DeviceExt,
155 PFILE_BASIC_INFORMATION BasicInfo)
156 {
157 ULONG NotifyFilter;
158
159 DPRINT("VfatSetBasicInformation()\n");
160
161 ASSERT(NULL != FileObject);
162 ASSERT(NULL != FCB);
163 ASSERT(NULL != DeviceExt);
164 ASSERT(NULL != BasicInfo);
165 /* Check volume label bit */
166 ASSERT(0 == (*FCB->Attributes & _A_VOLID));
167
168 NotifyFilter = 0;
169
170 if (BasicInfo->FileAttributes != 0)
171 {
172 UCHAR Attributes;
173
174 Attributes = (BasicInfo->FileAttributes & (FILE_ATTRIBUTE_ARCHIVE |
175 FILE_ATTRIBUTE_SYSTEM |
176 FILE_ATTRIBUTE_HIDDEN |
177 FILE_ATTRIBUTE_DIRECTORY |
178 FILE_ATTRIBUTE_READONLY));
179
180 if (vfatFCBIsDirectory(FCB))
181 {
182 if (BooleanFlagOn(BasicInfo->FileAttributes, FILE_ATTRIBUTE_TEMPORARY))
183 {
184 DPRINT("Setting temporary attribute on a directory!\n");
185 return STATUS_INVALID_PARAMETER;
186 }
187
188 Attributes |= FILE_ATTRIBUTE_DIRECTORY;
189 }
190 else
191 {
192 if (BooleanFlagOn(BasicInfo->FileAttributes, FILE_ATTRIBUTE_DIRECTORY))
193 {
194 DPRINT("Setting directory attribute on a file!\n");
195 return STATUS_INVALID_PARAMETER;
196 }
197 }
198
199 if (Attributes != *FCB->Attributes)
200 {
201 *FCB->Attributes = Attributes;
202 DPRINT("Setting attributes 0x%02x\n", *FCB->Attributes);
203 NotifyFilter |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
204 }
205 }
206
207 if (vfatVolumeIsFatX(DeviceExt))
208 {
209 if (BasicInfo->CreationTime.QuadPart != 0 && BasicInfo->CreationTime.QuadPart != -1)
210 {
211 FsdSystemTimeToDosDateTime(DeviceExt,
212 &BasicInfo->CreationTime,
213 &FCB->entry.FatX.CreationDate,
214 &FCB->entry.FatX.CreationTime);
215 NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION;
216 }
217
218 if (BasicInfo->LastAccessTime.QuadPart != 0 && BasicInfo->LastAccessTime.QuadPart != -1)
219 {
220 FsdSystemTimeToDosDateTime(DeviceExt,
221 &BasicInfo->LastAccessTime,
222 &FCB->entry.FatX.AccessDate,
223 &FCB->entry.FatX.AccessTime);
224 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
225 }
226
227 if (BasicInfo->LastWriteTime.QuadPart != 0 && BasicInfo->LastWriteTime.QuadPart != -1)
228 {
229 FsdSystemTimeToDosDateTime(DeviceExt,
230 &BasicInfo->LastWriteTime,
231 &FCB->entry.FatX.UpdateDate,
232 &FCB->entry.FatX.UpdateTime);
233 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
234 }
235 }
236 else
237 {
238 if (BasicInfo->CreationTime.QuadPart != 0 && BasicInfo->CreationTime.QuadPart != -1)
239 {
240 FsdSystemTimeToDosDateTime(DeviceExt,
241 &BasicInfo->CreationTime,
242 &FCB->entry.Fat.CreationDate,
243 &FCB->entry.Fat.CreationTime);
244 NotifyFilter |= FILE_NOTIFY_CHANGE_CREATION;
245 }
246
247 if (BasicInfo->LastAccessTime.QuadPart != 0 && BasicInfo->LastAccessTime.QuadPart != -1)
248 {
249 FsdSystemTimeToDosDateTime(DeviceExt,
250 &BasicInfo->LastAccessTime,
251 &FCB->entry.Fat.AccessDate,
252 NULL);
253 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
254 }
255
256 if (BasicInfo->LastWriteTime.QuadPart != 0 && BasicInfo->LastWriteTime.QuadPart != -1)
257 {
258 FsdSystemTimeToDosDateTime(DeviceExt,
259 &BasicInfo->LastWriteTime,
260 &FCB->entry.Fat.UpdateDate,
261 &FCB->entry.Fat.UpdateTime);
262 NotifyFilter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
263 }
264 }
265
266 VfatUpdateEntry(DeviceExt, FCB);
267
268 if (NotifyFilter != 0)
269 {
270 vfatReportChange(DeviceExt,
271 FCB,
272 NotifyFilter,
273 FILE_ACTION_MODIFIED);
274 }
275
276 return STATUS_SUCCESS;
277 }
278
279 NTSTATUS
VfatGetBasicInformation(PFILE_OBJECT FileObject,PVFATFCB FCB,PDEVICE_EXTENSION DeviceExt,PFILE_BASIC_INFORMATION BasicInfo,PULONG BufferLength)280 VfatGetBasicInformation(
281 PFILE_OBJECT FileObject,
282 PVFATFCB FCB,
283 PDEVICE_EXTENSION DeviceExt,
284 PFILE_BASIC_INFORMATION BasicInfo,
285 PULONG BufferLength)
286 {
287 UNREFERENCED_PARAMETER(FileObject);
288
289 DPRINT("VfatGetBasicInformation()\n");
290
291 if (*BufferLength < sizeof(FILE_BASIC_INFORMATION))
292 return STATUS_BUFFER_OVERFLOW;
293
294 RtlZeroMemory(BasicInfo, sizeof(FILE_BASIC_INFORMATION));
295
296 if (vfatVolumeIsFatX(DeviceExt))
297 {
298 FsdDosDateTimeToSystemTime(DeviceExt,
299 FCB->entry.FatX.CreationDate,
300 FCB->entry.FatX.CreationTime,
301 &BasicInfo->CreationTime);
302 FsdDosDateTimeToSystemTime(DeviceExt,
303 FCB->entry.FatX.AccessDate,
304 FCB->entry.FatX.AccessTime,
305 &BasicInfo->LastAccessTime);
306 FsdDosDateTimeToSystemTime(DeviceExt,
307 FCB->entry.FatX.UpdateDate,
308 FCB->entry.FatX.UpdateTime,
309 &BasicInfo->LastWriteTime);
310 BasicInfo->ChangeTime = BasicInfo->LastWriteTime;
311 }
312 else
313 {
314 FsdDosDateTimeToSystemTime(DeviceExt,
315 FCB->entry.Fat.CreationDate,
316 FCB->entry.Fat.CreationTime,
317 &BasicInfo->CreationTime);
318 FsdDosDateTimeToSystemTime(DeviceExt,
319 FCB->entry.Fat.AccessDate,
320 0,
321 &BasicInfo->LastAccessTime);
322 FsdDosDateTimeToSystemTime(DeviceExt,
323 FCB->entry.Fat.UpdateDate,
324 FCB->entry.Fat.UpdateTime,
325 &BasicInfo->LastWriteTime);
326 BasicInfo->ChangeTime = BasicInfo->LastWriteTime;
327 }
328
329 BasicInfo->FileAttributes = *FCB->Attributes & 0x3f;
330 /* Synthesize FILE_ATTRIBUTE_NORMAL */
331 if (0 == (BasicInfo->FileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
332 FILE_ATTRIBUTE_ARCHIVE |
333 FILE_ATTRIBUTE_SYSTEM |
334 FILE_ATTRIBUTE_HIDDEN |
335 FILE_ATTRIBUTE_READONLY)))
336 {
337 DPRINT("Synthesizing FILE_ATTRIBUTE_NORMAL\n");
338 BasicInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL;
339 }
340 DPRINT("Getting attributes 0x%02x\n", BasicInfo->FileAttributes);
341
342 *BufferLength -= sizeof(FILE_BASIC_INFORMATION);
343 return STATUS_SUCCESS;
344 }
345
346
347 static
348 NTSTATUS
VfatSetDispositionInformation(PFILE_OBJECT FileObject,PVFATFCB FCB,PDEVICE_EXTENSION DeviceExt,PFILE_DISPOSITION_INFORMATION DispositionInfo)349 VfatSetDispositionInformation(
350 PFILE_OBJECT FileObject,
351 PVFATFCB FCB,
352 PDEVICE_EXTENSION DeviceExt,
353 PFILE_DISPOSITION_INFORMATION DispositionInfo)
354 {
355 DPRINT("FsdSetDispositionInformation(<%wZ>, Delete %u)\n", &FCB->PathNameU, DispositionInfo->DeleteFile);
356
357 ASSERT(DeviceExt != NULL);
358 ASSERT(DeviceExt->FatInfo.BytesPerCluster != 0);
359 ASSERT(FCB != NULL);
360
361 if (!DispositionInfo->DeleteFile)
362 {
363 /* undelete the file */
364 FCB->Flags &= ~FCB_DELETE_PENDING;
365 FileObject->DeletePending = FALSE;
366 return STATUS_SUCCESS;
367 }
368
369 if (BooleanFlagOn(FCB->Flags, FCB_DELETE_PENDING))
370 {
371 /* stream already marked for deletion. just update the file object */
372 FileObject->DeletePending = TRUE;
373 return STATUS_SUCCESS;
374 }
375
376 if (vfatFCBIsReadOnly(FCB))
377 {
378 return STATUS_CANNOT_DELETE;
379 }
380
381 if (vfatFCBIsRoot(FCB) || IsDotOrDotDot(&FCB->LongNameU))
382 {
383 /* we cannot delete a '.', '..' or the root directory */
384 return STATUS_ACCESS_DENIED;
385 }
386
387 if (!MmFlushImageSection (FileObject->SectionObjectPointer, MmFlushForDelete))
388 {
389 /* can't delete a file if its mapped into a process */
390
391 DPRINT("MmFlushImageSection returned FALSE\n");
392 return STATUS_CANNOT_DELETE;
393 }
394
395 if (vfatFCBIsDirectory(FCB) && !VfatIsDirectoryEmpty(DeviceExt, FCB))
396 {
397 /* can't delete a non-empty directory */
398
399 return STATUS_DIRECTORY_NOT_EMPTY;
400 }
401
402 /* all good */
403 FCB->Flags |= FCB_DELETE_PENDING;
404 FileObject->DeletePending = TRUE;
405
406 return STATUS_SUCCESS;
407 }
408
409 static NTSTATUS
vfatPrepareTargetForRename(IN PDEVICE_EXTENSION DeviceExt,IN PVFATFCB * ParentFCB,IN PUNICODE_STRING NewName,IN BOOLEAN ReplaceIfExists,IN PUNICODE_STRING ParentName,OUT PBOOLEAN Deleted)410 vfatPrepareTargetForRename(
411 IN PDEVICE_EXTENSION DeviceExt,
412 IN PVFATFCB * ParentFCB,
413 IN PUNICODE_STRING NewName,
414 IN BOOLEAN ReplaceIfExists,
415 IN PUNICODE_STRING ParentName,
416 OUT PBOOLEAN Deleted)
417 {
418 NTSTATUS Status;
419 PVFATFCB TargetFcb;
420
421 DPRINT("vfatPrepareTargetForRename(%p, %p, %wZ, %d, %wZ, %p)\n", DeviceExt, ParentFCB, NewName, ReplaceIfExists, ParentName);
422
423 *Deleted = FALSE;
424 /* Try to open target */
425 Status = vfatGetFCBForFile(DeviceExt, ParentFCB, &TargetFcb, NewName);
426 /* If it exists */
427 if (NT_SUCCESS(Status))
428 {
429 DPRINT("Target file %wZ exists. FCB Flags %08x\n", NewName, TargetFcb->Flags);
430 /* Check whether we are allowed to replace */
431 if (ReplaceIfExists)
432 {
433 /* If that's a directory or a read-only file, we're not allowed */
434 if (vfatFCBIsDirectory(TargetFcb) || vfatFCBIsReadOnly(TargetFcb))
435 {
436 DPRINT("And this is a readonly file!\n");
437 vfatReleaseFCB(DeviceExt, *ParentFCB);
438 *ParentFCB = NULL;
439 vfatReleaseFCB(DeviceExt, TargetFcb);
440 return STATUS_OBJECT_NAME_COLLISION;
441 }
442
443
444 /* If we still have a file object, close it. */
445 if (TargetFcb->FileObject)
446 {
447 if (!MmFlushImageSection(TargetFcb->FileObject->SectionObjectPointer, MmFlushForDelete))
448 {
449 DPRINT("MmFlushImageSection failed.\n");
450 vfatReleaseFCB(DeviceExt, *ParentFCB);
451 *ParentFCB = NULL;
452 vfatReleaseFCB(DeviceExt, TargetFcb);
453 return STATUS_ACCESS_DENIED;
454 }
455
456 TargetFcb->FileObject->DeletePending = TRUE;
457 VfatCloseFile(DeviceExt, TargetFcb->FileObject);
458 }
459
460 /* If we are here, ensure the file isn't open by anyone! */
461 if (TargetFcb->OpenHandleCount != 0)
462 {
463 DPRINT("There are still open handles for this file.\n");
464 vfatReleaseFCB(DeviceExt, *ParentFCB);
465 *ParentFCB = NULL;
466 vfatReleaseFCB(DeviceExt, TargetFcb);
467 return STATUS_ACCESS_DENIED;
468 }
469
470 /* Effectively delete old file to allow renaming */
471 DPRINT("Effectively deleting the file.\n");
472 VfatDelEntry(DeviceExt, TargetFcb, NULL);
473 vfatReleaseFCB(DeviceExt, TargetFcb);
474 *Deleted = TRUE;
475 return STATUS_SUCCESS;
476 }
477 else
478 {
479 vfatReleaseFCB(DeviceExt, *ParentFCB);
480 *ParentFCB = NULL;
481 vfatReleaseFCB(DeviceExt, TargetFcb);
482 return STATUS_OBJECT_NAME_COLLISION;
483 }
484 }
485 else if (*ParentFCB != NULL)
486 {
487 return STATUS_SUCCESS;
488 }
489
490 /* Failure */
491 return Status;
492 }
493
494 static
495 BOOLEAN
IsThereAChildOpened(PVFATFCB FCB)496 IsThereAChildOpened(PVFATFCB FCB)
497 {
498 PLIST_ENTRY Entry;
499 PVFATFCB VolFCB;
500
501 for (Entry = FCB->ParentListHead.Flink; Entry != &FCB->ParentListHead; Entry = Entry->Flink)
502 {
503 VolFCB = CONTAINING_RECORD(Entry, VFATFCB, ParentListEntry);
504 if (VolFCB->OpenHandleCount != 0)
505 {
506 ASSERT(VolFCB->parentFcb == FCB);
507 DPRINT1("At least one children file opened! %wZ (%u, %u)\n", &VolFCB->PathNameU, VolFCB->RefCount, VolFCB->OpenHandleCount);
508 return TRUE;
509 }
510
511 if (vfatFCBIsDirectory(VolFCB) && !IsListEmpty(&VolFCB->ParentListHead))
512 {
513 if (IsThereAChildOpened(VolFCB))
514 {
515 return TRUE;
516 }
517 }
518 }
519
520 return FALSE;
521 }
522
523 static
524 VOID
VfatRenameChildFCB(PDEVICE_EXTENSION DeviceExt,PVFATFCB FCB)525 VfatRenameChildFCB(
526 PDEVICE_EXTENSION DeviceExt,
527 PVFATFCB FCB)
528 {
529 PLIST_ENTRY Entry;
530 PVFATFCB Child;
531
532 if (IsListEmpty(&FCB->ParentListHead))
533 return;
534
535 for (Entry = FCB->ParentListHead.Flink; Entry != &FCB->ParentListHead; Entry = Entry->Flink)
536 {
537 NTSTATUS Status;
538
539 Child = CONTAINING_RECORD(Entry, VFATFCB, ParentListEntry);
540 DPRINT("Found %wZ with still %lu references (parent: %lu)!\n", &Child->PathNameU, Child->RefCount, FCB->RefCount);
541
542 Status = vfatSetFCBNewDirName(DeviceExt, Child, FCB);
543 if (!NT_SUCCESS(Status))
544 continue;
545
546 if (vfatFCBIsDirectory(Child))
547 {
548 VfatRenameChildFCB(DeviceExt, Child);
549 }
550 }
551 }
552
553 /*
554 * FUNCTION: Set the file name information
555 */
556 static
557 NTSTATUS
VfatSetRenameInformation(PFILE_OBJECT FileObject,PVFATFCB FCB,PDEVICE_EXTENSION DeviceExt,PFILE_RENAME_INFORMATION RenameInfo,PFILE_OBJECT TargetFileObject)558 VfatSetRenameInformation(
559 PFILE_OBJECT FileObject,
560 PVFATFCB FCB,
561 PDEVICE_EXTENSION DeviceExt,
562 PFILE_RENAME_INFORMATION RenameInfo,
563 PFILE_OBJECT TargetFileObject)
564 {
565 #ifdef NASSERTS_RENAME
566 #pragma push_macro("ASSERT")
567 #undef ASSERT
568 #define ASSERT(x) ((VOID) 0)
569 #endif
570 NTSTATUS Status;
571 UNICODE_STRING NewName;
572 UNICODE_STRING SourcePath;
573 UNICODE_STRING SourceFile;
574 UNICODE_STRING NewPath;
575 UNICODE_STRING NewFile;
576 PFILE_OBJECT RootFileObject;
577 PVFATFCB RootFCB;
578 UNICODE_STRING RenameInfoString;
579 PVFATFCB ParentFCB;
580 IO_STATUS_BLOCK IoStatusBlock;
581 OBJECT_ATTRIBUTES ObjectAttributes;
582 HANDLE TargetHandle;
583 BOOLEAN DeletedTarget;
584 ULONG OldReferences, NewReferences;
585 PVFATFCB OldParent;
586
587 DPRINT("VfatSetRenameInfo(%p, %p, %p, %p, %p)\n", FileObject, FCB, DeviceExt, RenameInfo, TargetFileObject);
588
589 /* Disallow renaming root */
590 if (vfatFCBIsRoot(FCB))
591 {
592 return STATUS_INVALID_PARAMETER;
593 }
594
595 OldReferences = FCB->parentFcb->RefCount;
596 #ifdef NASSERTS_RENAME
597 UNREFERENCED_PARAMETER(OldReferences);
598 #endif
599
600 /* If we are performing relative opening for rename, get FO for getting FCB and path name */
601 if (RenameInfo->RootDirectory != NULL)
602 {
603 /* We cannot tolerate relative opening with a full path */
604 if (RenameInfo->FileName[0] == L'\\')
605 {
606 return STATUS_OBJECT_NAME_INVALID;
607 }
608
609 Status = ObReferenceObjectByHandle(RenameInfo->RootDirectory,
610 FILE_READ_DATA,
611 *IoFileObjectType,
612 ExGetPreviousMode(),
613 (PVOID *)&RootFileObject,
614 NULL);
615 if (!NT_SUCCESS(Status))
616 {
617 return Status;
618 }
619
620 RootFCB = RootFileObject->FsContext;
621 }
622
623 RtlInitEmptyUnicodeString(&NewName, NULL, 0);
624 ParentFCB = NULL;
625
626 if (TargetFileObject == NULL)
627 {
628 /* If we don't have target file object, construct paths thanks to relative FCB, if any, and with
629 * information supplied by the user
630 */
631
632 /* First, setup a string we'll work on */
633 RenameInfoString.Length = RenameInfo->FileNameLength;
634 RenameInfoString.MaximumLength = RenameInfo->FileNameLength;
635 RenameInfoString.Buffer = RenameInfo->FileName;
636
637 /* Check whether we have FQN */
638 if (RenameInfoString.Length > 6 * sizeof(WCHAR))
639 {
640 if (RenameInfoString.Buffer[0] == L'\\' && RenameInfoString.Buffer[1] == L'?' &&
641 RenameInfoString.Buffer[2] == L'?' && RenameInfoString.Buffer[3] == L'\\' &&
642 RenameInfoString.Buffer[5] == L':' && (RenameInfoString.Buffer[4] >= L'A' &&
643 RenameInfoString.Buffer[4] <= L'Z'))
644 {
645 /* If so, open its target directory */
646 InitializeObjectAttributes(&ObjectAttributes,
647 &RenameInfoString,
648 OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
649 NULL, NULL);
650
651 Status = IoCreateFile(&TargetHandle,
652 FILE_WRITE_DATA | SYNCHRONIZE,
653 &ObjectAttributes,
654 &IoStatusBlock,
655 NULL, 0,
656 FILE_SHARE_READ | FILE_SHARE_WRITE,
657 FILE_OPEN,
658 FILE_OPEN_FOR_BACKUP_INTENT,
659 NULL, 0,
660 CreateFileTypeNone,
661 NULL,
662 IO_FORCE_ACCESS_CHECK | IO_OPEN_TARGET_DIRECTORY);
663 if (!NT_SUCCESS(Status))
664 {
665 goto Cleanup;
666 }
667
668 /* Get its FO to get the FCB */
669 Status = ObReferenceObjectByHandle(TargetHandle,
670 FILE_WRITE_DATA,
671 *IoFileObjectType,
672 KernelMode,
673 (PVOID *)&TargetFileObject,
674 NULL);
675 if (!NT_SUCCESS(Status))
676 {
677 ZwClose(TargetHandle);
678 goto Cleanup;
679 }
680
681 /* Are we working on the same volume? */
682 if (IoGetRelatedDeviceObject(TargetFileObject) != IoGetRelatedDeviceObject(FileObject))
683 {
684 ObDereferenceObject(TargetFileObject);
685 ZwClose(TargetHandle);
686 TargetFileObject = NULL;
687 Status = STATUS_NOT_SAME_DEVICE;
688 goto Cleanup;
689 }
690 }
691 }
692
693 NewName.Length = 0;
694 NewName.MaximumLength = RenameInfo->FileNameLength;
695 if (RenameInfo->RootDirectory != NULL)
696 {
697 NewName.MaximumLength += sizeof(WCHAR) + RootFCB->PathNameU.Length;
698 }
699 else if (RenameInfo->FileName[0] != L'\\')
700 {
701 /* We don't have full path, and we don't have root directory:
702 * => we move inside the same directory
703 */
704 NewName.MaximumLength += sizeof(WCHAR) + FCB->DirNameU.Length;
705 }
706 else if (TargetFileObject != NULL)
707 {
708 /* We had a FQN:
709 * => we need to use its correct path
710 */
711 NewName.MaximumLength += sizeof(WCHAR) + ((PVFATFCB)TargetFileObject->FsContext)->PathNameU.Length;
712 }
713
714 NewName.Buffer = ExAllocatePoolWithTag(NonPagedPool, NewName.MaximumLength, TAG_NAME);
715 if (NewName.Buffer == NULL)
716 {
717 if (TargetFileObject != NULL)
718 {
719 ObDereferenceObject(TargetFileObject);
720 ZwClose(TargetHandle);
721 TargetFileObject = NULL;
722 }
723 Status = STATUS_INSUFFICIENT_RESOURCES;
724 goto Cleanup;
725 }
726
727 if (RenameInfo->RootDirectory != NULL)
728 {
729 /* Here, copy first absolute and then append relative */
730 RtlCopyUnicodeString(&NewName, &RootFCB->PathNameU);
731 NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\';
732 NewName.Length += sizeof(WCHAR);
733 RtlAppendUnicodeStringToString(&NewName, &RenameInfoString);
734 }
735 else if (RenameInfo->FileName[0] != L'\\')
736 {
737 /* Here, copy first work directory and then append filename */
738 RtlCopyUnicodeString(&NewName, &FCB->DirNameU);
739 NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\';
740 NewName.Length += sizeof(WCHAR);
741 RtlAppendUnicodeStringToString(&NewName, &RenameInfoString);
742 }
743 else if (TargetFileObject != NULL)
744 {
745 /* Here, copy first path name and then append filename */
746 RtlCopyUnicodeString(&NewName, &((PVFATFCB)TargetFileObject->FsContext)->PathNameU);
747 NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\';
748 NewName.Length += sizeof(WCHAR);
749 RtlAppendUnicodeStringToString(&NewName, &RenameInfoString);
750 }
751 else
752 {
753 /* Here we should have full path, so simply copy it */
754 RtlCopyUnicodeString(&NewName, &RenameInfoString);
755 }
756
757 /* Do we have to cleanup some stuff? */
758 if (TargetFileObject != NULL)
759 {
760 ObDereferenceObject(TargetFileObject);
761 ZwClose(TargetHandle);
762 TargetFileObject = NULL;
763 }
764 }
765 else
766 {
767 /* At that point, we shouldn't care about whether we are relative opening
768 * Target FO FCB should already have full path
769 */
770
771 /* Before constructing string, just make a sanity check (just to be sure!) */
772 if (IoGetRelatedDeviceObject(TargetFileObject) != IoGetRelatedDeviceObject(FileObject))
773 {
774 Status = STATUS_NOT_SAME_DEVICE;
775 goto Cleanup;
776 }
777
778 NewName.Length = 0;
779 NewName.MaximumLength = TargetFileObject->FileName.Length + ((PVFATFCB)TargetFileObject->FsContext)->PathNameU.Length + sizeof(WCHAR);
780 NewName.Buffer = ExAllocatePoolWithTag(NonPagedPool, NewName.MaximumLength, TAG_NAME);
781 if (NewName.Buffer == NULL)
782 {
783 Status = STATUS_INSUFFICIENT_RESOURCES;
784 goto Cleanup;
785 }
786
787 RtlCopyUnicodeString(&NewName, &((PVFATFCB)TargetFileObject->FsContext)->PathNameU);
788 /* If \, it's already backslash terminated, don't add it */
789 if (!vfatFCBIsRoot(TargetFileObject->FsContext))
790 {
791 NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\';
792 NewName.Length += sizeof(WCHAR);
793 }
794 RtlAppendUnicodeStringToString(&NewName, &TargetFileObject->FileName);
795 }
796
797 /* Explode our paths to get path & filename */
798 vfatSplitPathName(&FCB->PathNameU, &SourcePath, &SourceFile);
799 DPRINT("Old dir: %wZ, Old file: %wZ\n", &SourcePath, &SourceFile);
800 vfatSplitPathName(&NewName, &NewPath, &NewFile);
801 DPRINT("New dir: %wZ, New file: %wZ\n", &NewPath, &NewFile);
802
803 if (IsDotOrDotDot(&NewFile))
804 {
805 Status = STATUS_OBJECT_NAME_INVALID;
806 goto Cleanup;
807 }
808
809 if (vfatFCBIsDirectory(FCB) && !IsListEmpty(&FCB->ParentListHead))
810 {
811 if (IsThereAChildOpened(FCB))
812 {
813 Status = STATUS_ACCESS_DENIED;
814 ASSERT(OldReferences == FCB->parentFcb->RefCount);
815 goto Cleanup;
816 }
817 }
818
819 /* Are we working in place? */
820 if (FsRtlAreNamesEqual(&SourcePath, &NewPath, TRUE, NULL))
821 {
822 if (FsRtlAreNamesEqual(&SourceFile, &NewFile, FALSE, NULL))
823 {
824 Status = STATUS_SUCCESS;
825 ASSERT(OldReferences == FCB->parentFcb->RefCount);
826 goto Cleanup;
827 }
828
829 if (FsRtlAreNamesEqual(&SourceFile, &NewFile, TRUE, NULL))
830 {
831 vfatReportChange(DeviceExt,
832 FCB,
833 (vfatFCBIsDirectory(FCB) ?
834 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
835 FILE_ACTION_RENAMED_OLD_NAME);
836 Status = vfatRenameEntry(DeviceExt, FCB, &NewFile, TRUE);
837 if (NT_SUCCESS(Status))
838 {
839 vfatReportChange(DeviceExt,
840 FCB,
841 (vfatFCBIsDirectory(FCB) ?
842 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
843 FILE_ACTION_RENAMED_NEW_NAME);
844 }
845 }
846 else
847 {
848 /* Try to find target */
849 ParentFCB = FCB->parentFcb;
850 vfatGrabFCB(DeviceExt, ParentFCB);
851 Status = vfatPrepareTargetForRename(DeviceExt,
852 &ParentFCB,
853 &NewFile,
854 RenameInfo->ReplaceIfExists,
855 &NewPath,
856 &DeletedTarget);
857 if (!NT_SUCCESS(Status))
858 {
859 ASSERT(OldReferences == FCB->parentFcb->RefCount - 1);
860 ASSERT(OldReferences == ParentFCB->RefCount - 1);
861 goto Cleanup;
862 }
863
864 vfatReportChange(DeviceExt,
865 FCB,
866 (vfatFCBIsDirectory(FCB) ?
867 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
868 (DeletedTarget ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME));
869 Status = vfatRenameEntry(DeviceExt, FCB, &NewFile, FALSE);
870 if (NT_SUCCESS(Status))
871 {
872 if (DeletedTarget)
873 {
874 vfatReportChange(DeviceExt,
875 FCB,
876 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE
877 | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA,
878 FILE_ACTION_MODIFIED);
879 }
880 else
881 {
882 vfatReportChange(DeviceExt,
883 FCB,
884 (vfatFCBIsDirectory(FCB) ?
885 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
886 FILE_ACTION_RENAMED_NEW_NAME);
887 }
888 }
889 }
890
891 ASSERT(OldReferences == FCB->parentFcb->RefCount - 1); // extra grab
892 ASSERT(OldReferences == ParentFCB->RefCount - 1); // extra grab
893 }
894 else
895 {
896
897 /* Try to find target */
898 ParentFCB = NULL;
899 OldParent = FCB->parentFcb;
900 #ifdef NASSERTS_RENAME
901 UNREFERENCED_PARAMETER(OldParent);
902 #endif
903 Status = vfatPrepareTargetForRename(DeviceExt,
904 &ParentFCB,
905 &NewName,
906 RenameInfo->ReplaceIfExists,
907 &NewPath,
908 &DeletedTarget);
909 if (!NT_SUCCESS(Status))
910 {
911 ASSERT(OldReferences == FCB->parentFcb->RefCount);
912 goto Cleanup;
913 }
914
915 NewReferences = ParentFCB->RefCount;
916 #ifdef NASSERTS_RENAME
917 UNREFERENCED_PARAMETER(NewReferences);
918 #endif
919
920 vfatReportChange(DeviceExt,
921 FCB,
922 (vfatFCBIsDirectory(FCB) ?
923 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
924 FILE_ACTION_REMOVED);
925 Status = VfatMoveEntry(DeviceExt, FCB, &NewFile, ParentFCB);
926 if (NT_SUCCESS(Status))
927 {
928 if (DeletedTarget)
929 {
930 vfatReportChange(DeviceExt,
931 FCB,
932 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE
933 | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA,
934 FILE_ACTION_MODIFIED);
935 }
936 else
937 {
938 vfatReportChange(DeviceExt,
939 FCB,
940 (vfatFCBIsDirectory(FCB) ?
941 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
942 FILE_ACTION_ADDED);
943 }
944 }
945 }
946
947 if (NT_SUCCESS(Status) && vfatFCBIsDirectory(FCB))
948 {
949 VfatRenameChildFCB(DeviceExt, FCB);
950 }
951
952 ASSERT(OldReferences == OldParent->RefCount + 1); // removed file
953 ASSERT(NewReferences == ParentFCB->RefCount - 1); // new file
954 Cleanup:
955 if (ParentFCB != NULL) vfatReleaseFCB(DeviceExt, ParentFCB);
956 if (NewName.Buffer != NULL) ExFreePoolWithTag(NewName.Buffer, TAG_NAME);
957 if (RenameInfo->RootDirectory != NULL) ObDereferenceObject(RootFileObject);
958
959 return Status;
960 #ifdef NASSERTS_RENAME
961 #pragma pop_macro("ASSERT")
962 #endif
963 }
964
965 /*
966 * FUNCTION: Retrieve the file name information
967 */
968 static
969 NTSTATUS
VfatGetNameInformation(PFILE_OBJECT FileObject,PVFATFCB FCB,PDEVICE_EXTENSION DeviceExt,PFILE_NAME_INFORMATION NameInfo,PULONG BufferLength)970 VfatGetNameInformation(
971 PFILE_OBJECT FileObject,
972 PVFATFCB FCB,
973 PDEVICE_EXTENSION DeviceExt,
974 PFILE_NAME_INFORMATION NameInfo,
975 PULONG BufferLength)
976 {
977 ULONG BytesToCopy;
978
979 UNREFERENCED_PARAMETER(FileObject);
980 UNREFERENCED_PARAMETER(DeviceExt);
981
982 ASSERT(NameInfo != NULL);
983 ASSERT(FCB != NULL);
984
985 /* If buffer can't hold at least the file name length, bail out */
986 if (*BufferLength < (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]))
987 return STATUS_BUFFER_OVERFLOW;
988
989 /* Save file name length, and as much file len, as buffer length allows */
990 NameInfo->FileNameLength = FCB->PathNameU.Length;
991
992 /* Calculate amount of bytes to copy not to overflow the buffer */
993 BytesToCopy = min(FCB->PathNameU.Length,
994 *BufferLength - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]));
995
996 /* Fill in the bytes */
997 RtlCopyMemory(NameInfo->FileName, FCB->PathNameU.Buffer, BytesToCopy);
998
999 /* Check if we could write more but are not able to */
1000 if (*BufferLength < FCB->PathNameU.Length + (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]))
1001 {
1002 /* Return number of bytes written */
1003 *BufferLength -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + BytesToCopy;
1004 return STATUS_BUFFER_OVERFLOW;
1005 }
1006
1007 /* We filled up as many bytes, as needed */
1008 *BufferLength -= (FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + FCB->PathNameU.Length);
1009
1010 return STATUS_SUCCESS;
1011 }
1012
1013 static
1014 NTSTATUS
VfatGetInternalInformation(PVFATFCB Fcb,PDEVICE_EXTENSION DeviceExt,PFILE_INTERNAL_INFORMATION InternalInfo,PULONG BufferLength)1015 VfatGetInternalInformation(
1016 PVFATFCB Fcb,
1017 PDEVICE_EXTENSION DeviceExt,
1018 PFILE_INTERNAL_INFORMATION InternalInfo,
1019 PULONG BufferLength)
1020 {
1021 ASSERT(InternalInfo);
1022 ASSERT(Fcb);
1023
1024 if (*BufferLength < sizeof(FILE_INTERNAL_INFORMATION))
1025 return STATUS_BUFFER_OVERFLOW;
1026
1027 InternalInfo->IndexNumber.QuadPart = (LONGLONG)vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry) * DeviceExt->FatInfo.BytesPerCluster;
1028
1029 *BufferLength -= sizeof(FILE_INTERNAL_INFORMATION);
1030 return STATUS_SUCCESS;
1031 }
1032
1033
1034 /*
1035 * FUNCTION: Retrieve the file network open information
1036 */
1037 static
1038 NTSTATUS
VfatGetNetworkOpenInformation(PVFATFCB Fcb,PDEVICE_EXTENSION DeviceExt,PFILE_NETWORK_OPEN_INFORMATION NetworkInfo,PULONG BufferLength)1039 VfatGetNetworkOpenInformation(
1040 PVFATFCB Fcb,
1041 PDEVICE_EXTENSION DeviceExt,
1042 PFILE_NETWORK_OPEN_INFORMATION NetworkInfo,
1043 PULONG BufferLength)
1044 {
1045 ASSERT(NetworkInfo);
1046 ASSERT(Fcb);
1047
1048 if (*BufferLength < sizeof(FILE_NETWORK_OPEN_INFORMATION))
1049 return(STATUS_BUFFER_OVERFLOW);
1050
1051 if (vfatVolumeIsFatX(DeviceExt))
1052 {
1053 FsdDosDateTimeToSystemTime(DeviceExt,
1054 Fcb->entry.FatX.CreationDate,
1055 Fcb->entry.FatX.CreationTime,
1056 &NetworkInfo->CreationTime);
1057 FsdDosDateTimeToSystemTime(DeviceExt,
1058 Fcb->entry.FatX.AccessDate,
1059 Fcb->entry.FatX.AccessTime,
1060 &NetworkInfo->LastAccessTime);
1061 FsdDosDateTimeToSystemTime(DeviceExt,
1062 Fcb->entry.FatX.UpdateDate,
1063 Fcb->entry.FatX.UpdateTime,
1064 &NetworkInfo->LastWriteTime);
1065 NetworkInfo->ChangeTime.QuadPart = NetworkInfo->LastWriteTime.QuadPart;
1066 }
1067 else
1068 {
1069 FsdDosDateTimeToSystemTime(DeviceExt,
1070 Fcb->entry.Fat.CreationDate,
1071 Fcb->entry.Fat.CreationTime,
1072 &NetworkInfo->CreationTime);
1073 FsdDosDateTimeToSystemTime(DeviceExt,
1074 Fcb->entry.Fat.AccessDate,
1075 0,
1076 &NetworkInfo->LastAccessTime);
1077 FsdDosDateTimeToSystemTime(DeviceExt,
1078 Fcb->entry.Fat.UpdateDate,
1079 Fcb->entry.Fat.UpdateTime,
1080 &NetworkInfo->LastWriteTime);
1081 NetworkInfo->ChangeTime.QuadPart = NetworkInfo->LastWriteTime.QuadPart;
1082 }
1083
1084 if (vfatFCBIsDirectory(Fcb))
1085 {
1086 NetworkInfo->EndOfFile.QuadPart = 0L;
1087 NetworkInfo->AllocationSize.QuadPart = 0L;
1088 }
1089 else
1090 {
1091 NetworkInfo->AllocationSize = Fcb->RFCB.AllocationSize;
1092 NetworkInfo->EndOfFile = Fcb->RFCB.FileSize;
1093 }
1094
1095 NetworkInfo->FileAttributes = *Fcb->Attributes & 0x3f;
1096 /* Synthesize FILE_ATTRIBUTE_NORMAL */
1097 if (0 == (NetworkInfo->FileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
1098 FILE_ATTRIBUTE_ARCHIVE |
1099 FILE_ATTRIBUTE_SYSTEM |
1100 FILE_ATTRIBUTE_HIDDEN |
1101 FILE_ATTRIBUTE_READONLY)))
1102 {
1103 DPRINT("Synthesizing FILE_ATTRIBUTE_NORMAL\n");
1104 NetworkInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL;
1105 }
1106
1107 *BufferLength -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
1108 return STATUS_SUCCESS;
1109 }
1110
1111
1112 static
1113 NTSTATUS
VfatGetEaInformation(PFILE_OBJECT FileObject,PVFATFCB Fcb,PDEVICE_EXTENSION DeviceExt,PFILE_EA_INFORMATION Info,PULONG BufferLength)1114 VfatGetEaInformation(
1115 PFILE_OBJECT FileObject,
1116 PVFATFCB Fcb,
1117 PDEVICE_EXTENSION DeviceExt,
1118 PFILE_EA_INFORMATION Info,
1119 PULONG BufferLength)
1120 {
1121 UNREFERENCED_PARAMETER(FileObject);
1122 UNREFERENCED_PARAMETER(Fcb);
1123
1124 /* FIXME - use SEH to access the buffer! */
1125 Info->EaSize = 0;
1126 *BufferLength -= sizeof(*Info);
1127 if (DeviceExt->FatInfo.FatType == FAT12 ||
1128 DeviceExt->FatInfo.FatType == FAT16)
1129 {
1130 /* FIXME */
1131 DPRINT1("VFAT: FileEaInformation not implemented!\n");
1132 }
1133 return STATUS_SUCCESS;
1134 }
1135
1136
1137 /*
1138 * FUNCTION: Retrieve the all file information
1139 */
1140 static
1141 NTSTATUS
VfatGetAllInformation(PFILE_OBJECT FileObject,PVFATFCB Fcb,PDEVICE_EXTENSION DeviceExt,PFILE_ALL_INFORMATION Info,PULONG BufferLength)1142 VfatGetAllInformation(
1143 PFILE_OBJECT FileObject,
1144 PVFATFCB Fcb,
1145 PDEVICE_EXTENSION DeviceExt,
1146 PFILE_ALL_INFORMATION Info,
1147 PULONG BufferLength)
1148 {
1149 NTSTATUS Status;
1150
1151 ASSERT(Info);
1152 ASSERT(Fcb);
1153
1154 if (*BufferLength < FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName))
1155 return STATUS_BUFFER_OVERFLOW;
1156
1157 *BufferLength -= (sizeof(FILE_ACCESS_INFORMATION) + sizeof(FILE_MODE_INFORMATION) + sizeof(FILE_ALIGNMENT_INFORMATION));
1158
1159 /* Basic Information */
1160 Status = VfatGetBasicInformation(FileObject, Fcb, DeviceExt, &Info->BasicInformation, BufferLength);
1161 if (!NT_SUCCESS(Status)) return Status;
1162 /* Standard Information */
1163 Status = VfatGetStandardInformation(Fcb, &Info->StandardInformation, BufferLength);
1164 if (!NT_SUCCESS(Status)) return Status;
1165 /* Internal Information */
1166 Status = VfatGetInternalInformation(Fcb, DeviceExt, &Info->InternalInformation, BufferLength);
1167 if (!NT_SUCCESS(Status)) return Status;
1168 /* EA Information */
1169 Status = VfatGetEaInformation(FileObject, Fcb, DeviceExt, &Info->EaInformation, BufferLength);
1170 if (!NT_SUCCESS(Status)) return Status;
1171 /* Position Information */
1172 Status = VfatGetPositionInformation(FileObject, Fcb, DeviceExt, &Info->PositionInformation, BufferLength);
1173 if (!NT_SUCCESS(Status)) return Status;
1174 /* Name Information */
1175 Status = VfatGetNameInformation(FileObject, Fcb, DeviceExt, &Info->NameInformation, BufferLength);
1176
1177 return Status;
1178 }
1179
1180 static
1181 VOID
UpdateFileSize(PFILE_OBJECT FileObject,PVFATFCB Fcb,ULONG Size,ULONG ClusterSize,BOOLEAN IsFatX)1182 UpdateFileSize(
1183 PFILE_OBJECT FileObject,
1184 PVFATFCB Fcb,
1185 ULONG Size,
1186 ULONG ClusterSize,
1187 BOOLEAN IsFatX)
1188 {
1189 if (Size > 0)
1190 {
1191 Fcb->RFCB.AllocationSize.QuadPart = ROUND_UP_64(Size, ClusterSize);
1192 }
1193 else
1194 {
1195 Fcb->RFCB.AllocationSize.QuadPart = (LONGLONG)0;
1196 }
1197 if (!vfatFCBIsDirectory(Fcb))
1198 {
1199 if (IsFatX)
1200 Fcb->entry.FatX.FileSize = Size;
1201 else
1202 Fcb->entry.Fat.FileSize = Size;
1203 }
1204 Fcb->RFCB.FileSize.QuadPart = Size;
1205 Fcb->RFCB.ValidDataLength.QuadPart = Size;
1206
1207 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
1208 }
1209
1210 NTSTATUS
VfatSetAllocationSizeInformation(PFILE_OBJECT FileObject,PVFATFCB Fcb,PDEVICE_EXTENSION DeviceExt,PLARGE_INTEGER AllocationSize)1211 VfatSetAllocationSizeInformation(
1212 PFILE_OBJECT FileObject,
1213 PVFATFCB Fcb,
1214 PDEVICE_EXTENSION DeviceExt,
1215 PLARGE_INTEGER AllocationSize)
1216 {
1217 ULONG OldSize;
1218 ULONG Cluster, FirstCluster;
1219 NTSTATUS Status;
1220
1221 ULONG ClusterSize = DeviceExt->FatInfo.BytesPerCluster;
1222 ULONG NewSize = AllocationSize->u.LowPart;
1223 ULONG NCluster;
1224 BOOLEAN AllocSizeChanged = FALSE, IsFatX = vfatVolumeIsFatX(DeviceExt);
1225
1226 DPRINT("VfatSetAllocationSizeInformation(File <%wZ>, AllocationSize %d %u)\n",
1227 &Fcb->PathNameU, AllocationSize->HighPart, AllocationSize->LowPart);
1228
1229 if (IsFatX)
1230 OldSize = Fcb->entry.FatX.FileSize;
1231 else
1232 OldSize = Fcb->entry.Fat.FileSize;
1233
1234 if (AllocationSize->u.HighPart > 0)
1235 {
1236 return STATUS_INVALID_PARAMETER;
1237 }
1238
1239 if (OldSize == NewSize)
1240 {
1241 return STATUS_SUCCESS;
1242 }
1243
1244 FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry);
1245
1246 if (NewSize > Fcb->RFCB.AllocationSize.u.LowPart)
1247 {
1248 AllocSizeChanged = TRUE;
1249 if (FirstCluster == 0)
1250 {
1251 Fcb->LastCluster = Fcb->LastOffset = 0;
1252 Status = NextCluster(DeviceExt, FirstCluster, &FirstCluster, TRUE);
1253 if (!NT_SUCCESS(Status))
1254 {
1255 DPRINT1("NextCluster failed. Status = %x\n", Status);
1256 return Status;
1257 }
1258
1259 if (FirstCluster == 0xffffffff)
1260 {
1261 return STATUS_DISK_FULL;
1262 }
1263
1264 Status = OffsetToCluster(DeviceExt, FirstCluster,
1265 ROUND_DOWN(NewSize - 1, ClusterSize),
1266 &NCluster, TRUE);
1267 if (NCluster == 0xffffffff || !NT_SUCCESS(Status))
1268 {
1269 /* disk is full */
1270 NCluster = Cluster = FirstCluster;
1271 Status = STATUS_SUCCESS;
1272 while (NT_SUCCESS(Status) && Cluster != 0xffffffff && Cluster > 1)
1273 {
1274 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1275 WriteCluster(DeviceExt, Cluster, 0);
1276 Cluster = NCluster;
1277 }
1278 return STATUS_DISK_FULL;
1279 }
1280
1281 if (IsFatX)
1282 {
1283 Fcb->entry.FatX.FirstCluster = FirstCluster;
1284 }
1285 else
1286 {
1287 if (DeviceExt->FatInfo.FatType == FAT32)
1288 {
1289 Fcb->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0x0000FFFF);
1290 Fcb->entry.Fat.FirstClusterHigh = FirstCluster >> 16;
1291 }
1292 else
1293 {
1294 ASSERT((FirstCluster >> 16) == 0);
1295 Fcb->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0x0000FFFF);
1296 }
1297 }
1298 }
1299 else
1300 {
1301 if (Fcb->LastCluster > 0)
1302 {
1303 if (Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize == Fcb->LastOffset)
1304 {
1305 Cluster = Fcb->LastCluster;
1306 Status = STATUS_SUCCESS;
1307 }
1308 else
1309 {
1310 Status = OffsetToCluster(DeviceExt, Fcb->LastCluster,
1311 Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize - Fcb->LastOffset,
1312 &Cluster, FALSE);
1313 }
1314 }
1315 else
1316 {
1317 Status = OffsetToCluster(DeviceExt, FirstCluster,
1318 Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize,
1319 &Cluster, FALSE);
1320 }
1321
1322 if (!NT_SUCCESS(Status))
1323 {
1324 return Status;
1325 }
1326
1327 Fcb->LastCluster = Cluster;
1328 Fcb->LastOffset = Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize;
1329
1330 /* FIXME: Check status */
1331 /* Cluster points now to the last cluster within the chain */
1332 Status = OffsetToCluster(DeviceExt, Cluster,
1333 ROUND_DOWN(NewSize - 1, ClusterSize) - Fcb->LastOffset,
1334 &NCluster, TRUE);
1335 if (NCluster == 0xffffffff || !NT_SUCCESS(Status))
1336 {
1337 /* disk is full */
1338 NCluster = Cluster;
1339 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1340 WriteCluster(DeviceExt, Cluster, 0xffffffff);
1341 Cluster = NCluster;
1342 while (NT_SUCCESS(Status) && Cluster != 0xffffffff && Cluster > 1)
1343 {
1344 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1345 WriteCluster(DeviceExt, Cluster, 0);
1346 Cluster = NCluster;
1347 }
1348 return STATUS_DISK_FULL;
1349 }
1350 }
1351 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize, vfatVolumeIsFatX(DeviceExt));
1352 }
1353 else if (NewSize + ClusterSize <= Fcb->RFCB.AllocationSize.u.LowPart)
1354 {
1355 DPRINT("Check for the ability to set file size\n");
1356 if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer,
1357 (PLARGE_INTEGER)AllocationSize))
1358 {
1359 DPRINT("Couldn't set file size!\n");
1360 return STATUS_USER_MAPPED_FILE;
1361 }
1362 DPRINT("Can set file size\n");
1363
1364 AllocSizeChanged = TRUE;
1365 /* FIXME: Use the cached cluster/offset better way. */
1366 Fcb->LastCluster = Fcb->LastOffset = 0;
1367 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize, vfatVolumeIsFatX(DeviceExt));
1368 if (NewSize > 0)
1369 {
1370 Status = OffsetToCluster(DeviceExt, FirstCluster,
1371 ROUND_DOWN(NewSize - 1, ClusterSize),
1372 &Cluster, FALSE);
1373
1374 NCluster = Cluster;
1375 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1376 WriteCluster(DeviceExt, Cluster, 0xffffffff);
1377 Cluster = NCluster;
1378 }
1379 else
1380 {
1381 if (IsFatX)
1382 {
1383 Fcb->entry.FatX.FirstCluster = 0;
1384 }
1385 else
1386 {
1387 if (DeviceExt->FatInfo.FatType == FAT32)
1388 {
1389 Fcb->entry.Fat.FirstCluster = 0;
1390 Fcb->entry.Fat.FirstClusterHigh = 0;
1391 }
1392 else
1393 {
1394 Fcb->entry.Fat.FirstCluster = 0;
1395 }
1396 }
1397
1398 NCluster = Cluster = FirstCluster;
1399 Status = STATUS_SUCCESS;
1400 }
1401
1402 while (NT_SUCCESS(Status) && 0xffffffff != Cluster && Cluster > 1)
1403 {
1404 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1405 WriteCluster(DeviceExt, Cluster, 0);
1406 Cluster = NCluster;
1407 }
1408
1409 if (DeviceExt->FatInfo.FatType == FAT32)
1410 {
1411 FAT32UpdateFreeClustersCount(DeviceExt);
1412 }
1413 }
1414 else
1415 {
1416 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize, vfatVolumeIsFatX(DeviceExt));
1417 }
1418
1419 /* Update the on-disk directory entry */
1420 Fcb->Flags |= FCB_IS_DIRTY;
1421 if (AllocSizeChanged)
1422 {
1423 VfatUpdateEntry(DeviceExt, Fcb);
1424
1425 vfatReportChange(DeviceExt, Fcb, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED);
1426 }
1427 return STATUS_SUCCESS;
1428 }
1429
1430 /*
1431 * FUNCTION: Retrieve the specified file information
1432 */
1433 NTSTATUS
VfatQueryInformation(PVFAT_IRP_CONTEXT IrpContext)1434 VfatQueryInformation(
1435 PVFAT_IRP_CONTEXT IrpContext)
1436 {
1437 FILE_INFORMATION_CLASS FileInformationClass;
1438 PVFATFCB FCB;
1439
1440 NTSTATUS Status = STATUS_SUCCESS;
1441 PVOID SystemBuffer;
1442 ULONG BufferLength;
1443
1444 /* PRECONDITION */
1445 ASSERT(IrpContext);
1446
1447 /* INITIALIZATION */
1448 FileInformationClass = IrpContext->Stack->Parameters.QueryFile.FileInformationClass;
1449 FCB = (PVFATFCB) IrpContext->FileObject->FsContext;
1450
1451 DPRINT("VfatQueryInformation is called for '%s'\n",
1452 FileInformationClass >= FileMaximumInformation - 1 ? "????" : FileInformationClassNames[FileInformationClass]);
1453
1454 if (FCB == NULL)
1455 {
1456 DPRINT1("IRP_MJ_QUERY_INFORMATION without FCB!\n");
1457 IrpContext->Irp->IoStatus.Information = 0;
1458 return STATUS_INVALID_PARAMETER;
1459 }
1460
1461 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
1462 BufferLength = IrpContext->Stack->Parameters.QueryFile.Length;
1463
1464 if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE))
1465 {
1466 if (!ExAcquireResourceSharedLite(&FCB->MainResource,
1467 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1468 {
1469 return VfatMarkIrpContextForQueue(IrpContext);
1470 }
1471 }
1472
1473 switch (FileInformationClass)
1474 {
1475 case FileStandardInformation:
1476 Status = VfatGetStandardInformation(FCB,
1477 SystemBuffer,
1478 &BufferLength);
1479 break;
1480
1481 case FilePositionInformation:
1482 Status = VfatGetPositionInformation(IrpContext->FileObject,
1483 FCB,
1484 IrpContext->DeviceExt,
1485 SystemBuffer,
1486 &BufferLength);
1487 break;
1488
1489 case FileBasicInformation:
1490 Status = VfatGetBasicInformation(IrpContext->FileObject,
1491 FCB,
1492 IrpContext->DeviceExt,
1493 SystemBuffer,
1494 &BufferLength);
1495 break;
1496
1497 case FileNameInformation:
1498 Status = VfatGetNameInformation(IrpContext->FileObject,
1499 FCB,
1500 IrpContext->DeviceExt,
1501 SystemBuffer,
1502 &BufferLength);
1503 break;
1504
1505 case FileInternalInformation:
1506 Status = VfatGetInternalInformation(FCB,
1507 IrpContext->DeviceExt,
1508 SystemBuffer,
1509 &BufferLength);
1510 break;
1511
1512 case FileNetworkOpenInformation:
1513 Status = VfatGetNetworkOpenInformation(FCB,
1514 IrpContext->DeviceExt,
1515 SystemBuffer,
1516 &BufferLength);
1517 break;
1518
1519 case FileAllInformation:
1520 Status = VfatGetAllInformation(IrpContext->FileObject,
1521 FCB,
1522 IrpContext->DeviceExt,
1523 SystemBuffer,
1524 &BufferLength);
1525 break;
1526
1527 case FileEaInformation:
1528 Status = VfatGetEaInformation(IrpContext->FileObject,
1529 FCB,
1530 IrpContext->DeviceExt,
1531 SystemBuffer,
1532 &BufferLength);
1533 break;
1534
1535 case FileAlternateNameInformation:
1536 Status = STATUS_NOT_IMPLEMENTED;
1537 break;
1538
1539 default:
1540 Status = STATUS_INVALID_PARAMETER;
1541 }
1542
1543 if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE))
1544 {
1545 ExReleaseResourceLite(&FCB->MainResource);
1546 }
1547
1548 if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW)
1549 IrpContext->Irp->IoStatus.Information =
1550 IrpContext->Stack->Parameters.QueryFile.Length - BufferLength;
1551 else
1552 IrpContext->Irp->IoStatus.Information = 0;
1553
1554 return Status;
1555 }
1556
1557 /*
1558 * FUNCTION: Retrieve the specified file information
1559 */
1560 NTSTATUS
VfatSetInformation(PVFAT_IRP_CONTEXT IrpContext)1561 VfatSetInformation(
1562 PVFAT_IRP_CONTEXT IrpContext)
1563 {
1564 FILE_INFORMATION_CLASS FileInformationClass;
1565 PVFATFCB FCB;
1566 NTSTATUS Status = STATUS_SUCCESS;
1567 PVOID SystemBuffer;
1568 BOOLEAN LockDir;
1569
1570 /* PRECONDITION */
1571 ASSERT(IrpContext);
1572
1573 DPRINT("VfatSetInformation(IrpContext %p)\n", IrpContext);
1574
1575 /* INITIALIZATION */
1576 FileInformationClass =
1577 IrpContext->Stack->Parameters.SetFile.FileInformationClass;
1578 FCB = (PVFATFCB) IrpContext->FileObject->FsContext;
1579 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
1580
1581 DPRINT("VfatSetInformation is called for '%s'\n",
1582 FileInformationClass >= FileMaximumInformation - 1 ? "????" : FileInformationClassNames[ FileInformationClass]);
1583
1584 DPRINT("FileInformationClass %d\n", FileInformationClass);
1585 DPRINT("SystemBuffer %p\n", SystemBuffer);
1586
1587 if (FCB == NULL)
1588 {
1589 DPRINT1("IRP_MJ_SET_INFORMATION without FCB!\n");
1590 IrpContext->Irp->IoStatus.Information = 0;
1591 return STATUS_INVALID_PARAMETER;
1592 }
1593
1594 /* Special: We should call MmCanFileBeTruncated here to determine if changing
1595 the file size would be allowed. If not, we bail with the right error.
1596 We must do this before acquiring the lock. */
1597 if (FileInformationClass == FileEndOfFileInformation)
1598 {
1599 DPRINT("Check for the ability to set file size\n");
1600 if (!MmCanFileBeTruncated(IrpContext->FileObject->SectionObjectPointer,
1601 (PLARGE_INTEGER)SystemBuffer))
1602 {
1603 DPRINT("Couldn't set file size!\n");
1604 IrpContext->Irp->IoStatus.Information = 0;
1605 return STATUS_USER_MAPPED_FILE;
1606 }
1607 DPRINT("Can set file size\n");
1608 }
1609
1610 LockDir = FALSE;
1611 if (FileInformationClass == FileRenameInformation || FileInformationClass == FileAllocationInformation ||
1612 FileInformationClass == FileEndOfFileInformation || FileInformationClass == FileBasicInformation)
1613 {
1614 LockDir = TRUE;
1615 }
1616
1617 if (LockDir)
1618 {
1619 if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
1620 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1621 {
1622 return VfatMarkIrpContextForQueue(IrpContext);
1623 }
1624 }
1625
1626 if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE))
1627 {
1628 if (!ExAcquireResourceExclusiveLite(&FCB->MainResource,
1629 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1630 {
1631 if (LockDir)
1632 {
1633 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
1634 }
1635
1636 return VfatMarkIrpContextForQueue(IrpContext);
1637 }
1638 }
1639
1640 switch (FileInformationClass)
1641 {
1642 case FilePositionInformation:
1643 Status = VfatSetPositionInformation(IrpContext->FileObject,
1644 SystemBuffer);
1645 break;
1646
1647 case FileDispositionInformation:
1648 Status = VfatSetDispositionInformation(IrpContext->FileObject,
1649 FCB,
1650 IrpContext->DeviceExt,
1651 SystemBuffer);
1652 break;
1653
1654 case FileAllocationInformation:
1655 case FileEndOfFileInformation:
1656 Status = VfatSetAllocationSizeInformation(IrpContext->FileObject,
1657 FCB,
1658 IrpContext->DeviceExt,
1659 (PLARGE_INTEGER)SystemBuffer);
1660 break;
1661
1662 case FileBasicInformation:
1663 Status = VfatSetBasicInformation(IrpContext->FileObject,
1664 FCB,
1665 IrpContext->DeviceExt,
1666 SystemBuffer);
1667 break;
1668
1669 case FileRenameInformation:
1670 Status = VfatSetRenameInformation(IrpContext->FileObject,
1671 FCB,
1672 IrpContext->DeviceExt,
1673 SystemBuffer,
1674 IrpContext->Stack->Parameters.SetFile.FileObject);
1675 break;
1676
1677 default:
1678 Status = STATUS_NOT_SUPPORTED;
1679 }
1680
1681 if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE))
1682 {
1683 ExReleaseResourceLite(&FCB->MainResource);
1684 }
1685
1686 if (LockDir)
1687 {
1688 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
1689 }
1690
1691 IrpContext->Irp->IoStatus.Information = 0;
1692 return Status;
1693 }
1694
1695 /* EOF */
1696