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