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