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