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