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