[KMTESTS:IO]
[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 /* Are we working in place? */
708 if (FsRtlAreNamesEqual(&SourcePath, &NewPath, TRUE, NULL))
709 {
710 if (FsRtlAreNamesEqual(&SourceFile, &NewFile, FALSE, NULL))
711 {
712 Status = STATUS_SUCCESS;
713 ASSERT(OldReferences == FCB->parentFcb->RefCount);
714 goto Cleanup;
715 }
716
717 if (FsRtlAreNamesEqual(&SourceFile, &NewFile, TRUE, NULL))
718 {
719 FsRtlNotifyFullReportChange(DeviceExt->NotifySync,
720 &(DeviceExt->NotifyList),
721 (PSTRING)&FCB->PathNameU,
722 FCB->PathNameU.Length - FCB->LongNameU.Length,
723 NULL,
724 NULL,
725 ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) ?
726 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
727 FILE_ACTION_RENAMED_OLD_NAME,
728 NULL);
729 Status = vfatRenameEntry(DeviceExt, FCB, &NewFile, TRUE);
730 if (NT_SUCCESS(Status))
731 {
732 FsRtlNotifyFullReportChange(DeviceExt->NotifySync,
733 &(DeviceExt->NotifyList),
734 (PSTRING)&FCB->PathNameU,
735 FCB->PathNameU.Length - FCB->LongNameU.Length,
736 NULL,
737 NULL,
738 ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) ?
739 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
740 FILE_ACTION_RENAMED_NEW_NAME,
741 NULL);
742 }
743 }
744 else
745 {
746 /* Try to find target */
747 ParentFCB = FCB->parentFcb;
748 vfatGrabFCB(DeviceExt, ParentFCB);
749 Status = vfatPrepareTargetForRename(DeviceExt,
750 &ParentFCB,
751 &NewFile,
752 RenameInfo->ReplaceIfExists,
753 &NewPath,
754 &DeletedTarget);
755 if (!NT_SUCCESS(Status))
756 {
757 ASSERT(OldReferences == FCB->parentFcb->RefCount - 1);
758 ASSERT(OldReferences == ParentFCB->RefCount - 1);
759 goto Cleanup;
760 }
761
762 FsRtlNotifyFullReportChange(DeviceExt->NotifySync,
763 &(DeviceExt->NotifyList),
764 (PSTRING)&FCB->PathNameU,
765 FCB->PathNameU.Length - FCB->LongNameU.Length,
766 NULL,
767 NULL,
768 ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) ?
769 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
770 (DeletedTarget ? FILE_ACTION_REMOVED : FILE_ACTION_RENAMED_OLD_NAME),
771 NULL);
772 Status = vfatRenameEntry(DeviceExt, FCB, &NewFile, FALSE);
773 if (NT_SUCCESS(Status))
774 {
775 if (DeletedTarget)
776 {
777 FsRtlNotifyFullReportChange(DeviceExt->NotifySync,
778 &(DeviceExt->NotifyList),
779 (PSTRING)&FCB->PathNameU,
780 FCB->PathNameU.Length - FCB->LongNameU.Length,
781 NULL,
782 NULL,
783 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE
784 | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA,
785 FILE_ACTION_MODIFIED,
786 NULL);
787 }
788 else
789 {
790 FsRtlNotifyFullReportChange(DeviceExt->NotifySync,
791 &(DeviceExt->NotifyList),
792 (PSTRING)&FCB->PathNameU,
793 FCB->PathNameU.Length - FCB->LongNameU.Length,
794 NULL,
795 NULL,
796 ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) ?
797 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
798 FILE_ACTION_RENAMED_NEW_NAME,
799 NULL);
800 }
801 }
802 }
803
804 ASSERT(OldReferences == FCB->parentFcb->RefCount - 1); // extra grab
805 ASSERT(OldReferences == ParentFCB->RefCount - 1); // extra grab
806 }
807 else
808 {
809
810 /* Try to find target */
811 ParentFCB = NULL;
812 OldParent = FCB->parentFcb;
813 #ifdef NASSERTS_RENAME
814 UNREFERENCED_PARAMETER(OldParent);
815 #endif
816 Status = vfatPrepareTargetForRename(DeviceExt,
817 &ParentFCB,
818 &NewName,
819 RenameInfo->ReplaceIfExists,
820 &NewPath,
821 &DeletedTarget);
822 if (!NT_SUCCESS(Status))
823 {
824 ASSERT(OldReferences == FCB->parentFcb->RefCount);
825 goto Cleanup;
826 }
827
828 NewReferences = ParentFCB->RefCount;
829 #ifdef NASSERTS_RENAME
830 UNREFERENCED_PARAMETER(NewReferences);
831 #endif
832
833 FsRtlNotifyFullReportChange(DeviceExt->NotifySync,
834 &(DeviceExt->NotifyList),
835 (PSTRING)&FCB->PathNameU,
836 FCB->PathNameU.Length - FCB->LongNameU.Length,
837 NULL,
838 NULL,
839 ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) ?
840 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
841 FILE_ACTION_REMOVED,
842 NULL);
843 Status = VfatMoveEntry(DeviceExt, FCB, &NewFile, ParentFCB);
844 if (NT_SUCCESS(Status))
845 {
846 if (DeletedTarget)
847 {
848 FsRtlNotifyFullReportChange(DeviceExt->NotifySync,
849 &(DeviceExt->NotifyList),
850 (PSTRING)&FCB->PathNameU,
851 FCB->PathNameU.Length - FCB->LongNameU.Length,
852 NULL,
853 NULL,
854 FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE | FILE_NOTIFY_CHANGE_LAST_WRITE
855 | FILE_NOTIFY_CHANGE_LAST_ACCESS | FILE_NOTIFY_CHANGE_CREATION | FILE_NOTIFY_CHANGE_EA,
856 FILE_ACTION_MODIFIED,
857 NULL);
858 }
859 else
860 {
861 FsRtlNotifyFullReportChange(DeviceExt->NotifySync,
862 &(DeviceExt->NotifyList),
863 (PSTRING)&FCB->PathNameU,
864 FCB->PathNameU.Length - FCB->LongNameU.Length,
865 NULL,
866 NULL,
867 ((*FCB->Attributes & FILE_ATTRIBUTE_DIRECTORY) ?
868 FILE_NOTIFY_CHANGE_DIR_NAME : FILE_NOTIFY_CHANGE_FILE_NAME),
869 FILE_ACTION_ADDED,
870 NULL);
871 }
872 }
873 }
874
875 ASSERT(OldReferences == OldParent->RefCount + 1); // removed file
876 ASSERT(NewReferences == ParentFCB->RefCount - 1); // new file
877 Cleanup:
878 if (ParentFCB != NULL) vfatReleaseFCB(DeviceExt, ParentFCB);
879 if (NewName.Buffer != NULL) ExFreePoolWithTag(NewName.Buffer, TAG_VFAT);
880 if (RenameInfo->RootDirectory != NULL) ObDereferenceObject(RootFileObject);
881
882 return Status;
883 #ifdef NASSERTS_RENAME
884 #pragma pop_macro("ASSERT")
885 #endif
886 }
887
888 /*
889 * FUNCTION: Retrieve the file name information
890 */
891 static
892 NTSTATUS
893 VfatGetNameInformation(
894 PFILE_OBJECT FileObject,
895 PVFATFCB FCB,
896 PDEVICE_OBJECT DeviceObject,
897 PFILE_NAME_INFORMATION NameInfo,
898 PULONG BufferLength)
899 {
900 ULONG BytesToCopy;
901
902 UNREFERENCED_PARAMETER(FileObject);
903 UNREFERENCED_PARAMETER(DeviceObject);
904
905 ASSERT(NameInfo != NULL);
906 ASSERT(FCB != NULL);
907
908 /* If buffer can't hold at least the file name length, bail out */
909 if (*BufferLength < (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]))
910 return STATUS_BUFFER_OVERFLOW;
911
912 /* Save file name length, and as much file len, as buffer length allows */
913 NameInfo->FileNameLength = FCB->PathNameU.Length;
914
915 /* Calculate amount of bytes to copy not to overflow the buffer */
916 BytesToCopy = min(FCB->PathNameU.Length,
917 *BufferLength - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]));
918
919 /* Fill in the bytes */
920 RtlCopyMemory(NameInfo->FileName, FCB->PathNameU.Buffer, BytesToCopy);
921
922 /* Check if we could write more but are not able to */
923 if (*BufferLength < FCB->PathNameU.Length + (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]))
924 {
925 /* Return number of bytes written */
926 *BufferLength -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + BytesToCopy;
927 return STATUS_BUFFER_OVERFLOW;
928 }
929
930 /* We filled up as many bytes, as needed */
931 *BufferLength -= (FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + FCB->PathNameU.Length);
932
933 return STATUS_SUCCESS;
934 }
935
936 static
937 NTSTATUS
938 VfatGetInternalInformation(
939 PVFATFCB Fcb,
940 PFILE_INTERNAL_INFORMATION InternalInfo,
941 PULONG BufferLength)
942 {
943 ASSERT(InternalInfo);
944 ASSERT(Fcb);
945
946 if (*BufferLength < sizeof(FILE_INTERNAL_INFORMATION))
947 return STATUS_BUFFER_OVERFLOW;
948 // FIXME: get a real index, that can be used in a create operation
949 InternalInfo->IndexNumber.QuadPart = 0;
950 *BufferLength -= sizeof(FILE_INTERNAL_INFORMATION);
951 return STATUS_SUCCESS;
952 }
953
954
955 /*
956 * FUNCTION: Retrieve the file network open information
957 */
958 static
959 NTSTATUS
960 VfatGetNetworkOpenInformation(
961 PVFATFCB Fcb,
962 PDEVICE_EXTENSION DeviceExt,
963 PFILE_NETWORK_OPEN_INFORMATION NetworkInfo,
964 PULONG BufferLength)
965 {
966 ASSERT(NetworkInfo);
967 ASSERT(Fcb);
968
969 if (*BufferLength < sizeof(FILE_NETWORK_OPEN_INFORMATION))
970 return(STATUS_BUFFER_OVERFLOW);
971
972 if (Fcb->Flags & FCB_IS_FATX_ENTRY)
973 {
974 FsdDosDateTimeToSystemTime(DeviceExt,
975 Fcb->entry.FatX.CreationDate,
976 Fcb->entry.FatX.CreationTime,
977 &NetworkInfo->CreationTime);
978 FsdDosDateTimeToSystemTime(DeviceExt,
979 Fcb->entry.FatX.AccessDate,
980 Fcb->entry.FatX.AccessTime,
981 &NetworkInfo->LastAccessTime);
982 FsdDosDateTimeToSystemTime(DeviceExt,
983 Fcb->entry.FatX.UpdateDate,
984 Fcb->entry.FatX.UpdateTime,
985 &NetworkInfo->LastWriteTime);
986 NetworkInfo->ChangeTime.QuadPart = NetworkInfo->LastWriteTime.QuadPart;
987 }
988 else
989 {
990 FsdDosDateTimeToSystemTime(DeviceExt,
991 Fcb->entry.Fat.CreationDate,
992 Fcb->entry.Fat.CreationTime,
993 &NetworkInfo->CreationTime);
994 FsdDosDateTimeToSystemTime(DeviceExt,
995 Fcb->entry.Fat.AccessDate,
996 0,
997 &NetworkInfo->LastAccessTime);
998 FsdDosDateTimeToSystemTime(DeviceExt,
999 Fcb->entry.Fat.UpdateDate,
1000 Fcb->entry.Fat.UpdateTime,
1001 &NetworkInfo->LastWriteTime);
1002 NetworkInfo->ChangeTime.QuadPart = NetworkInfo->LastWriteTime.QuadPart;
1003 }
1004
1005 if (vfatFCBIsDirectory(Fcb))
1006 {
1007 NetworkInfo->EndOfFile.QuadPart = 0L;
1008 NetworkInfo->AllocationSize.QuadPart = 0L;
1009 }
1010 else
1011 {
1012 NetworkInfo->AllocationSize = Fcb->RFCB.AllocationSize;
1013 NetworkInfo->EndOfFile = Fcb->RFCB.FileSize;
1014 }
1015
1016 NetworkInfo->FileAttributes = *Fcb->Attributes & 0x3f;
1017 /* Synthesize FILE_ATTRIBUTE_NORMAL */
1018 if (0 == (NetworkInfo->FileAttributes & (FILE_ATTRIBUTE_DIRECTORY |
1019 FILE_ATTRIBUTE_ARCHIVE |
1020 FILE_ATTRIBUTE_SYSTEM |
1021 FILE_ATTRIBUTE_HIDDEN |
1022 FILE_ATTRIBUTE_READONLY)))
1023 {
1024 DPRINT("Synthesizing FILE_ATTRIBUTE_NORMAL\n");
1025 NetworkInfo->FileAttributes |= FILE_ATTRIBUTE_NORMAL;
1026 }
1027
1028 *BufferLength -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
1029 return STATUS_SUCCESS;
1030 }
1031
1032
1033 static
1034 NTSTATUS
1035 VfatGetEaInformation(
1036 PFILE_OBJECT FileObject,
1037 PVFATFCB Fcb,
1038 PDEVICE_OBJECT DeviceObject,
1039 PFILE_EA_INFORMATION Info,
1040 PULONG BufferLength)
1041 {
1042 PDEVICE_EXTENSION DeviceExt = DeviceObject->DeviceExtension;
1043
1044 UNREFERENCED_PARAMETER(FileObject);
1045 UNREFERENCED_PARAMETER(Fcb);
1046
1047 /* FIXME - use SEH to access the buffer! */
1048 Info->EaSize = 0;
1049 *BufferLength -= sizeof(*Info);
1050 if (DeviceExt->FatInfo.FatType == FAT12 ||
1051 DeviceExt->FatInfo.FatType == FAT16)
1052 {
1053 /* FIXME */
1054 DPRINT1("VFAT: FileEaInformation not implemented!\n");
1055 }
1056 return STATUS_SUCCESS;
1057 }
1058
1059
1060 /*
1061 * FUNCTION: Retrieve the all file information
1062 */
1063 static
1064 NTSTATUS
1065 VfatGetAllInformation(
1066 PFILE_OBJECT FileObject,
1067 PVFATFCB Fcb,
1068 PDEVICE_OBJECT DeviceObject,
1069 PFILE_ALL_INFORMATION Info,
1070 PULONG BufferLength)
1071 {
1072 NTSTATUS Status;
1073 ULONG InitialBufferLength = *BufferLength;
1074
1075 ASSERT(Info);
1076 ASSERT(Fcb);
1077
1078 if (*BufferLength < sizeof(FILE_ALL_INFORMATION))
1079 return STATUS_BUFFER_OVERFLOW;
1080
1081 /* Basic Information */
1082 Status = VfatGetBasicInformation(FileObject, Fcb, DeviceObject, &Info->BasicInformation, BufferLength);
1083 if (!NT_SUCCESS(Status)) return Status;
1084 /* Standard Information */
1085 Status = VfatGetStandardInformation(Fcb, &Info->StandardInformation, BufferLength);
1086 if (!NT_SUCCESS(Status)) return Status;
1087 /* Internal Information */
1088 Status = VfatGetInternalInformation(Fcb, &Info->InternalInformation, BufferLength);
1089 if (!NT_SUCCESS(Status)) return Status;
1090 /* EA Information */
1091 Info->EaInformation.EaSize = 0;
1092 /* Access Information: The IO-Manager adds this information */
1093 /* Position Information */
1094 Status = VfatGetPositionInformation(FileObject, Fcb, DeviceObject, &Info->PositionInformation, BufferLength);
1095 if (!NT_SUCCESS(Status)) return Status;
1096 /* Mode Information: The IO-Manager adds this information */
1097 /* Alignment Information: The IO-Manager adds this information */
1098 /* Name Information */
1099 Status = VfatGetNameInformation(FileObject, Fcb, DeviceObject, &Info->NameInformation, BufferLength);
1100
1101 *BufferLength = InitialBufferLength - sizeof(FILE_ALL_INFORMATION);
1102 if (InitialBufferLength > sizeof(FILE_ALL_INFORMATION))
1103 *BufferLength -= min(InitialBufferLength - sizeof(FILE_ALL_INFORMATION), Fcb->PathNameU.Length);
1104
1105 return Status;
1106 }
1107
1108 static
1109 VOID
1110 UpdateFileSize(
1111 PFILE_OBJECT FileObject,
1112 PVFATFCB Fcb,
1113 ULONG Size,
1114 ULONG ClusterSize)
1115 {
1116 if (Size > 0)
1117 {
1118 Fcb->RFCB.AllocationSize.QuadPart = ROUND_UP(Size, ClusterSize);
1119 }
1120 else
1121 {
1122 Fcb->RFCB.AllocationSize.QuadPart = (LONGLONG)0;
1123 }
1124 if (!vfatFCBIsDirectory(Fcb))
1125 {
1126 if (Fcb->Flags & FCB_IS_FATX_ENTRY)
1127 Fcb->entry.FatX.FileSize = Size;
1128 else
1129 Fcb->entry.Fat.FileSize = Size;
1130 }
1131 Fcb->RFCB.FileSize.QuadPart = Size;
1132 Fcb->RFCB.ValidDataLength.QuadPart = Size;
1133
1134 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
1135 }
1136
1137 NTSTATUS
1138 VfatSetAllocationSizeInformation(
1139 PFILE_OBJECT FileObject,
1140 PVFATFCB Fcb,
1141 PDEVICE_EXTENSION DeviceExt,
1142 PLARGE_INTEGER AllocationSize)
1143 {
1144 ULONG OldSize;
1145 ULONG Cluster, FirstCluster;
1146 NTSTATUS Status;
1147
1148 ULONG ClusterSize = DeviceExt->FatInfo.BytesPerCluster;
1149 ULONG NewSize = AllocationSize->u.LowPart;
1150 ULONG NCluster;
1151 BOOLEAN AllocSizeChanged = FALSE;
1152
1153 DPRINT("VfatSetAllocationSizeInformation(File <%wZ>, AllocationSize %d %u)\n",
1154 &Fcb->PathNameU, AllocationSize->HighPart, AllocationSize->LowPart);
1155
1156 if (Fcb->Flags & FCB_IS_FATX_ENTRY)
1157 OldSize = Fcb->entry.FatX.FileSize;
1158 else
1159 OldSize = Fcb->entry.Fat.FileSize;
1160
1161 if (AllocationSize->u.HighPart > 0)
1162 {
1163 return STATUS_INVALID_PARAMETER;
1164 }
1165
1166 if (OldSize == NewSize)
1167 {
1168 return STATUS_SUCCESS;
1169 }
1170
1171 FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry);
1172
1173 if (NewSize > Fcb->RFCB.AllocationSize.u.LowPart)
1174 {
1175 AllocSizeChanged = TRUE;
1176 if (FirstCluster == 0)
1177 {
1178 Fcb->LastCluster = Fcb->LastOffset = 0;
1179 Status = NextCluster(DeviceExt, FirstCluster, &FirstCluster, TRUE);
1180 if (!NT_SUCCESS(Status))
1181 {
1182 DPRINT1("NextCluster failed. Status = %x\n", Status);
1183 return Status;
1184 }
1185
1186 if (FirstCluster == 0xffffffff)
1187 {
1188 return STATUS_DISK_FULL;
1189 }
1190
1191 Status = OffsetToCluster(DeviceExt, FirstCluster,
1192 ROUND_DOWN(NewSize - 1, ClusterSize),
1193 &NCluster, TRUE);
1194 if (NCluster == 0xffffffff || !NT_SUCCESS(Status))
1195 {
1196 /* disk is full */
1197 NCluster = Cluster = FirstCluster;
1198 Status = STATUS_SUCCESS;
1199 while (NT_SUCCESS(Status) && Cluster != 0xffffffff && Cluster > 1)
1200 {
1201 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1202 WriteCluster(DeviceExt, Cluster, 0);
1203 Cluster = NCluster;
1204 }
1205 return STATUS_DISK_FULL;
1206 }
1207
1208 if (Fcb->Flags & FCB_IS_FATX_ENTRY)
1209 {
1210 Fcb->entry.FatX.FirstCluster = FirstCluster;
1211 }
1212 else
1213 {
1214 if (DeviceExt->FatInfo.FatType == FAT32)
1215 {
1216 Fcb->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0x0000FFFF);
1217 Fcb->entry.Fat.FirstClusterHigh = FirstCluster >> 16;
1218 }
1219 else
1220 {
1221 ASSERT((FirstCluster >> 16) == 0);
1222 Fcb->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0x0000FFFF);
1223 }
1224 }
1225 }
1226 else
1227 {
1228 if (Fcb->LastCluster > 0)
1229 {
1230 if (Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize == Fcb->LastOffset)
1231 {
1232 Cluster = Fcb->LastCluster;
1233 Status = STATUS_SUCCESS;
1234 }
1235 else
1236 {
1237 Status = OffsetToCluster(DeviceExt, Fcb->LastCluster,
1238 Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize - Fcb->LastOffset,
1239 &Cluster, FALSE);
1240 }
1241 }
1242 else
1243 {
1244 Status = OffsetToCluster(DeviceExt, FirstCluster,
1245 Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize,
1246 &Cluster, FALSE);
1247 }
1248
1249 if (!NT_SUCCESS(Status))
1250 {
1251 return Status;
1252 }
1253
1254 Fcb->LastCluster = Cluster;
1255 Fcb->LastOffset = Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize;
1256
1257 /* FIXME: Check status */
1258 /* Cluster points now to the last cluster within the chain */
1259 Status = OffsetToCluster(DeviceExt, Cluster,
1260 ROUND_DOWN(NewSize - 1, ClusterSize) - Fcb->LastOffset,
1261 &NCluster, TRUE);
1262 if (NCluster == 0xffffffff || !NT_SUCCESS(Status))
1263 {
1264 /* disk is full */
1265 NCluster = Cluster;
1266 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1267 WriteCluster(DeviceExt, Cluster, 0xffffffff);
1268 Cluster = NCluster;
1269 while (NT_SUCCESS(Status) && Cluster != 0xffffffff && Cluster > 1)
1270 {
1271 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1272 WriteCluster(DeviceExt, Cluster, 0);
1273 Cluster = NCluster;
1274 }
1275 return STATUS_DISK_FULL;
1276 }
1277 }
1278 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize);
1279 }
1280 else if (NewSize + ClusterSize <= Fcb->RFCB.AllocationSize.u.LowPart)
1281 {
1282 DPRINT("Check for the ability to set file size\n");
1283 if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer,
1284 (PLARGE_INTEGER)AllocationSize))
1285 {
1286 DPRINT("Couldn't set file size!\n");
1287 return STATUS_USER_MAPPED_FILE;
1288 }
1289 DPRINT("Can set file size\n");
1290
1291 AllocSizeChanged = TRUE;
1292 /* FIXME: Use the cached cluster/offset better way. */
1293 Fcb->LastCluster = Fcb->LastOffset = 0;
1294 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize);
1295 if (NewSize > 0)
1296 {
1297 Status = OffsetToCluster(DeviceExt, FirstCluster,
1298 ROUND_DOWN(NewSize - 1, ClusterSize),
1299 &Cluster, FALSE);
1300
1301 NCluster = Cluster;
1302 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1303 WriteCluster(DeviceExt, Cluster, 0xffffffff);
1304 Cluster = NCluster;
1305 }
1306 else
1307 {
1308 if (Fcb->Flags & FCB_IS_FATX_ENTRY)
1309 {
1310 Fcb->entry.FatX.FirstCluster = 0;
1311 }
1312 else
1313 {
1314 if (DeviceExt->FatInfo.FatType == FAT32)
1315 {
1316 Fcb->entry.Fat.FirstCluster = 0;
1317 Fcb->entry.Fat.FirstClusterHigh = 0;
1318 }
1319 else
1320 {
1321 Fcb->entry.Fat.FirstCluster = 0;
1322 }
1323 }
1324
1325 NCluster = Cluster = FirstCluster;
1326 Status = STATUS_SUCCESS;
1327 }
1328
1329 while (NT_SUCCESS(Status) && 0xffffffff != Cluster && Cluster > 1)
1330 {
1331 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1332 WriteCluster(DeviceExt, Cluster, 0);
1333 Cluster = NCluster;
1334 }
1335 }
1336 else
1337 {
1338 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize);
1339 }
1340
1341 /* Update the on-disk directory entry */
1342 Fcb->Flags |= FCB_IS_DIRTY;
1343 if (AllocSizeChanged)
1344 {
1345 VfatUpdateEntry(Fcb);
1346 }
1347 return STATUS_SUCCESS;
1348 }
1349
1350 /*
1351 * FUNCTION: Retrieve the specified file information
1352 */
1353 NTSTATUS
1354 VfatQueryInformation(
1355 PVFAT_IRP_CONTEXT IrpContext)
1356 {
1357 FILE_INFORMATION_CLASS FileInformationClass;
1358 PVFATFCB FCB = NULL;
1359
1360 NTSTATUS Status = STATUS_SUCCESS;
1361 PVOID SystemBuffer;
1362 ULONG BufferLength;
1363
1364 /* PRECONDITION */
1365 ASSERT(IrpContext);
1366
1367 /* INITIALIZATION */
1368 FileInformationClass = IrpContext->Stack->Parameters.QueryFile.FileInformationClass;
1369 FCB = (PVFATFCB) IrpContext->FileObject->FsContext;
1370
1371 DPRINT("VfatQueryInformation is called for '%s'\n",
1372 FileInformationClass >= FileMaximumInformation - 1 ? "????" : FileInformationClassNames[FileInformationClass]);
1373
1374
1375 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
1376 BufferLength = IrpContext->Stack->Parameters.QueryFile.Length;
1377
1378 if (!(FCB->Flags & FCB_IS_PAGE_FILE))
1379 {
1380 if (!ExAcquireResourceSharedLite(&FCB->MainResource,
1381 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1382 {
1383 return VfatMarkIrpContextForQueue(IrpContext);
1384 }
1385 }
1386
1387 switch (FileInformationClass)
1388 {
1389 case FileStandardInformation:
1390 Status = VfatGetStandardInformation(FCB,
1391 SystemBuffer,
1392 &BufferLength);
1393 break;
1394
1395 case FilePositionInformation:
1396 Status = VfatGetPositionInformation(IrpContext->FileObject,
1397 FCB,
1398 IrpContext->DeviceObject,
1399 SystemBuffer,
1400 &BufferLength);
1401 break;
1402
1403 case FileBasicInformation:
1404 Status = VfatGetBasicInformation(IrpContext->FileObject,
1405 FCB,
1406 IrpContext->DeviceObject,
1407 SystemBuffer,
1408 &BufferLength);
1409 break;
1410
1411 case FileNameInformation:
1412 Status = VfatGetNameInformation(IrpContext->FileObject,
1413 FCB,
1414 IrpContext->DeviceObject,
1415 SystemBuffer,
1416 &BufferLength);
1417 break;
1418
1419 case FileInternalInformation:
1420 Status = VfatGetInternalInformation(FCB,
1421 SystemBuffer,
1422 &BufferLength);
1423 break;
1424
1425 case FileNetworkOpenInformation:
1426 Status = VfatGetNetworkOpenInformation(FCB,
1427 IrpContext->DeviceExt,
1428 SystemBuffer,
1429 &BufferLength);
1430 break;
1431
1432 case FileAllInformation:
1433 Status = VfatGetAllInformation(IrpContext->FileObject,
1434 FCB,
1435 IrpContext->DeviceObject,
1436 SystemBuffer,
1437 &BufferLength);
1438 break;
1439
1440 case FileEaInformation:
1441 Status = VfatGetEaInformation(IrpContext->FileObject,
1442 FCB,
1443 IrpContext->DeviceObject,
1444 SystemBuffer,
1445 &BufferLength);
1446 break;
1447
1448 case FileAlternateNameInformation:
1449 Status = STATUS_NOT_IMPLEMENTED;
1450 break;
1451
1452 default:
1453 Status = STATUS_INVALID_PARAMETER;
1454 }
1455
1456 if (!(FCB->Flags & FCB_IS_PAGE_FILE))
1457 {
1458 ExReleaseResourceLite(&FCB->MainResource);
1459 }
1460
1461 if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW)
1462 IrpContext->Irp->IoStatus.Information =
1463 IrpContext->Stack->Parameters.QueryFile.Length - BufferLength;
1464 else
1465 IrpContext->Irp->IoStatus.Information = 0;
1466
1467 return Status;
1468 }
1469
1470 /*
1471 * FUNCTION: Retrieve the specified file information
1472 */
1473 NTSTATUS
1474 VfatSetInformation(
1475 PVFAT_IRP_CONTEXT IrpContext)
1476 {
1477 FILE_INFORMATION_CLASS FileInformationClass;
1478 PVFATFCB FCB = NULL;
1479 NTSTATUS Status = STATUS_SUCCESS;
1480 PVOID SystemBuffer;
1481
1482 /* PRECONDITION */
1483 ASSERT(IrpContext);
1484
1485 DPRINT("VfatSetInformation(IrpContext %p)\n", IrpContext);
1486
1487 /* INITIALIZATION */
1488 FileInformationClass =
1489 IrpContext->Stack->Parameters.SetFile.FileInformationClass;
1490 FCB = (PVFATFCB) IrpContext->FileObject->FsContext;
1491 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
1492
1493 DPRINT("VfatSetInformation is called for '%s'\n",
1494 FileInformationClass >= FileMaximumInformation - 1 ? "????" : FileInformationClassNames[ FileInformationClass]);
1495
1496 DPRINT("FileInformationClass %d\n", FileInformationClass);
1497 DPRINT("SystemBuffer %p\n", SystemBuffer);
1498
1499 /* Special: We should call MmCanFileBeTruncated here to determine if changing
1500 the file size would be allowed. If not, we bail with the right error.
1501 We must do this before acquiring the lock. */
1502 if (FileInformationClass == FileEndOfFileInformation)
1503 {
1504 DPRINT("Check for the ability to set file size\n");
1505 if (!MmCanFileBeTruncated(IrpContext->FileObject->SectionObjectPointer,
1506 (PLARGE_INTEGER)SystemBuffer))
1507 {
1508 DPRINT("Couldn't set file size!\n");
1509 IrpContext->Irp->IoStatus.Information = 0;
1510 return STATUS_USER_MAPPED_FILE;
1511 }
1512 DPRINT("Can set file size\n");
1513 }
1514
1515 if (FileInformationClass == FileRenameInformation)
1516 {
1517 if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
1518 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1519 {
1520 return VfatMarkIrpContextForQueue(IrpContext);
1521 }
1522 }
1523
1524 if (!(FCB->Flags & FCB_IS_PAGE_FILE))
1525 {
1526 if (!ExAcquireResourceExclusiveLite(&FCB->MainResource,
1527 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1528 {
1529 if (FileInformationClass == FileRenameInformation)
1530 {
1531 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
1532 }
1533
1534 return VfatMarkIrpContextForQueue(IrpContext);
1535 }
1536 }
1537
1538 switch (FileInformationClass)
1539 {
1540 case FilePositionInformation:
1541 Status = VfatSetPositionInformation(IrpContext->FileObject,
1542 SystemBuffer);
1543 break;
1544
1545 case FileDispositionInformation:
1546 Status = VfatSetDispositionInformation(IrpContext->FileObject,
1547 FCB,
1548 IrpContext->DeviceObject,
1549 SystemBuffer);
1550 break;
1551
1552 case FileAllocationInformation:
1553 case FileEndOfFileInformation:
1554 Status = VfatSetAllocationSizeInformation(IrpContext->FileObject,
1555 FCB,
1556 IrpContext->DeviceExt,
1557 (PLARGE_INTEGER)SystemBuffer);
1558 break;
1559
1560 case FileBasicInformation:
1561 Status = VfatSetBasicInformation(IrpContext->FileObject,
1562 FCB,
1563 IrpContext->DeviceExt,
1564 SystemBuffer);
1565 break;
1566
1567 case FileRenameInformation:
1568 Status = VfatSetRenameInformation(IrpContext->FileObject,
1569 FCB,
1570 IrpContext->DeviceExt,
1571 SystemBuffer,
1572 IrpContext->Stack->Parameters.SetFile.FileObject);
1573 break;
1574
1575 default:
1576 Status = STATUS_NOT_SUPPORTED;
1577 }
1578
1579 if (!(FCB->Flags & FCB_IS_PAGE_FILE))
1580 {
1581 ExReleaseResourceLite(&FCB->MainResource);
1582 }
1583
1584 if (FileInformationClass == FileRenameInformation)
1585 {
1586 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
1587 }
1588
1589 IrpContext->Irp->IoStatus.Information = 0;
1590 return Status;
1591 }
1592
1593 /* EOF */