[FASTFAT] Don't add an extra \, when renaming a file at root.
[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 /* If \, it's already backslash terminated, don't add it */
791 if (!vfatFCBIsRoot(TargetFileObject->FsContext))
792 {
793 NewName.Buffer[NewName.Length / sizeof(WCHAR)] = L'\\';
794 NewName.Length += sizeof(WCHAR);
795 }
796 RtlAppendUnicodeStringToString(&NewName, &TargetFileObject->FileName);
797 }
798
799 /* Explode our paths to get path & filename */
800 vfatSplitPathName(&FCB->PathNameU, &SourcePath, &SourceFile);
801 DPRINT("Old dir: %wZ, Old file: %wZ\n", &SourcePath, &SourceFile);
802 vfatSplitPathName(&NewName, &NewPath, &NewFile);
803 DPRINT("New dir: %wZ, New file: %wZ\n", &NewPath, &NewFile);
804
805 if (vfatFCBIsDirectory(FCB) && !IsListEmpty(&FCB->ParentListHead))
806 {
807 if (IsThereAChildOpened(FCB))
808 {
809 Status = STATUS_ACCESS_DENIED;
810 ASSERT(OldReferences == FCB->parentFcb->RefCount);
811 goto Cleanup;
812 }
813 }
814
815 /* Are we working in place? */
816 if (FsRtlAreNamesEqual(&SourcePath, &NewPath, TRUE, NULL))
817 {
818 if (FsRtlAreNamesEqual(&SourceFile, &NewFile, FALSE, NULL))
819 {
820 Status = STATUS_SUCCESS;
821 ASSERT(OldReferences == FCB->parentFcb->RefCount);
822 goto Cleanup;
823 }
824
825 if (FsRtlAreNamesEqual(&SourceFile, &NewFile, TRUE, NULL))
826 {
827 vfatReportChange(DeviceExt,
828 FCB,
829 (vfatFCBIsDirectory(FCB) ?
830 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
831 FILE_ACTION_RENAMED_OLD_NAME);
832 Status = vfatRenameEntry(DeviceExt, FCB, &NewFile, TRUE);
833 if (NT_SUCCESS(Status))
834 {
835 vfatReportChange(DeviceExt,
836 FCB,
837 (vfatFCBIsDirectory(FCB) ?
838 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
839 FILE_ACTION_RENAMED_NEW_NAME);
840 }
841 }
842 else
843 {
844 /* Try to find target */
845 ParentFCB = FCB->parentFcb;
846 vfatGrabFCB(DeviceExt, ParentFCB);
847 Status = vfatPrepareTargetForRename(DeviceExt,
848 &ParentFCB,
849 &NewFile,
850 RenameInfo->ReplaceIfExists,
851 &NewPath,
852 &DeletedTarget);
853 if (!NT_SUCCESS(Status))
854 {
855 ASSERT(OldReferences == FCB->parentFcb->RefCount - 1);
856 ASSERT(OldReferences == ParentFCB->RefCount - 1);
857 goto Cleanup;
858 }
859
860 vfatReportChange(DeviceExt,
861 FCB,
862 (vfatFCBIsDirectory(FCB) ?
863 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
864 (DeletedTarget ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME));
865 Status = vfatRenameEntry(DeviceExt, FCB, &NewFile, FALSE);
866 if (NT_SUCCESS(Status))
867 {
868 if (DeletedTarget)
869 {
870 vfatReportChange(DeviceExt,
871 FCB,
872 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE
873 | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA,
874 FILE_ACTION_MODIFIED);
875 }
876 else
877 {
878 vfatReportChange(DeviceExt,
879 FCB,
880 (vfatFCBIsDirectory(FCB) ?
881 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
882 FILE_ACTION_RENAMED_NEW_NAME);
883 }
884 }
885 }
886
887 ASSERT(OldReferences == FCB->parentFcb->RefCount - 1); // extra grab
888 ASSERT(OldReferences == ParentFCB->RefCount - 1); // extra grab
889 }
890 else
891 {
892
893 /* Try to find target */
894 ParentFCB = NULL;
895 OldParent = FCB->parentFcb;
896 #ifdef NASSERTS_RENAME
897 UNREFERENCED_PARAMETER(OldParent);
898 #endif
899 Status = vfatPrepareTargetForRename(DeviceExt,
900 &ParentFCB,
901 &NewName,
902 RenameInfo->ReplaceIfExists,
903 &NewPath,
904 &DeletedTarget);
905 if (!NT_SUCCESS(Status))
906 {
907 ASSERT(OldReferences == FCB->parentFcb->RefCount);
908 goto Cleanup;
909 }
910
911 NewReferences = ParentFCB->RefCount;
912 #ifdef NASSERTS_RENAME
913 UNREFERENCED_PARAMETER(NewReferences);
914 #endif
915
916 vfatReportChange(DeviceExt,
917 FCB,
918 (vfatFCBIsDirectory(FCB) ?
919 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
920 FILE_ACTION_REMOVED);
921 Status = VfatMoveEntry(DeviceExt, FCB, &NewFile, ParentFCB);
922 if (NT_SUCCESS(Status))
923 {
924 if (DeletedTarget)
925 {
926 vfatReportChange(DeviceExt,
927 FCB,
928 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE
929 | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA,
930 FILE_ACTION_MODIFIED);
931 }
932 else
933 {
934 vfatReportChange(DeviceExt,
935 FCB,
936 (vfatFCBIsDirectory(FCB) ?
937 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
938 FILE_ACTION_ADDED);
939 }
940 }
941 }
942
943 if (NT_SUCCESS(Status) && vfatFCBIsDirectory(FCB))
944 {
945 VfatRenameChildFCB(DeviceExt, FCB);
946 }
947
948 ASSERT(OldReferences == OldParent->RefCount + 1); // removed file
949 ASSERT(NewReferences == ParentFCB->RefCount - 1); // new file
950 Cleanup:
951 if (ParentFCB != NULL) vfatReleaseFCB(DeviceExt, ParentFCB);
952 if (NewName.Buffer != NULL) ExFreePoolWithTag(NewName.Buffer, TAG_VFAT);
953 if (RenameInfo->RootDirectory != NULL) ObDereferenceObject(RootFileObject);
954
955 return Status;
956 #ifdef NASSERTS_RENAME
957 #pragma pop_macro("ASSERT")
958 #endif
959 }
960
961 /*
962 * FUNCTION: Retrieve the file name information
963 */
964 static
965 NTSTATUS
966 VfatGetNameInformation(
967 PFILE_OBJECT FileObject,
968 PVFATFCB FCB,
969 PDEVICE_EXTENSION DeviceExt,
970 PFILE_NAME_INFORMATION NameInfo,
971 PULONG BufferLength)
972 {
973 ULONG BytesToCopy;
974
975 UNREFERENCED_PARAMETER(FileObject);
976 UNREFERENCED_PARAMETER(DeviceExt);
977
978 ASSERT(NameInfo != NULL);
979 ASSERT(FCB != NULL);
980
981 /* If buffer can't hold at least the file name length, bail out */
982 if (*BufferLength < (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]))
983 return STATUS_BUFFER_OVERFLOW;
984
985 /* Save file name length, and as much file len, as buffer length allows */
986 NameInfo->FileNameLength = FCB->PathNameU.Length;
987
988 /* Calculate amount of bytes to copy not to overflow the buffer */
989 BytesToCopy = min(FCB->PathNameU.Length,
990 *BufferLength - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]));
991
992 /* Fill in the bytes */
993 RtlCopyMemory(NameInfo->FileName, FCB->PathNameU.Buffer, BytesToCopy);
994
995 /* Check if we could write more but are not able to */
996 if (*BufferLength < FCB->PathNameU.Length + (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]))
997 {
998 /* Return number of bytes written */
999 *BufferLength -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + BytesToCopy;
1000 return STATUS_BUFFER_OVERFLOW;
1001 }
1002
1003 /* We filled up as many bytes, as needed */
1004 *BufferLength -= (FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + FCB->PathNameU.Length);
1005
1006 return STATUS_SUCCESS;
1007 }
1008
1009 static
1010 NTSTATUS
1011 VfatGetInternalInformation(
1012 PVFATFCB Fcb,
1013 PDEVICE_EXTENSION DeviceExt,
1014 PFILE_INTERNAL_INFORMATION InternalInfo,
1015 PULONG BufferLength)
1016 {
1017 ASSERT(InternalInfo);
1018 ASSERT(Fcb);
1019
1020 if (*BufferLength < sizeof(FILE_INTERNAL_INFORMATION))
1021 return STATUS_BUFFER_OVERFLOW;
1022
1023 InternalInfo->IndexNumber.QuadPart = (LONGLONG)vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry) * DeviceExt->FatInfo.BytesPerCluster;
1024
1025 *BufferLength -= sizeof(FILE_INTERNAL_INFORMATION);
1026 return STATUS_SUCCESS;
1027 }
1028
1029
1030 /*
1031 * FUNCTION: Retrieve the file network open information
1032 */
1033 static
1034 NTSTATUS
1035 VfatGetNetworkOpenInformation(
1036 PVFATFCB Fcb,
1037 PDEVICE_EXTENSION DeviceExt,
1038 PFILE_NETWORK_OPEN_INFORMATION NetworkInfo,
1039 PULONG BufferLength)
1040 {
1041 ASSERT(NetworkInfo);
1042 ASSERT(Fcb);
1043
1044 if (*BufferLength < sizeof(FILE_NETWORK_OPEN_INFORMATION))
1045 return(STATUS_BUFFER_OVERFLOW);
1046
1047 if (vfatVolumeIsFatX(DeviceExt))
1048 {
1049 FsdDosDateTimeToSystemTime(DeviceExt,
1050 Fcb->entry.FatX.CreationDate,
1051 Fcb->entry.FatX.CreationTime,
1052 &NetworkInfo->CreationTime);
1053 FsdDosDateTimeToSystemTime(DeviceExt,
1054 Fcb->entry.FatX.AccessDate,
1055 Fcb->entry.FatX.AccessTime,
1056 &NetworkInfo->LastAccessTime);
1057 FsdDosDateTimeToSystemTime(DeviceExt,
1058 Fcb->entry.FatX.UpdateDate,
1059 Fcb->entry.FatX.UpdateTime,
1060 &NetworkInfo->LastWriteTime);
1061 NetworkInfo->ChangeTime.QuadPart = NetworkInfo->LastWriteTime.QuadPart;
1062 }
1063 else
1064 {
1065 FsdDosDateTimeToSystemTime(DeviceExt,
1066 Fcb->entry.Fat.CreationDate,
1067 Fcb->entry.Fat.CreationTime,
1068 &NetworkInfo->CreationTime);
1069 FsdDosDateTimeToSystemTime(DeviceExt,
1070 Fcb->entry.Fat.AccessDate,
1071 0,
1072 &NetworkInfo->LastAccessTime);
1073 FsdDosDateTimeToSystemTime(DeviceExt,
1074 Fcb->entry.Fat.UpdateDate,
1075 Fcb->entry.Fat.UpdateTime,
1076 &NetworkInfo->LastWriteTime);
1077 NetworkInfo->ChangeTime.QuadPart = NetworkInfo->LastWriteTime.QuadPart;
1078 }
1079
1080 if (vfatFCBIsDirectory(Fcb))
1081 {
1082 NetworkInfo->EndOfFile.QuadPart = 0L;
1083 NetworkInfo->AllocationSize.QuadPart = 0L;
1084 }
1085 else
1086 {
1087 NetworkInfo->AllocationSize = Fcb->RFCB.AllocationSize;
1088 NetworkInfo->EndOfFile = Fcb->RFCB.FileSize;
1089 }
1090
1091 NetworkInfo->FileAttributes = *Fcb->Attributes & 0x3f;
1092 /* Synthesize FILE_ATTRIBUTE_NORMAL */
1093 if (0 == (NetworkInfo->FileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
1094 FILE_ATTRIBUTE_ARCHIVE |
1095 FILE_ATTRIBUTE_SYSTEM |
1096 FILE_ATTRIBUTE_HIDDEN |
1097 FILE_ATTRIBUTE_READONLY)))
1098 {
1099 DPRINT("Synthesizing FILE_ATTRIBUTE_NORMAL\n");
1100 NetworkInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL;
1101 }
1102
1103 *BufferLength -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
1104 return STATUS_SUCCESS;
1105 }
1106
1107
1108 static
1109 NTSTATUS
1110 VfatGetEaInformation(
1111 PFILE_OBJECT FileObject,
1112 PVFATFCB Fcb,
1113 PDEVICE_EXTENSION DeviceExt,
1114 PFILE_EA_INFORMATION Info,
1115 PULONG BufferLength)
1116 {
1117 UNREFERENCED_PARAMETER(FileObject);
1118 UNREFERENCED_PARAMETER(Fcb);
1119
1120 /* FIXME - use SEH to access the buffer! */
1121 Info->EaSize = 0;
1122 *BufferLength -= sizeof(*Info);
1123 if (DeviceExt->FatInfo.FatType == FAT12 ||
1124 DeviceExt->FatInfo.FatType == FAT16)
1125 {
1126 /* FIXME */
1127 DPRINT1("VFAT: FileEaInformation not implemented!\n");
1128 }
1129 return STATUS_SUCCESS;
1130 }
1131
1132
1133 /*
1134 * FUNCTION: Retrieve the all file information
1135 */
1136 static
1137 NTSTATUS
1138 VfatGetAllInformation(
1139 PFILE_OBJECT FileObject,
1140 PVFATFCB Fcb,
1141 PDEVICE_EXTENSION DeviceExt,
1142 PFILE_ALL_INFORMATION Info,
1143 PULONG BufferLength)
1144 {
1145 NTSTATUS Status;
1146
1147 ASSERT(Info);
1148 ASSERT(Fcb);
1149
1150 if (*BufferLength < FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName))
1151 return STATUS_BUFFER_OVERFLOW;
1152
1153 *BufferLength -= (sizeof(FILE_ACCESS_INFORMATION) + sizeof(FILE_MODE_INFORMATION) + sizeof(FILE_ALIGNMENT_INFORMATION));
1154
1155 /* Basic Information */
1156 Status = VfatGetBasicInformation(FileObject, Fcb, DeviceExt, &Info->BasicInformation, BufferLength);
1157 if (!NT_SUCCESS(Status)) return Status;
1158 /* Standard Information */
1159 Status = VfatGetStandardInformation(Fcb, &Info->StandardInformation, BufferLength);
1160 if (!NT_SUCCESS(Status)) return Status;
1161 /* Internal Information */
1162 Status = VfatGetInternalInformation(Fcb, DeviceExt, &Info->InternalInformation, BufferLength);
1163 if (!NT_SUCCESS(Status)) return Status;
1164 /* EA Information */
1165 Status = VfatGetEaInformation(FileObject, Fcb, DeviceExt, &Info->EaInformation, BufferLength);
1166 if (!NT_SUCCESS(Status)) return Status;
1167 /* Position Information */
1168 Status = VfatGetPositionInformation(FileObject, Fcb, DeviceExt, &Info->PositionInformation, BufferLength);
1169 if (!NT_SUCCESS(Status)) return Status;
1170 /* Name Information */
1171 Status = VfatGetNameInformation(FileObject, Fcb, DeviceExt, &Info->NameInformation, BufferLength);
1172
1173 return Status;
1174 }
1175
1176 static
1177 VOID
1178 UpdateFileSize(
1179 PFILE_OBJECT FileObject,
1180 PVFATFCB Fcb,
1181 ULONG Size,
1182 ULONG ClusterSize,
1183 BOOLEAN IsFatX)
1184 {
1185 if (Size > 0)
1186 {
1187 Fcb->RFCB.AllocationSize.QuadPart = ROUND_UP_64(Size, ClusterSize);
1188 }
1189 else
1190 {
1191 Fcb->RFCB.AllocationSize.QuadPart = (LONGLONG)0;
1192 }
1193 if (!vfatFCBIsDirectory(Fcb))
1194 {
1195 if (IsFatX)
1196 Fcb->entry.FatX.FileSize = Size;
1197 else
1198 Fcb->entry.Fat.FileSize = Size;
1199 }
1200 Fcb->RFCB.FileSize.QuadPart = Size;
1201 Fcb->RFCB.ValidDataLength.QuadPart = Size;
1202
1203 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
1204 }
1205
1206 NTSTATUS
1207 VfatSetAllocationSizeInformation(
1208 PFILE_OBJECT FileObject,
1209 PVFATFCB Fcb,
1210 PDEVICE_EXTENSION DeviceExt,
1211 PLARGE_INTEGER AllocationSize)
1212 {
1213 ULONG OldSize;
1214 ULONG Cluster, FirstCluster;
1215 NTSTATUS Status;
1216
1217 ULONG ClusterSize = DeviceExt->FatInfo.BytesPerCluster;
1218 ULONG NewSize = AllocationSize->u.LowPart;
1219 ULONG NCluster;
1220 BOOLEAN AllocSizeChanged = FALSE, IsFatX = vfatVolumeIsFatX(DeviceExt);
1221
1222 DPRINT("VfatSetAllocationSizeInformation(File <%wZ>, AllocationSize %d %u)\n",
1223 &Fcb->PathNameU, AllocationSize->HighPart, AllocationSize->LowPart);
1224
1225 if (IsFatX)
1226 OldSize = Fcb->entry.FatX.FileSize;
1227 else
1228 OldSize = Fcb->entry.Fat.FileSize;
1229
1230 if (AllocationSize->u.HighPart > 0)
1231 {
1232 return STATUS_INVALID_PARAMETER;
1233 }
1234
1235 if (OldSize == NewSize)
1236 {
1237 return STATUS_SUCCESS;
1238 }
1239
1240 FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry);
1241
1242 if (NewSize > Fcb->RFCB.AllocationSize.u.LowPart)
1243 {
1244 AllocSizeChanged = TRUE;
1245 if (FirstCluster == 0)
1246 {
1247 Fcb->LastCluster = Fcb->LastOffset = 0;
1248 Status = NextCluster(DeviceExt, FirstCluster, &FirstCluster, TRUE);
1249 if (!NT_SUCCESS(Status))
1250 {
1251 DPRINT1("NextCluster failed. Status = %x\n", Status);
1252 return Status;
1253 }
1254
1255 if (FirstCluster == 0xffffffff)
1256 {
1257 return STATUS_DISK_FULL;
1258 }
1259
1260 Status = OffsetToCluster(DeviceExt, FirstCluster,
1261 ROUND_DOWN(NewSize - 1, ClusterSize),
1262 &NCluster, TRUE);
1263 if (NCluster == 0xffffffff || !NT_SUCCESS(Status))
1264 {
1265 /* disk is full */
1266 NCluster = Cluster = FirstCluster;
1267 Status = STATUS_SUCCESS;
1268 while (NT_SUCCESS(Status) && Cluster != 0xffffffff && Cluster > 1)
1269 {
1270 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1271 WriteCluster(DeviceExt, Cluster, 0);
1272 Cluster = NCluster;
1273 }
1274 return STATUS_DISK_FULL;
1275 }
1276
1277 if (IsFatX)
1278 {
1279 Fcb->entry.FatX.FirstCluster = FirstCluster;
1280 }
1281 else
1282 {
1283 if (DeviceExt->FatInfo.FatType == FAT32)
1284 {
1285 Fcb->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0x0000FFFF);
1286 Fcb->entry.Fat.FirstClusterHigh = FirstCluster >> 16;
1287 }
1288 else
1289 {
1290 ASSERT((FirstCluster >> 16) == 0);
1291 Fcb->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0x0000FFFF);
1292 }
1293 }
1294 }
1295 else
1296 {
1297 if (Fcb->LastCluster > 0)
1298 {
1299 if (Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize == Fcb->LastOffset)
1300 {
1301 Cluster = Fcb->LastCluster;
1302 Status = STATUS_SUCCESS;
1303 }
1304 else
1305 {
1306 Status = OffsetToCluster(DeviceExt, Fcb->LastCluster,
1307 Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize - Fcb->LastOffset,
1308 &Cluster, FALSE);
1309 }
1310 }
1311 else
1312 {
1313 Status = OffsetToCluster(DeviceExt, FirstCluster,
1314 Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize,
1315 &Cluster, FALSE);
1316 }
1317
1318 if (!NT_SUCCESS(Status))
1319 {
1320 return Status;
1321 }
1322
1323 Fcb->LastCluster = Cluster;
1324 Fcb->LastOffset = Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize;
1325
1326 /* FIXME: Check status */
1327 /* Cluster points now to the last cluster within the chain */
1328 Status = OffsetToCluster(DeviceExt, Cluster,
1329 ROUND_DOWN(NewSize - 1, ClusterSize) - Fcb->LastOffset,
1330 &NCluster, TRUE);
1331 if (NCluster == 0xffffffff || !NT_SUCCESS(Status))
1332 {
1333 /* disk is full */
1334 NCluster = Cluster;
1335 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1336 WriteCluster(DeviceExt, Cluster, 0xffffffff);
1337 Cluster = NCluster;
1338 while (NT_SUCCESS(Status) && Cluster != 0xffffffff && Cluster > 1)
1339 {
1340 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1341 WriteCluster(DeviceExt, Cluster, 0);
1342 Cluster = NCluster;
1343 }
1344 return STATUS_DISK_FULL;
1345 }
1346 }
1347 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize, vfatVolumeIsFatX(DeviceExt));
1348 }
1349 else if (NewSize + ClusterSize <= Fcb->RFCB.AllocationSize.u.LowPart)
1350 {
1351 DPRINT("Check for the ability to set file size\n");
1352 if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer,
1353 (PLARGE_INTEGER)AllocationSize))
1354 {
1355 DPRINT("Couldn't set file size!\n");
1356 return STATUS_USER_MAPPED_FILE;
1357 }
1358 DPRINT("Can set file size\n");
1359
1360 AllocSizeChanged = TRUE;
1361 /* FIXME: Use the cached cluster/offset better way. */
1362 Fcb->LastCluster = Fcb->LastOffset = 0;
1363 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize, vfatVolumeIsFatX(DeviceExt));
1364 if (NewSize > 0)
1365 {
1366 Status = OffsetToCluster(DeviceExt, FirstCluster,
1367 ROUND_DOWN(NewSize - 1, ClusterSize),
1368 &Cluster, FALSE);
1369
1370 NCluster = Cluster;
1371 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1372 WriteCluster(DeviceExt, Cluster, 0xffffffff);
1373 Cluster = NCluster;
1374 }
1375 else
1376 {
1377 if (IsFatX)
1378 {
1379 Fcb->entry.FatX.FirstCluster = 0;
1380 }
1381 else
1382 {
1383 if (DeviceExt->FatInfo.FatType == FAT32)
1384 {
1385 Fcb->entry.Fat.FirstCluster = 0;
1386 Fcb->entry.Fat.FirstClusterHigh = 0;
1387 }
1388 else
1389 {
1390 Fcb->entry.Fat.FirstCluster = 0;
1391 }
1392 }
1393
1394 NCluster = Cluster = FirstCluster;
1395 Status = STATUS_SUCCESS;
1396 }
1397
1398 while (NT_SUCCESS(Status) && 0xffffffff != Cluster && Cluster > 1)
1399 {
1400 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1401 WriteCluster(DeviceExt, Cluster, 0);
1402 Cluster = NCluster;
1403 }
1404
1405 if (DeviceExt->FatInfo.FatType == FAT32)
1406 {
1407 FAT32UpdateFreeClustersCount(DeviceExt);
1408 }
1409 }
1410 else
1411 {
1412 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize, vfatVolumeIsFatX(DeviceExt));
1413 }
1414
1415 /* Update the on-disk directory entry */
1416 Fcb->Flags |= FCB_IS_DIRTY;
1417 if (AllocSizeChanged)
1418 {
1419 VfatUpdateEntry(DeviceExt, Fcb);
1420
1421 vfatReportChange(DeviceExt, Fcb, FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED);
1422 }
1423 return STATUS_SUCCESS;
1424 }
1425
1426 /*
1427 * FUNCTION: Retrieve the specified file information
1428 */
1429 NTSTATUS
1430 VfatQueryInformation(
1431 PVFAT_IRP_CONTEXT IrpContext)
1432 {
1433 FILE_INFORMATION_CLASS FileInformationClass;
1434 PVFATFCB FCB;
1435
1436 NTSTATUS Status = STATUS_SUCCESS;
1437 PVOID SystemBuffer;
1438 ULONG BufferLength;
1439
1440 /* PRECONDITION */
1441 ASSERT(IrpContext);
1442
1443 /* INITIALIZATION */
1444 FileInformationClass = IrpContext->Stack->Parameters.QueryFile.FileInformationClass;
1445 FCB = (PVFATFCB) IrpContext->FileObject->FsContext;
1446
1447 DPRINT("VfatQueryInformation is called for '%s'\n",
1448 FileInformationClass >= FileMaximumInformation - 1 ? "????" : FileInformationClassNames[FileInformationClass]);
1449
1450 if (FCB == NULL)
1451 {
1452 DPRINT1("IRP_MJ_QUERY_INFORMATION without FCB!\n");
1453 IrpContext->Irp->IoStatus.Information = 0;
1454 return STATUS_INVALID_PARAMETER;
1455 }
1456
1457 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
1458 BufferLength = IrpContext->Stack->Parameters.QueryFile.Length;
1459
1460 if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE))
1461 {
1462 if (!ExAcquireResourceSharedLite(&FCB->MainResource,
1463 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1464 {
1465 return VfatMarkIrpContextForQueue(IrpContext);
1466 }
1467 }
1468
1469 switch (FileInformationClass)
1470 {
1471 case FileStandardInformation:
1472 Status = VfatGetStandardInformation(FCB,
1473 SystemBuffer,
1474 &BufferLength);
1475 break;
1476
1477 case FilePositionInformation:
1478 Status = VfatGetPositionInformation(IrpContext->FileObject,
1479 FCB,
1480 IrpContext->DeviceExt,
1481 SystemBuffer,
1482 &BufferLength);
1483 break;
1484
1485 case FileBasicInformation:
1486 Status = VfatGetBasicInformation(IrpContext->FileObject,
1487 FCB,
1488 IrpContext->DeviceExt,
1489 SystemBuffer,
1490 &BufferLength);
1491 break;
1492
1493 case FileNameInformation:
1494 Status = VfatGetNameInformation(IrpContext->FileObject,
1495 FCB,
1496 IrpContext->DeviceExt,
1497 SystemBuffer,
1498 &BufferLength);
1499 break;
1500
1501 case FileInternalInformation:
1502 Status = VfatGetInternalInformation(FCB,
1503 IrpContext->DeviceExt,
1504 SystemBuffer,
1505 &BufferLength);
1506 break;
1507
1508 case FileNetworkOpenInformation:
1509 Status = VfatGetNetworkOpenInformation(FCB,
1510 IrpContext->DeviceExt,
1511 SystemBuffer,
1512 &BufferLength);
1513 break;
1514
1515 case FileAllInformation:
1516 Status = VfatGetAllInformation(IrpContext->FileObject,
1517 FCB,
1518 IrpContext->DeviceExt,
1519 SystemBuffer,
1520 &BufferLength);
1521 break;
1522
1523 case FileEaInformation:
1524 Status = VfatGetEaInformation(IrpContext->FileObject,
1525 FCB,
1526 IrpContext->DeviceExt,
1527 SystemBuffer,
1528 &BufferLength);
1529 break;
1530
1531 case FileAlternateNameInformation:
1532 Status = STATUS_NOT_IMPLEMENTED;
1533 break;
1534
1535 default:
1536 Status = STATUS_INVALID_PARAMETER;
1537 }
1538
1539 if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE))
1540 {
1541 ExReleaseResourceLite(&FCB->MainResource);
1542 }
1543
1544 if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW)
1545 IrpContext->Irp->IoStatus.Information =
1546 IrpContext->Stack->Parameters.QueryFile.Length - BufferLength;
1547 else
1548 IrpContext->Irp->IoStatus.Information = 0;
1549
1550 return Status;
1551 }
1552
1553 /*
1554 * FUNCTION: Retrieve the specified file information
1555 */
1556 NTSTATUS
1557 VfatSetInformation(
1558 PVFAT_IRP_CONTEXT IrpContext)
1559 {
1560 FILE_INFORMATION_CLASS FileInformationClass;
1561 PVFATFCB FCB;
1562 NTSTATUS Status = STATUS_SUCCESS;
1563 PVOID SystemBuffer;
1564 BOOLEAN LockDir;
1565
1566 /* PRECONDITION */
1567 ASSERT(IrpContext);
1568
1569 DPRINT("VfatSetInformation(IrpContext %p)\n", IrpContext);
1570
1571 /* INITIALIZATION */
1572 FileInformationClass =
1573 IrpContext->Stack->Parameters.SetFile.FileInformationClass;
1574 FCB = (PVFATFCB) IrpContext->FileObject->FsContext;
1575 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
1576
1577 DPRINT("VfatSetInformation is called for '%s'\n",
1578 FileInformationClass >= FileMaximumInformation - 1 ? "????" : FileInformationClassNames[ FileInformationClass]);
1579
1580 DPRINT("FileInformationClass %d\n", FileInformationClass);
1581 DPRINT("SystemBuffer %p\n", SystemBuffer);
1582
1583 if (FCB == NULL)
1584 {
1585 DPRINT1("IRP_MJ_SET_INFORMATION without FCB!\n");
1586 IrpContext->Irp->IoStatus.Information = 0;
1587 return STATUS_INVALID_PARAMETER;
1588 }
1589
1590 /* Special: We should call MmCanFileBeTruncated here to determine if changing
1591 the file size would be allowed. If not, we bail with the right error.
1592 We must do this before acquiring the lock. */
1593 if (FileInformationClass == FileEndOfFileInformation)
1594 {
1595 DPRINT("Check for the ability to set file size\n");
1596 if (!MmCanFileBeTruncated(IrpContext->FileObject->SectionObjectPointer,
1597 (PLARGE_INTEGER)SystemBuffer))
1598 {
1599 DPRINT("Couldn't set file size!\n");
1600 IrpContext->Irp->IoStatus.Information = 0;
1601 return STATUS_USER_MAPPED_FILE;
1602 }
1603 DPRINT("Can set file size\n");
1604 }
1605
1606 LockDir = FALSE;
1607 if (FileInformationClass == FileRenameInformation || FileInformationClass == FileAllocationInformation ||
1608 FileInformationClass == FileEndOfFileInformation || FileInformationClass == FileBasicInformation)
1609 {
1610 LockDir = TRUE;
1611 }
1612
1613 if (LockDir)
1614 {
1615 if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
1616 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1617 {
1618 return VfatMarkIrpContextForQueue(IrpContext);
1619 }
1620 }
1621
1622 if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE))
1623 {
1624 if (!ExAcquireResourceExclusiveLite(&FCB->MainResource,
1625 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1626 {
1627 if (LockDir)
1628 {
1629 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
1630 }
1631
1632 return VfatMarkIrpContextForQueue(IrpContext);
1633 }
1634 }
1635
1636 switch (FileInformationClass)
1637 {
1638 case FilePositionInformation:
1639 Status = VfatSetPositionInformation(IrpContext->FileObject,
1640 SystemBuffer);
1641 break;
1642
1643 case FileDispositionInformation:
1644 Status = VfatSetDispositionInformation(IrpContext->FileObject,
1645 FCB,
1646 IrpContext->DeviceExt,
1647 SystemBuffer);
1648 break;
1649
1650 case FileAllocationInformation:
1651 case FileEndOfFileInformation:
1652 Status = VfatSetAllocationSizeInformation(IrpContext->FileObject,
1653 FCB,
1654 IrpContext->DeviceExt,
1655 (PLARGE_INTEGER)SystemBuffer);
1656 break;
1657
1658 case FileBasicInformation:
1659 Status = VfatSetBasicInformation(IrpContext->FileObject,
1660 FCB,
1661 IrpContext->DeviceExt,
1662 SystemBuffer);
1663 break;
1664
1665 case FileRenameInformation:
1666 Status = VfatSetRenameInformation(IrpContext->FileObject,
1667 FCB,
1668 IrpContext->DeviceExt,
1669 SystemBuffer,
1670 IrpContext->Stack->Parameters.SetFile.FileObject);
1671 break;
1672
1673 default:
1674 Status = STATUS_NOT_SUPPORTED;
1675 }
1676
1677 if (!BooleanFlagOn(FCB->Flags, FCB_IS_PAGE_FILE))
1678 {
1679 ExReleaseResourceLite(&FCB->MainResource);
1680 }
1681
1682 if (LockDir)
1683 {
1684 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
1685 }
1686
1687 IrpContext->Irp->IoStatus.Information = 0;
1688 return Status;
1689 }
1690
1691 /* EOF */