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