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