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