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