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
NtfsGetStandardInformation(PNTFS_FCB Fcb,PDEVICE_OBJECT DeviceObject,PFILE_STANDARD_INFORMATION StandardInfo,PULONG BufferLength)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
NtfsGetPositionInformation(PFILE_OBJECT FileObject,PFILE_POSITION_INFORMATION PositionInfo,PULONG BufferLength)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
NtfsGetBasicInformation(PFILE_OBJECT FileObject,PNTFS_FCB Fcb,PDEVICE_OBJECT DeviceObject,PFILE_BASIC_INFORMATION BasicInfo,PULONG BufferLength)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
NtfsGetNameInformation(PFILE_OBJECT FileObject,PNTFS_FCB Fcb,PDEVICE_OBJECT DeviceObject,PFILE_NAME_INFORMATION NameInfo,PULONG BufferLength)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
NtfsGetInternalInformation(PNTFS_FCB Fcb,PFILE_INTERNAL_INFORMATION InternalInfo,PULONG BufferLength)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
NtfsGetNetworkOpenInformation(PNTFS_FCB Fcb,PDEVICE_EXTENSION DeviceExt,PFILE_NETWORK_OPEN_INFORMATION NetworkInfo,PULONG BufferLength)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
NtfsGetStreamInformation(PNTFS_FCB Fcb,PDEVICE_EXTENSION DeviceExt,PFILE_STREAM_INFORMATION StreamInfo,PULONG BufferLength)226 NtfsGetStreamInformation(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
GetInfoClassName(FILE_INFORMATION_CLASS infoClass)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
NtfsQueryInformation(PNTFS_IRP_CONTEXT IrpContext)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 = NtfsGetStreamInformation(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
NtfsSetEndOfFile(PNTFS_FCB Fcb,PFILE_OBJECT FileObject,PDEVICE_EXTENSION DeviceExt,ULONG IrpFlags,BOOLEAN CaseSensitive,PLARGE_INTEGER NewFileSize)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
NtfsSetInformation(PNTFS_IRP_CONTEXT IrpContext)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