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