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