xref: /reactos/drivers/filesystems/ntfs/finfo.c (revision 9393fc32)
1 /*
2  *  ReactOS kernel
3  *  Copyright (C) 2002 ReactOS Team
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  * COPYRIGHT:        See COPYING in the top level directory
20  * PROJECT:          ReactOS kernel
21  * FILE:             drivers/filesystem/ntfs/dirctl.c
22  * PURPOSE:          NTFS filesystem driver
23  * PROGRAMMERS:      Eric Kohl
24  *                   Hervé Poussineau (hpoussin@reactos.org)
25  *                   Pierre Schweitzer (pierre@reactos.org)
26  */
27 
28 /* INCLUDES *****************************************************************/
29 
30 #include "ntfs.h"
31 
32 #define NDEBUG
33 #include <debug.h>
34 
35 /* FUNCTIONS ****************************************************************/
36 
37 /*
38  * FUNCTION: Retrieve the standard file information
39  */
40 static
41 NTSTATUS
42 NtfsGetStandardInformation(PNTFS_FCB Fcb,
43                            PDEVICE_OBJECT DeviceObject,
44                            PFILE_STANDARD_INFORMATION StandardInfo,
45                            PULONG BufferLength)
46 {
47     UNREFERENCED_PARAMETER(DeviceObject);
48 
49     DPRINT("NtfsGetStandardInformation(%p, %p, %p, %p)\n", Fcb, DeviceObject, StandardInfo, BufferLength);
50 
51     if (*BufferLength < sizeof(FILE_STANDARD_INFORMATION))
52         return STATUS_BUFFER_TOO_SMALL;
53 
54     /* PRECONDITION */
55     ASSERT(StandardInfo != NULL);
56     ASSERT(Fcb != NULL);
57 
58     RtlZeroMemory(StandardInfo,
59                   sizeof(FILE_STANDARD_INFORMATION));
60 
61     StandardInfo->AllocationSize = Fcb->RFCB.AllocationSize;
62     StandardInfo->EndOfFile = Fcb->RFCB.FileSize;
63     StandardInfo->NumberOfLinks = Fcb->LinkCount;
64     StandardInfo->DeletePending = FALSE;
65     StandardInfo->Directory = NtfsFCBIsDirectory(Fcb);
66 
67     *BufferLength -= sizeof(FILE_STANDARD_INFORMATION);
68 
69     return STATUS_SUCCESS;
70 }
71 
72 
73 static
74 NTSTATUS
75 NtfsGetPositionInformation(PFILE_OBJECT FileObject,
76                            PFILE_POSITION_INFORMATION PositionInfo,
77                            PULONG BufferLength)
78 {
79     DPRINT1("NtfsGetPositionInformation(%p, %p, %p)\n", FileObject, PositionInfo, BufferLength);
80 
81     if (*BufferLength < sizeof(FILE_POSITION_INFORMATION))
82         return STATUS_BUFFER_TOO_SMALL;
83 
84     PositionInfo->CurrentByteOffset.QuadPart = FileObject->CurrentByteOffset.QuadPart;
85 
86     DPRINT("Getting position %I64x\n",
87            PositionInfo->CurrentByteOffset.QuadPart);
88 
89     *BufferLength -= sizeof(FILE_POSITION_INFORMATION);
90 
91     return STATUS_SUCCESS;
92 }
93 
94 
95 static
96 NTSTATUS
97 NtfsGetBasicInformation(PFILE_OBJECT FileObject,
98                         PNTFS_FCB Fcb,
99                         PDEVICE_OBJECT DeviceObject,
100                         PFILE_BASIC_INFORMATION BasicInfo,
101                         PULONG BufferLength)
102 {
103     PFILENAME_ATTRIBUTE FileName = &Fcb->Entry;
104 
105     DPRINT("NtfsGetBasicInformation(%p, %p, %p, %p, %p)\n", FileObject, Fcb, DeviceObject, BasicInfo, BufferLength);
106 
107     if (*BufferLength < sizeof(FILE_BASIC_INFORMATION))
108         return STATUS_BUFFER_TOO_SMALL;
109 
110     RtlZeroMemory(BasicInfo, sizeof(FILE_BASIC_INFORMATION));
111 
112     BasicInfo->CreationTime.QuadPart = FileName->CreationTime;
113     BasicInfo->LastAccessTime.QuadPart = FileName->LastAccessTime;
114     BasicInfo->LastWriteTime.QuadPart = FileName->LastWriteTime;
115     BasicInfo->ChangeTime.QuadPart = FileName->ChangeTime;
116 
117     NtfsFileFlagsToAttributes(FileName->FileAttributes, &BasicInfo->FileAttributes);
118 
119     *BufferLength -= sizeof(FILE_BASIC_INFORMATION);
120 
121     return STATUS_SUCCESS;
122 }
123 
124 
125 /*
126  * FUNCTION: Retrieve the file name information
127  */
128 static
129 NTSTATUS
130 NtfsGetNameInformation(PFILE_OBJECT FileObject,
131                        PNTFS_FCB Fcb,
132                        PDEVICE_OBJECT DeviceObject,
133                        PFILE_NAME_INFORMATION NameInfo,
134                        PULONG BufferLength)
135 {
136     ULONG BytesToCopy;
137 
138     UNREFERENCED_PARAMETER(FileObject);
139     UNREFERENCED_PARAMETER(DeviceObject);
140 
141     DPRINT("NtfsGetNameInformation(%p, %p, %p, %p, %p)\n", FileObject, Fcb, DeviceObject, NameInfo, BufferLength);
142 
143     ASSERT(NameInfo != NULL);
144     ASSERT(Fcb != NULL);
145 
146     /* If buffer can't hold at least the file name length, bail out */
147     if (*BufferLength < (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]))
148         return STATUS_BUFFER_TOO_SMALL;
149 
150     /* Save file name length, and as much file len, as buffer length allows */
151     NameInfo->FileNameLength = wcslen(Fcb->PathName) * sizeof(WCHAR);
152 
153     /* Calculate amount of bytes to copy not to overflow the buffer */
154     BytesToCopy = min(NameInfo->FileNameLength,
155                       *BufferLength - FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]));
156 
157     /* Fill in the bytes */
158     RtlCopyMemory(NameInfo->FileName, Fcb->PathName, BytesToCopy);
159 
160     /* Check if we could write more but are not able to */
161     if (*BufferLength < NameInfo->FileNameLength + (ULONG)FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]))
162     {
163         /* Return number of bytes written */
164         *BufferLength -= FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + BytesToCopy;
165         return STATUS_BUFFER_OVERFLOW;
166     }
167 
168     /* We filled up as many bytes, as needed */
169     *BufferLength -= (FIELD_OFFSET(FILE_NAME_INFORMATION, FileName[0]) + NameInfo->FileNameLength);
170 
171     return STATUS_SUCCESS;
172 }
173 
174 
175 static
176 NTSTATUS
177 NtfsGetInternalInformation(PNTFS_FCB Fcb,
178                            PFILE_INTERNAL_INFORMATION InternalInfo,
179                            PULONG BufferLength)
180 {
181     DPRINT1("NtfsGetInternalInformation(%p, %p, %p)\n", Fcb, InternalInfo, BufferLength);
182 
183     ASSERT(InternalInfo);
184     ASSERT(Fcb);
185 
186     if (*BufferLength < sizeof(FILE_INTERNAL_INFORMATION))
187         return STATUS_BUFFER_TOO_SMALL;
188 
189     InternalInfo->IndexNumber.QuadPart = Fcb->MFTIndex;
190 
191     *BufferLength -= sizeof(FILE_INTERNAL_INFORMATION);
192 
193     return STATUS_SUCCESS;
194 }
195 
196 static
197 NTSTATUS
198 NtfsGetNetworkOpenInformation(PNTFS_FCB Fcb,
199                               PDEVICE_EXTENSION DeviceExt,
200                               PFILE_NETWORK_OPEN_INFORMATION NetworkInfo,
201                               PULONG BufferLength)
202 {
203     PFILENAME_ATTRIBUTE FileName = &Fcb->Entry;
204 
205     DPRINT("NtfsGetNetworkOpenInformation(%p, %p, %p, %p)\n", Fcb, DeviceExt, NetworkInfo, BufferLength);
206 
207     if (*BufferLength < sizeof(FILE_NETWORK_OPEN_INFORMATION))
208         return STATUS_BUFFER_TOO_SMALL;
209 
210     NetworkInfo->CreationTime.QuadPart = FileName->CreationTime;
211     NetworkInfo->LastAccessTime.QuadPart = FileName->LastAccessTime;
212     NetworkInfo->LastWriteTime.QuadPart = FileName->LastWriteTime;
213     NetworkInfo->ChangeTime.QuadPart = FileName->ChangeTime;
214 
215     NetworkInfo->EndOfFile = Fcb->RFCB.FileSize;
216     NetworkInfo->AllocationSize = Fcb->RFCB.AllocationSize;
217 
218     NtfsFileFlagsToAttributes(FileName->FileAttributes, &NetworkInfo->FileAttributes);
219 
220     *BufferLength -= sizeof(FILE_NETWORK_OPEN_INFORMATION);
221     return STATUS_SUCCESS;
222 }
223 
224 static
225 NTSTATUS
226 NtfsGetSteamInformation(PNTFS_FCB Fcb,
227                         PDEVICE_EXTENSION DeviceExt,
228                         PFILE_STREAM_INFORMATION StreamInfo,
229                         PULONG BufferLength)
230 {
231     ULONG CurrentSize;
232     FIND_ATTR_CONTXT Context;
233     PNTFS_ATTR_RECORD Attribute;
234     NTSTATUS Status, BrowseStatus;
235     PFILE_RECORD_HEADER FileRecord;
236     PFILE_STREAM_INFORMATION CurrentInfo = StreamInfo, Previous = NULL;
237 
238     if (*BufferLength < sizeof(FILE_STREAM_INFORMATION))
239         return STATUS_BUFFER_TOO_SMALL;
240 
241     FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
242     if (FileRecord == NULL)
243     {
244         DPRINT1("Not enough memory!\n");
245         return STATUS_INSUFFICIENT_RESOURCES;
246     }
247 
248     Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord);
249     if (!NT_SUCCESS(Status))
250     {
251         DPRINT1("Can't find record!\n");
252         ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
253         return Status;
254     }
255 
256     BrowseStatus = FindFirstAttribute(&Context, DeviceExt, FileRecord, FALSE, &Attribute);
257     while (NT_SUCCESS(BrowseStatus))
258     {
259         if (Attribute->Type == AttributeData)
260         {
261             CurrentSize = FIELD_OFFSET(FILE_STREAM_INFORMATION, StreamName) + Attribute->NameLength * sizeof(WCHAR) + wcslen(L"::$DATA") * sizeof(WCHAR);
262 
263             if (CurrentSize > *BufferLength)
264             {
265                 Status = STATUS_BUFFER_OVERFLOW;
266                 break;
267             }
268 
269             CurrentInfo->NextEntryOffset = 0;
270             CurrentInfo->StreamNameLength = (Attribute->NameLength + wcslen(L"::$DATA")) * sizeof(WCHAR);
271             CurrentInfo->StreamSize.QuadPart = AttributeDataLength(Attribute);
272             CurrentInfo->StreamAllocationSize.QuadPart = AttributeAllocatedLength(Attribute);
273             CurrentInfo->StreamName[0] = L':';
274             RtlMoveMemory(&CurrentInfo->StreamName[1], (PWCHAR)((ULONG_PTR)Attribute + Attribute->NameOffset), CurrentInfo->StreamNameLength);
275             RtlMoveMemory(&CurrentInfo->StreamName[Attribute->NameLength + 1], L":$DATA", sizeof(L":$DATA") - sizeof(UNICODE_NULL));
276 
277             if (Previous != NULL)
278             {
279                 Previous->NextEntryOffset = (ULONG_PTR)CurrentInfo - (ULONG_PTR)Previous;
280             }
281             Previous = CurrentInfo;
282             CurrentInfo = (PFILE_STREAM_INFORMATION)((ULONG_PTR)CurrentInfo + CurrentSize);
283             *BufferLength -= CurrentSize;
284         }
285 
286         BrowseStatus = FindNextAttribute(&Context, &Attribute);
287     }
288 
289     FindCloseAttribute(&Context);
290     ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
291     return Status;
292 }
293 
294 // Convert enum value to friendly name
295 const PCSTR
296 GetInfoClassName(FILE_INFORMATION_CLASS infoClass)
297 {
298     const PCSTR fileInfoClassNames[] = { "???????",
299         "FileDirectoryInformation",
300         "FileFullDirectoryInformation",
301         "FileBothDirectoryInformation",
302         "FileBasicInformation",
303         "FileStandardInformation",
304         "FileInternalInformation",
305         "FileEaInformation",
306         "FileAccessInformation",
307         "FileNameInformation",
308         "FileRenameInformation",
309         "FileLinkInformation",
310         "FileNamesInformation",
311         "FileDispositionInformation",
312         "FilePositionInformation",
313         "FileFullEaInformation",
314         "FileModeInformation",
315         "FileAlignmentInformation",
316         "FileAllInformation",
317         "FileAllocationInformation",
318         "FileEndOfFileInformation",
319         "FileAlternateNameInformation",
320         "FileStreamInformation",
321         "FilePipeInformation",
322         "FilePipeLocalInformation",
323         "FilePipeRemoteInformation",
324         "FileMailslotQueryInformation",
325         "FileMailslotSetInformation",
326         "FileCompressionInformation",
327         "FileObjectIdInformation",
328         "FileCompletionInformation",
329         "FileMoveClusterInformation",
330         "FileQuotaInformation",
331         "FileReparsePointInformation",
332         "FileNetworkOpenInformation",
333         "FileAttributeTagInformation",
334         "FileTrackingInformation",
335         "FileIdBothDirectoryInformation",
336         "FileIdFullDirectoryInformation",
337         "FileValidDataLengthInformation",
338         "FileShortNameInformation",
339         "FileIoCompletionNotificationInformation",
340         "FileIoStatusBlockRangeInformation",
341         "FileIoPriorityHintInformation",
342         "FileSfioReserveInformation",
343         "FileSfioVolumeInformation",
344         "FileHardLinkInformation",
345         "FileProcessIdsUsingFileInformation",
346         "FileNormalizedNameInformation",
347         "FileNetworkPhysicalNameInformation",
348         "FileIdGlobalTxDirectoryInformation",
349         "FileIsRemoteDeviceInformation",
350         "FileAttributeCacheInformation",
351         "FileNumaNodeInformation",
352         "FileStandardLinkInformation",
353         "FileRemoteProtocolInformation",
354         "FileReplaceCompletionInformation",
355         "FileMaximumInformation",
356         "FileDirectoryInformation",
357         "FileFullDirectoryInformation",
358         "FileBothDirectoryInformation",
359         "FileBasicInformation",
360         "FileStandardInformation",
361         "FileInternalInformation",
362         "FileEaInformation",
363         "FileAccessInformation",
364         "FileNameInformation",
365         "FileRenameInformation",
366         "FileLinkInformation",
367         "FileNamesInformation",
368         "FileDispositionInformation",
369         "FilePositionInformation",
370         "FileFullEaInformation",
371         "FileModeInformation",
372         "FileAlignmentInformation",
373         "FileAllInformation",
374         "FileAllocationInformation",
375         "FileEndOfFileInformation",
376         "FileAlternateNameInformation",
377         "FileStreamInformation",
378         "FilePipeInformation",
379         "FilePipeLocalInformation",
380         "FilePipeRemoteInformation",
381         "FileMailslotQueryInformation",
382         "FileMailslotSetInformation",
383         "FileCompressionInformation",
384         "FileObjectIdInformation",
385         "FileCompletionInformation",
386         "FileMoveClusterInformation",
387         "FileQuotaInformation",
388         "FileReparsePointInformation",
389         "FileNetworkOpenInformation",
390         "FileAttributeTagInformation",
391         "FileTrackingInformation",
392         "FileIdBothDirectoryInformation",
393         "FileIdFullDirectoryInformation",
394         "FileValidDataLengthInformation",
395         "FileShortNameInformation",
396         "FileIoCompletionNotificationInformation",
397         "FileIoStatusBlockRangeInformation",
398         "FileIoPriorityHintInformation",
399         "FileSfioReserveInformation",
400         "FileSfioVolumeInformation",
401         "FileHardLinkInformation",
402         "FileProcessIdsUsingFileInformation",
403         "FileNormalizedNameInformation",
404         "FileNetworkPhysicalNameInformation",
405         "FileIdGlobalTxDirectoryInformation",
406         "FileIsRemoteDeviceInformation",
407         "FileAttributeCacheInformation",
408         "FileNumaNodeInformation",
409         "FileStandardLinkInformation",
410         "FileRemoteProtocolInformation",
411         "FileReplaceCompletionInformation",
412         "FileMaximumInformation" };
413     return fileInfoClassNames[infoClass];
414 }
415 
416 /*
417  * FUNCTION: Retrieve the specified file information
418  */
419 NTSTATUS
420 NtfsQueryInformation(PNTFS_IRP_CONTEXT IrpContext)
421 {
422     FILE_INFORMATION_CLASS FileInformationClass;
423     PIO_STACK_LOCATION Stack;
424     PFILE_OBJECT FileObject;
425     PNTFS_FCB Fcb;
426     PVOID SystemBuffer;
427     ULONG BufferLength;
428     PIRP Irp;
429     PDEVICE_OBJECT DeviceObject;
430     NTSTATUS Status = STATUS_SUCCESS;
431 
432     DPRINT1("NtfsQueryInformation(%p)\n", IrpContext);
433 
434     Irp = IrpContext->Irp;
435     Stack = IrpContext->Stack;
436     DeviceObject = IrpContext->DeviceObject;
437     FileInformationClass = Stack->Parameters.QueryFile.FileInformationClass;
438     FileObject = IrpContext->FileObject;
439     Fcb = FileObject->FsContext;
440 
441     SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
442     BufferLength = Stack->Parameters.QueryFile.Length;
443 
444     if (!ExAcquireResourceSharedLite(&Fcb->MainResource,
445                                      BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
446     {
447         return NtfsMarkIrpContextForQueue(IrpContext);
448     }
449 
450     switch (FileInformationClass)
451     {
452         case FileStandardInformation:
453             Status = NtfsGetStandardInformation(Fcb,
454                                                 DeviceObject,
455                                                 SystemBuffer,
456                                                 &BufferLength);
457             break;
458 
459         case FilePositionInformation:
460             Status = NtfsGetPositionInformation(FileObject,
461                                                 SystemBuffer,
462                                                 &BufferLength);
463             break;
464 
465         case FileBasicInformation:
466             Status = NtfsGetBasicInformation(FileObject,
467                                              Fcb,
468                                              DeviceObject,
469                                              SystemBuffer,
470                                              &BufferLength);
471             break;
472 
473         case FileNameInformation:
474             Status = NtfsGetNameInformation(FileObject,
475                                             Fcb,
476                                             DeviceObject,
477                                             SystemBuffer,
478                                             &BufferLength);
479             break;
480 
481         case FileInternalInformation:
482             Status = NtfsGetInternalInformation(Fcb,
483                                                 SystemBuffer,
484                                                 &BufferLength);
485             break;
486 
487         case FileNetworkOpenInformation:
488             Status = NtfsGetNetworkOpenInformation(Fcb,
489                                                    DeviceObject->DeviceExtension,
490                                                    SystemBuffer,
491                                                    &BufferLength);
492             break;
493 
494         case FileStreamInformation:
495             Status = NtfsGetSteamInformation(Fcb,
496                                              DeviceObject->DeviceExtension,
497                                              SystemBuffer,
498                                              &BufferLength);
499             break;
500 
501         case FileAlternateNameInformation:
502         case FileAllInformation:
503             DPRINT1("Unimplemented information class: %s\n", GetInfoClassName(FileInformationClass));
504             Status = STATUS_NOT_IMPLEMENTED;
505             break;
506 
507         default:
508             DPRINT1("Unimplemented information class: %s\n", GetInfoClassName(FileInformationClass));
509             Status = STATUS_INVALID_PARAMETER;
510     }
511 
512     ExReleaseResourceLite(&Fcb->MainResource);
513 
514     if (NT_SUCCESS(Status))
515         Irp->IoStatus.Information =
516             Stack->Parameters.QueryFile.Length - BufferLength;
517     else
518         Irp->IoStatus.Information = 0;
519 
520     return Status;
521 }
522 
523 /**
524 * @name NtfsSetEndOfFile
525 * @implemented
526 *
527 * Sets the end of file (file size) for a given file.
528 *
529 * @param Fcb
530 * Pointer to an NTFS_FCB which describes the target file. Fcb->MainResource should have been
531 * acquired with ExAcquireResourceSharedLite().
532 *
533 * @param FileObject
534 * Pointer to a FILE_OBJECT describing the target file.
535 *
536 * @param DeviceExt
537 * Points to the target disk's DEVICE_EXTENSION
538 *
539 * @param IrpFlags
540 * ULONG describing the flags of the original IRP request (Irp->Flags).
541 *
542 * @param CaseSensitive
543 * Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE
544 * if an application opened the file with the FILE_FLAG_POSIX_SEMANTICS flag.
545 *
546 * @param NewFileSize
547 * Pointer to a LARGE_INTEGER which indicates the new end of file (file size).
548 *
549 * @return
550 * STATUS_SUCCESS if successful,
551 * STATUS_USER_MAPPED_FILE if trying to truncate a file but MmCanFileBeTruncated() returned false,
552 * STATUS_OBJECT_NAME_NOT_FOUND if there was no $DATA attribute associated with the target file,
553 * STATUS_INVALID_PARAMETER if there was no $FILENAME attribute associated with the target file,
554 * STATUS_INSUFFICIENT_RESOURCES if an allocation failed,
555 * STATUS_ACCESS_DENIED if target file is a volume or if paging is involved.
556 *
557 * @remarks As this function sets the size of a file at the file-level
558 * (and not at the attribute level) it's not recommended to use this
559 * function alongside functions that operate on the data attribute directly.
560 *
561 */
562 NTSTATUS
563 NtfsSetEndOfFile(PNTFS_FCB Fcb,
564                  PFILE_OBJECT FileObject,
565                  PDEVICE_EXTENSION DeviceExt,
566                  ULONG IrpFlags,
567                  BOOLEAN CaseSensitive,
568                  PLARGE_INTEGER NewFileSize)
569 {
570     LARGE_INTEGER CurrentFileSize;
571     PFILE_RECORD_HEADER FileRecord;
572     PNTFS_ATTR_CONTEXT DataContext;
573     ULONG AttributeOffset;
574     NTSTATUS Status = STATUS_SUCCESS;
575     ULONGLONG AllocationSize;
576     PFILENAME_ATTRIBUTE FileNameAttribute;
577     ULONGLONG ParentMFTId;
578     UNICODE_STRING FileName;
579 
580 
581     // Allocate non-paged memory for the file record
582     FileRecord = ExAllocateFromNPagedLookasideList(&DeviceExt->FileRecLookasideList);
583     if (FileRecord == NULL)
584     {
585         DPRINT1("Couldn't allocate memory for file record!");
586         return STATUS_INSUFFICIENT_RESOURCES;
587     }
588 
589     // read the file record
590     DPRINT("Reading file record...\n");
591     Status = ReadFileRecord(DeviceExt, Fcb->MFTIndex, FileRecord);
592     if (!NT_SUCCESS(Status))
593     {
594         // We couldn't get the file's record. Free the memory and return the error
595         DPRINT1("Can't find record for %wS!\n", Fcb->ObjectName);
596         ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
597         return Status;
598     }
599 
600     DPRINT("Found record for %wS\n", Fcb->ObjectName);
601 
602     CurrentFileSize.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, NULL);
603 
604     // Are we trying to decrease the file size?
605     if (NewFileSize->QuadPart < CurrentFileSize.QuadPart)
606     {
607         // Is the file mapped?
608         if (!MmCanFileBeTruncated(FileObject->SectionObjectPointer,
609                                   NewFileSize))
610         {
611             DPRINT1("Couldn't decrease file size!\n");
612             ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
613             return STATUS_USER_MAPPED_FILE;
614         }
615     }
616 
617     // Find the attribute with the data stream for our file
618     DPRINT("Finding Data Attribute...\n");
619     Status = FindAttribute(DeviceExt,
620                            FileRecord,
621                            AttributeData,
622                            Fcb->Stream,
623                            wcslen(Fcb->Stream),
624                            &DataContext,
625                            &AttributeOffset);
626 
627     // Did we fail to find the attribute?
628     if (!NT_SUCCESS(Status))
629     {
630         DPRINT1("No '%S' data stream associated with file!\n", Fcb->Stream);
631         ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
632         return Status;
633     }
634 
635     // Get the size of the data attribute
636     CurrentFileSize.QuadPart = AttributeDataLength(DataContext->pRecord);
637 
638     // Are we enlarging the attribute?
639     if (NewFileSize->QuadPart > CurrentFileSize.QuadPart)
640     {
641         // is increasing the stream size not allowed?
642         if ((Fcb->Flags & FCB_IS_VOLUME) ||
643             (IrpFlags & IRP_PAGING_IO))
644         {
645             // TODO - just fail for now
646             ReleaseAttributeContext(DataContext);
647             ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
648             return STATUS_ACCESS_DENIED;
649         }
650     }
651 
652     // set the attribute data length
653     Status = SetAttributeDataLength(FileObject, Fcb, DataContext, AttributeOffset, FileRecord, NewFileSize);
654     if (!NT_SUCCESS(Status))
655     {
656         ReleaseAttributeContext(DataContext);
657         ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
658         return Status;
659     }
660 
661     // now we need to update this file's size in every directory index entry that references it
662     // TODO: expand to work with every filename / hardlink stored in the file record.
663     FileNameAttribute = GetBestFileNameFromRecord(Fcb->Vcb, FileRecord);
664     if (FileNameAttribute == NULL)
665     {
666         DPRINT1("Unable to find FileName attribute associated with file!\n");
667         ReleaseAttributeContext(DataContext);
668         ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
669         return STATUS_INVALID_PARAMETER;
670     }
671 
672     ParentMFTId = FileNameAttribute->DirectoryFileReferenceNumber & NTFS_MFT_MASK;
673 
674     FileName.Buffer = FileNameAttribute->Name;
675     FileName.Length = FileNameAttribute->NameLength * sizeof(WCHAR);
676     FileName.MaximumLength = FileName.Length;
677 
678     AllocationSize = AttributeAllocatedLength(DataContext->pRecord);
679 
680     Status = UpdateFileNameRecord(Fcb->Vcb,
681                                   ParentMFTId,
682                                   &FileName,
683                                   FALSE,
684                                   NewFileSize->QuadPart,
685                                   AllocationSize,
686                                   CaseSensitive);
687 
688     ReleaseAttributeContext(DataContext);
689     ExFreeToNPagedLookasideList(&DeviceExt->FileRecLookasideList, FileRecord);
690 
691     return Status;
692 }
693 
694 /**
695 * @name NtfsSetInformation
696 * @implemented
697 *
698 * Sets the specified file information.
699 *
700 * @param IrpContext
701 * Points to an NTFS_IRP_CONTEXT which describes the set operation
702 *
703 * @return
704 * STATUS_SUCCESS if successful,
705 * STATUS_NOT_IMPLEMENTED if trying to set an unimplemented information class,
706 * STATUS_USER_MAPPED_FILE if trying to truncate a file but MmCanFileBeTruncated() returned false,
707 * STATUS_OBJECT_NAME_NOT_FOUND if there was no $DATA attribute associated with the target file,
708 * STATUS_INVALID_PARAMETER if there was no $FILENAME attribute associated with the target file,
709 * STATUS_INSUFFICIENT_RESOURCES if an allocation failed,
710 * STATUS_ACCESS_DENIED if target file is a volume or if paging is involved.
711 *
712 * @remarks Called by NtfsDispatch() in response to an IRP_MJ_SET_INFORMATION request.
713 * Only the FileEndOfFileInformation InformationClass is fully implemented. FileAllocationInformation
714 * is a hack and not a true implementation, but it's enough to make SetEndOfFile() work.
715 * All other information classes are TODO.
716 *
717 */
718 NTSTATUS
719 NtfsSetInformation(PNTFS_IRP_CONTEXT IrpContext)
720 {
721     FILE_INFORMATION_CLASS FileInformationClass;
722     PIO_STACK_LOCATION Stack;
723     PDEVICE_EXTENSION DeviceExt;
724     PFILE_OBJECT FileObject;
725     PNTFS_FCB Fcb;
726     PVOID SystemBuffer;
727     ULONG BufferLength;
728     PIRP Irp;
729     PDEVICE_OBJECT DeviceObject;
730     NTSTATUS Status = STATUS_NOT_IMPLEMENTED;
731 
732     DPRINT("NtfsSetInformation(%p)\n", IrpContext);
733 
734     Irp = IrpContext->Irp;
735     Stack = IrpContext->Stack;
736     DeviceObject = IrpContext->DeviceObject;
737     DeviceExt = DeviceObject->DeviceExtension;
738     FileInformationClass = Stack->Parameters.QueryFile.FileInformationClass;
739     FileObject = IrpContext->FileObject;
740     Fcb = FileObject->FsContext;
741 
742     SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
743     BufferLength = Stack->Parameters.QueryFile.Length;
744 
745     if (!ExAcquireResourceSharedLite(&Fcb->MainResource,
746                                      BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT)))
747     {
748         return NtfsMarkIrpContextForQueue(IrpContext);
749     }
750 
751     switch (FileInformationClass)
752     {
753         PFILE_END_OF_FILE_INFORMATION EndOfFileInfo;
754 
755         /* TODO: Allocation size is not actually the same as file end for NTFS,
756            however, few applications are likely to make the distinction. */
757         case FileAllocationInformation:
758             DPRINT1("FIXME: Using hacky method of setting FileAllocationInformation.\n");
759         case FileEndOfFileInformation:
760             EndOfFileInfo = (PFILE_END_OF_FILE_INFORMATION)SystemBuffer;
761             Status = NtfsSetEndOfFile(Fcb,
762                                       FileObject,
763                                       DeviceExt,
764                                       Irp->Flags,
765                                       BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE),
766                                       &EndOfFileInfo->EndOfFile);
767             break;
768 
769         // TODO: all other information classes
770 
771         default:
772             DPRINT1("FIXME: Unimplemented information class: %s\n", GetInfoClassName(FileInformationClass));
773             Status = STATUS_NOT_IMPLEMENTED;
774     }
775 
776     ExReleaseResourceLite(&Fcb->MainResource);
777 
778     if (NT_SUCCESS(Status))
779         Irp->IoStatus.Information =
780         Stack->Parameters.QueryFile.Length - BufferLength;
781     else
782         Irp->IoStatus.Information = 0;
783 
784     return Status;
785 }
786 /* EOF */
787