[NTFS] - Add some utility functions and improve some comments. Improve NtfsAddFilenam...
[reactos.git] / drivers / filesystems / ntfs / finfo.c
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 DPRINT1("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 DPRINT1("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 DPRINT1("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 DPRINT1("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 = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
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 = ExAllocatePoolWithTag(NonPagedPool, DeviceExt->NtfsInfo.BytesPerFileRecord, TAG_NTFS);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
595 return Status;
596 }
597
598 DPRINT("Found record for %wS\n", Fcb->ObjectName);
599
600 CurrentFileSize.QuadPart = NtfsGetFileSize(DeviceExt, FileRecord, L"", 0, (PULONGLONG)&CurrentFileSize);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
630 return Status;
631 }
632
633 // Get the size of the data attribute
634 CurrentFileSize.QuadPart = AttributeDataLength(&DataContext->Record);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
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 ExFreePoolWithTag(FileRecord, TAG_NTFS);
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 = ROUND_UP(NewFileSize->QuadPart, Fcb->Vcb->NtfsInfo.BytesPerCluster);
677
678 Status = UpdateFileNameRecord(Fcb->Vcb,
679 ParentMFTId,
680 &FileName,
681 FALSE,
682 NewFileSize->QuadPart,
683 AllocationSize,
684 CaseSensitive);
685
686 ReleaseAttributeContext(DataContext);
687 ExFreePoolWithTag(FileRecord, TAG_NTFS);
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 DPRINT1("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 */