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