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