83e691c63054da1529f34ccc2e5e7017838f0808
[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
1074 ASSERT(Info);
1075 ASSERT(Fcb);
1076
1077 if (*BufferLength < FIELD_OFFSET(FILE_ALL_INFORMATION, NameInformation.FileName))
1078 return STATUS_BUFFER_OVERFLOW;
1079
1080 /* Basic Information */
1081 Status = VfatGetBasicInformation(FileObject, Fcb, DeviceObject, &Info->BasicInformation, BufferLength);
1082 if (!NT_SUCCESS(Status)) return Status;
1083 /* Standard Information */
1084 Status = VfatGetStandardInformation(Fcb, &Info->StandardInformation, BufferLength);
1085 if (!NT_SUCCESS(Status)) return Status;
1086 /* Internal Information */
1087 Status = VfatGetInternalInformation(Fcb, &Info->InternalInformation, BufferLength);
1088 if (!NT_SUCCESS(Status)) return Status;
1089 /* EA Information */
1090 Status = VfatGetEaInformation(FileObject, Fcb, DeviceObject, &Info->EaInformation, BufferLength);
1091 if (!NT_SUCCESS(Status)) return Status;
1092 /* Access Information: The IO-Manager adds this information */
1093 *BufferLength -= sizeof(FILE_ACCESS_INFORMATION);
1094 /* Position Information */
1095 Status = VfatGetPositionInformation(FileObject, Fcb, DeviceObject, &Info->PositionInformation, BufferLength);
1096 if (!NT_SUCCESS(Status)) return Status;
1097 /* Mode Information: The IO-Manager adds this information */
1098 *BufferLength -= sizeof(FILE_MODE_INFORMATION);
1099 /* Alignment Information: The IO-Manager adds this information */
1100 *BufferLength -= sizeof(FILE_ALIGNMENT_INFORMATION);
1101 /* Name Information */
1102 Status = VfatGetNameInformation(FileObject, Fcb, DeviceObject, &Info->NameInformation, BufferLength);
1103
1104 return Status;
1105 }
1106
1107 static
1108 VOID
1109 UpdateFileSize(
1110 PFILE_OBJECT FileObject,
1111 PVFATFCB Fcb,
1112 ULONG Size,
1113 ULONG ClusterSize)
1114 {
1115 if (Size > 0)
1116 {
1117 Fcb->RFCB.AllocationSize.QuadPart = ROUND_UP_64(Size, ClusterSize);
1118 }
1119 else
1120 {
1121 Fcb->RFCB.AllocationSize.QuadPart = (LONGLONG)0;
1122 }
1123 if (!vfatFCBIsDirectory(Fcb))
1124 {
1125 if (Fcb->Flags & FCB_IS_FATX_ENTRY)
1126 Fcb->entry.FatX.FileSize = Size;
1127 else
1128 Fcb->entry.Fat.FileSize = Size;
1129 }
1130 Fcb->RFCB.FileSize.QuadPart = Size;
1131 Fcb->RFCB.ValidDataLength.QuadPart = Size;
1132
1133 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
1134 }
1135
1136 NTSTATUS
1137 VfatSetAllocationSizeInformation(
1138 PFILE_OBJECT FileObject,
1139 PVFATFCB Fcb,
1140 PDEVICE_EXTENSION DeviceExt,
1141 PLARGE_INTEGER AllocationSize)
1142 {
1143 ULONG OldSize;
1144 ULONG Cluster, FirstCluster;
1145 NTSTATUS Status;
1146
1147 ULONG ClusterSize = DeviceExt->FatInfo.BytesPerCluster;
1148 ULONG NewSize = AllocationSize->u.LowPart;
1149 ULONG NCluster;
1150 BOOLEAN AllocSizeChanged = FALSE;
1151
1152 DPRINT("VfatSetAllocationSizeInformation(File <%wZ>, AllocationSize %d %u)\n",
1153 &Fcb->PathNameU, AllocationSize->HighPart, AllocationSize->LowPart);
1154
1155 if (Fcb->Flags & FCB_IS_FATX_ENTRY)
1156 OldSize = Fcb->entry.FatX.FileSize;
1157 else
1158 OldSize = Fcb->entry.Fat.FileSize;
1159
1160 if (AllocationSize->u.HighPart > 0)
1161 {
1162 return STATUS_INVALID_PARAMETER;
1163 }
1164
1165 if (OldSize == NewSize)
1166 {
1167 return STATUS_SUCCESS;
1168 }
1169
1170 FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Fcb->entry);
1171
1172 if (NewSize > Fcb->RFCB.AllocationSize.u.LowPart)
1173 {
1174 AllocSizeChanged = TRUE;
1175 if (FirstCluster == 0)
1176 {
1177 Fcb->LastCluster = Fcb->LastOffset = 0;
1178 Status = NextCluster(DeviceExt, FirstCluster, &FirstCluster, TRUE);
1179 if (!NT_SUCCESS(Status))
1180 {
1181 DPRINT1("NextCluster failed. Status = %x\n", Status);
1182 return Status;
1183 }
1184
1185 if (FirstCluster == 0xffffffff)
1186 {
1187 return STATUS_DISK_FULL;
1188 }
1189
1190 Status = OffsetToCluster(DeviceExt, FirstCluster,
1191 ROUND_DOWN(NewSize - 1, ClusterSize),
1192 &NCluster, TRUE);
1193 if (NCluster == 0xffffffff || !NT_SUCCESS(Status))
1194 {
1195 /* disk is full */
1196 NCluster = Cluster = FirstCluster;
1197 Status = STATUS_SUCCESS;
1198 while (NT_SUCCESS(Status) && Cluster != 0xffffffff && Cluster > 1)
1199 {
1200 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1201 WriteCluster(DeviceExt, Cluster, 0);
1202 Cluster = NCluster;
1203 }
1204 return STATUS_DISK_FULL;
1205 }
1206
1207 if (Fcb->Flags & FCB_IS_FATX_ENTRY)
1208 {
1209 Fcb->entry.FatX.FirstCluster = FirstCluster;
1210 }
1211 else
1212 {
1213 if (DeviceExt->FatInfo.FatType == FAT32)
1214 {
1215 Fcb->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0x0000FFFF);
1216 Fcb->entry.Fat.FirstClusterHigh = FirstCluster >> 16;
1217 }
1218 else
1219 {
1220 ASSERT((FirstCluster >> 16) == 0);
1221 Fcb->entry.Fat.FirstCluster = (unsigned short)(FirstCluster & 0x0000FFFF);
1222 }
1223 }
1224 }
1225 else
1226 {
1227 if (Fcb->LastCluster > 0)
1228 {
1229 if (Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize == Fcb->LastOffset)
1230 {
1231 Cluster = Fcb->LastCluster;
1232 Status = STATUS_SUCCESS;
1233 }
1234 else
1235 {
1236 Status = OffsetToCluster(DeviceExt, Fcb->LastCluster,
1237 Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize - Fcb->LastOffset,
1238 &Cluster, FALSE);
1239 }
1240 }
1241 else
1242 {
1243 Status = OffsetToCluster(DeviceExt, FirstCluster,
1244 Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize,
1245 &Cluster, FALSE);
1246 }
1247
1248 if (!NT_SUCCESS(Status))
1249 {
1250 return Status;
1251 }
1252
1253 Fcb->LastCluster = Cluster;
1254 Fcb->LastOffset = Fcb->RFCB.AllocationSize.u.LowPart - ClusterSize;
1255
1256 /* FIXME: Check status */
1257 /* Cluster points now to the last cluster within the chain */
1258 Status = OffsetToCluster(DeviceExt, Cluster,
1259 ROUND_DOWN(NewSize - 1, ClusterSize) - Fcb->LastOffset,
1260 &NCluster, TRUE);
1261 if (NCluster == 0xffffffff || !NT_SUCCESS(Status))
1262 {
1263 /* disk is full */
1264 NCluster = Cluster;
1265 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1266 WriteCluster(DeviceExt, Cluster, 0xffffffff);
1267 Cluster = NCluster;
1268 while (NT_SUCCESS(Status) && Cluster != 0xffffffff && Cluster > 1)
1269 {
1270 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1271 WriteCluster(DeviceExt, Cluster, 0);
1272 Cluster = NCluster;
1273 }
1274 return STATUS_DISK_FULL;
1275 }
1276 }
1277 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize);
1278 }
1279 else if (NewSize + ClusterSize <= Fcb->RFCB.AllocationSize.u.LowPart)
1280 {
1281 DPRINT("Check for the ability to set file size\n");
1282 if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer,
1283 (PLARGE_INTEGER)AllocationSize))
1284 {
1285 DPRINT("Couldn't set file size!\n");
1286 return STATUS_USER_MAPPED_FILE;
1287 }
1288 DPRINT("Can set file size\n");
1289
1290 AllocSizeChanged = TRUE;
1291 /* FIXME: Use the cached cluster/offset better way. */
1292 Fcb->LastCluster = Fcb->LastOffset = 0;
1293 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize);
1294 if (NewSize > 0)
1295 {
1296 Status = OffsetToCluster(DeviceExt, FirstCluster,
1297 ROUND_DOWN(NewSize - 1, ClusterSize),
1298 &Cluster, FALSE);
1299
1300 NCluster = Cluster;
1301 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1302 WriteCluster(DeviceExt, Cluster, 0xffffffff);
1303 Cluster = NCluster;
1304 }
1305 else
1306 {
1307 if (Fcb->Flags & FCB_IS_FATX_ENTRY)
1308 {
1309 Fcb->entry.FatX.FirstCluster = 0;
1310 }
1311 else
1312 {
1313 if (DeviceExt->FatInfo.FatType == FAT32)
1314 {
1315 Fcb->entry.Fat.FirstCluster = 0;
1316 Fcb->entry.Fat.FirstClusterHigh = 0;
1317 }
1318 else
1319 {
1320 Fcb->entry.Fat.FirstCluster = 0;
1321 }
1322 }
1323
1324 NCluster = Cluster = FirstCluster;
1325 Status = STATUS_SUCCESS;
1326 }
1327
1328 while (NT_SUCCESS(Status) && 0xffffffff != Cluster && Cluster > 1)
1329 {
1330 Status = NextCluster(DeviceExt, FirstCluster, &NCluster, FALSE);
1331 WriteCluster(DeviceExt, Cluster, 0);
1332 Cluster = NCluster;
1333 }
1334 }
1335 else
1336 {
1337 UpdateFileSize(FileObject, Fcb, NewSize, ClusterSize);
1338 }
1339
1340 /* Update the on-disk directory entry */
1341 Fcb->Flags |= FCB_IS_DIRTY;
1342 if (AllocSizeChanged)
1343 {
1344 VfatUpdateEntry(Fcb);
1345 }
1346 return STATUS_SUCCESS;
1347 }
1348
1349 /*
1350 * FUNCTION: Retrieve the specified file information
1351 */
1352 NTSTATUS
1353 VfatQueryInformation(
1354 PVFAT_IRP_CONTEXT IrpContext)
1355 {
1356 FILE_INFORMATION_CLASS FileInformationClass;
1357 PVFATFCB FCB = NULL;
1358
1359 NTSTATUS Status = STATUS_SUCCESS;
1360 PVOID SystemBuffer;
1361 ULONG BufferLength;
1362
1363 /* PRECONDITION */
1364 ASSERT(IrpContext);
1365
1366 /* INITIALIZATION */
1367 FileInformationClass = IrpContext->Stack->Parameters.QueryFile.FileInformationClass;
1368 FCB = (PVFATFCB) IrpContext->FileObject->FsContext;
1369
1370 DPRINT("VfatQueryInformation is called for '%s'\n",
1371 FileInformationClass >= FileMaximumInformation - 1 ? "????" : FileInformationClassNames[FileInformationClass]);
1372
1373
1374 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
1375 BufferLength = IrpContext->Stack->Parameters.QueryFile.Length;
1376
1377 if (!(FCB->Flags & FCB_IS_PAGE_FILE))
1378 {
1379 if (!ExAcquireResourceSharedLite(&FCB->MainResource,
1380 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1381 {
1382 return VfatMarkIrpContextForQueue(IrpContext);
1383 }
1384 }
1385
1386 switch (FileInformationClass)
1387 {
1388 case FileStandardInformation:
1389 Status = VfatGetStandardInformation(FCB,
1390 SystemBuffer,
1391 &BufferLength);
1392 break;
1393
1394 case FilePositionInformation:
1395 Status = VfatGetPositionInformation(IrpContext->FileObject,
1396 FCB,
1397 IrpContext->DeviceObject,
1398 SystemBuffer,
1399 &BufferLength);
1400 break;
1401
1402 case FileBasicInformation:
1403 Status = VfatGetBasicInformation(IrpContext->FileObject,
1404 FCB,
1405 IrpContext->DeviceObject,
1406 SystemBuffer,
1407 &BufferLength);
1408 break;
1409
1410 case FileNameInformation:
1411 Status = VfatGetNameInformation(IrpContext->FileObject,
1412 FCB,
1413 IrpContext->DeviceObject,
1414 SystemBuffer,
1415 &BufferLength);
1416 break;
1417
1418 case FileInternalInformation:
1419 Status = VfatGetInternalInformation(FCB,
1420 SystemBuffer,
1421 &BufferLength);
1422 break;
1423
1424 case FileNetworkOpenInformation:
1425 Status = VfatGetNetworkOpenInformation(FCB,
1426 IrpContext->DeviceExt,
1427 SystemBuffer,
1428 &BufferLength);
1429 break;
1430
1431 case FileAllInformation:
1432 Status = VfatGetAllInformation(IrpContext->FileObject,
1433 FCB,
1434 IrpContext->DeviceObject,
1435 SystemBuffer,
1436 &BufferLength);
1437 break;
1438
1439 case FileEaInformation:
1440 Status = VfatGetEaInformation(IrpContext->FileObject,
1441 FCB,
1442 IrpContext->DeviceObject,
1443 SystemBuffer,
1444 &BufferLength);
1445 break;
1446
1447 case FileAlternateNameInformation:
1448 Status = STATUS_NOT_IMPLEMENTED;
1449 break;
1450
1451 default:
1452 Status = STATUS_INVALID_PARAMETER;
1453 }
1454
1455 if (!(FCB->Flags & FCB_IS_PAGE_FILE))
1456 {
1457 ExReleaseResourceLite(&FCB->MainResource);
1458 }
1459
1460 if (NT_SUCCESS(Status) || Status == STATUS_BUFFER_OVERFLOW)
1461 IrpContext->Irp->IoStatus.Information =
1462 IrpContext->Stack->Parameters.QueryFile.Length - BufferLength;
1463 else
1464 IrpContext->Irp->IoStatus.Information = 0;
1465
1466 return Status;
1467 }
1468
1469 /*
1470 * FUNCTION: Retrieve the specified file information
1471 */
1472 NTSTATUS
1473 VfatSetInformation(
1474 PVFAT_IRP_CONTEXT IrpContext)
1475 {
1476 FILE_INFORMATION_CLASS FileInformationClass;
1477 PVFATFCB FCB = NULL;
1478 NTSTATUS Status = STATUS_SUCCESS;
1479 PVOID SystemBuffer;
1480
1481 /* PRECONDITION */
1482 ASSERT(IrpContext);
1483
1484 DPRINT("VfatSetInformation(IrpContext %p)\n", IrpContext);
1485
1486 /* INITIALIZATION */
1487 FileInformationClass =
1488 IrpContext->Stack->Parameters.SetFile.FileInformationClass;
1489 FCB = (PVFATFCB) IrpContext->FileObject->FsContext;
1490 SystemBuffer = IrpContext->Irp->AssociatedIrp.SystemBuffer;
1491
1492 DPRINT("VfatSetInformation is called for '%s'\n",
1493 FileInformationClass >= FileMaximumInformation - 1 ? "????" : FileInformationClassNames[ FileInformationClass]);
1494
1495 DPRINT("FileInformationClass %d\n", FileInformationClass);
1496 DPRINT("SystemBuffer %p\n", SystemBuffer);
1497
1498 /* Special: We should call MmCanFileBeTruncated here to determine if changing
1499 the file size would be allowed. If not, we bail with the right error.
1500 We must do this before acquiring the lock. */
1501 if (FileInformationClass == FileEndOfFileInformation)
1502 {
1503 DPRINT("Check for the ability to set file size\n");
1504 if (!MmCanFileBeTruncated(IrpContext->FileObject->SectionObjectPointer,
1505 (PLARGE_INTEGER)SystemBuffer))
1506 {
1507 DPRINT("Couldn't set file size!\n");
1508 IrpContext->Irp->IoStatus.Information = 0;
1509 return STATUS_USER_MAPPED_FILE;
1510 }
1511 DPRINT("Can set file size\n");
1512 }
1513
1514 if (FileInformationClass == FileRenameInformation)
1515 {
1516 if (!ExAcquireResourceExclusiveLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource,
1517 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1518 {
1519 return VfatMarkIrpContextForQueue(IrpContext);
1520 }
1521 }
1522
1523 if (!(FCB->Flags & FCB_IS_PAGE_FILE))
1524 {
1525 if (!ExAcquireResourceExclusiveLite(&FCB->MainResource,
1526 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
1527 {
1528 if (FileInformationClass == FileRenameInformation)
1529 {
1530 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
1531 }
1532
1533 return VfatMarkIrpContextForQueue(IrpContext);
1534 }
1535 }
1536
1537 switch (FileInformationClass)
1538 {
1539 case FilePositionInformation:
1540 Status = VfatSetPositionInformation(IrpContext->FileObject,
1541 SystemBuffer);
1542 break;
1543
1544 case FileDispositionInformation:
1545 Status = VfatSetDispositionInformation(IrpContext->FileObject,
1546 FCB,
1547 IrpContext->DeviceObject,
1548 SystemBuffer);
1549 break;
1550
1551 case FileAllocationInformation:
1552 case FileEndOfFileInformation:
1553 Status = VfatSetAllocationSizeInformation(IrpContext->FileObject,
1554 FCB,
1555 IrpContext->DeviceExt,
1556 (PLARGE_INTEGER)SystemBuffer);
1557 break;
1558
1559 case FileBasicInformation:
1560 Status = VfatSetBasicInformation(IrpContext->FileObject,
1561 FCB,
1562 IrpContext->DeviceExt,
1563 SystemBuffer);
1564 break;
1565
1566 case FileRenameInformation:
1567 Status = VfatSetRenameInformation(IrpContext->FileObject,
1568 FCB,
1569 IrpContext->DeviceExt,
1570 SystemBuffer,
1571 IrpContext->Stack->Parameters.SetFile.FileObject);
1572 break;
1573
1574 default:
1575 Status = STATUS_NOT_SUPPORTED;
1576 }
1577
1578 if (!(FCB->Flags & FCB_IS_PAGE_FILE))
1579 {
1580 ExReleaseResourceLite(&FCB->MainResource);
1581 }
1582
1583 if (FileInformationClass == FileRenameInformation)
1584 {
1585 ExReleaseResourceLite(&((PDEVICE_EXTENSION)IrpContext->DeviceObject->DeviceExtension)->DirResource);
1586 }
1587
1588 IrpContext->Irp->IoStatus.Information = 0;
1589 return Status;
1590 }
1591
1592 /* EOF */