[NTFS] Don't leak memory in case of failure while creating a directory
[reactos.git] / drivers / filesystems / ntfs / create.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 2002, 2014 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/create.c
22 * PURPOSE: NTFS filesystem driver
23 * PROGRAMMERS: Eric Kohl
24 * Pierre Schweitzer (pierre@reactos.org)
25 */
26
27 /* INCLUDES *****************************************************************/
28
29 #include "ntfs.h"
30
31 #define NDEBUG
32 #include <debug.h>
33
34 static PCWSTR MftIdToName[] = {
35 L"$MFT",
36 L"$MFTMirr",
37 L"$LogFile",
38 L"$Volume",
39 L"AttrDef",
40 L".",
41 L"$Bitmap",
42 L"$Boot",
43 L"$BadClus",
44 L"$Quota",
45 L"$UpCase",
46 L"$Extended",
47 };
48
49 /* FUNCTIONS ****************************************************************/
50
51 static
52 NTSTATUS
53 NtfsMakeAbsoluteFilename(PFILE_OBJECT pFileObject,
54 PWSTR pRelativeFileName,
55 PWSTR *pAbsoluteFilename)
56 {
57 PWSTR rcName;
58 PNTFS_FCB Fcb;
59
60 DPRINT("try related for %S\n", pRelativeFileName);
61 Fcb = pFileObject->FsContext;
62 ASSERT(Fcb);
63
64 if (Fcb->Flags & FCB_IS_VOLUME)
65 {
66 /* This is likely to be an opening by ID, return ourselves */
67 if (pRelativeFileName[0] == L'\\')
68 {
69 *pAbsoluteFilename = NULL;
70 return STATUS_SUCCESS;
71 }
72
73 return STATUS_INVALID_PARAMETER;
74 }
75
76 /* verify related object is a directory and target name
77 don't start with \. */
78 if (NtfsFCBIsDirectory(Fcb) == FALSE ||
79 pRelativeFileName[0] == L'\\')
80 {
81 return STATUS_INVALID_PARAMETER;
82 }
83
84 /* construct absolute path name */
85 ASSERT(wcslen (Fcb->PathName) + 1 + wcslen (pRelativeFileName) + 1 <= MAX_PATH);
86 rcName = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH * sizeof(WCHAR), TAG_NTFS);
87 if (!rcName)
88 {
89 return STATUS_INSUFFICIENT_RESOURCES;
90 }
91
92 wcscpy(rcName, Fcb->PathName);
93 if (!NtfsFCBIsRoot(Fcb))
94 wcscat (rcName, L"\\");
95 wcscat (rcName, pRelativeFileName);
96 *pAbsoluteFilename = rcName;
97
98 return STATUS_SUCCESS;
99 }
100
101
102 static
103 NTSTATUS
104 NtfsMoonWalkID(PDEVICE_EXTENSION DeviceExt,
105 ULONGLONG Id,
106 PUNICODE_STRING OutPath)
107 {
108 NTSTATUS Status;
109 PFILE_RECORD_HEADER MftRecord;
110 PFILENAME_ATTRIBUTE FileName;
111 WCHAR FullPath[MAX_PATH];
112 ULONG WritePosition = MAX_PATH - 1;
113
114 DPRINT1("NtfsMoonWalkID(%p, %I64x, %p)\n", DeviceExt, Id, OutPath);
115
116 RtlZeroMemory(FullPath, sizeof(FullPath));
117 MftRecord = ExAllocatePoolWithTag(NonPagedPool,
118 DeviceExt->NtfsInfo.BytesPerFileRecord,
119 TAG_NTFS);
120 if (MftRecord == NULL)
121 {
122 return STATUS_INSUFFICIENT_RESOURCES;
123 }
124
125 while (TRUE)
126 {
127 Status = ReadFileRecord(DeviceExt, Id, MftRecord);
128 if (!NT_SUCCESS(Status))
129 break;
130
131 ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
132 if (!(MftRecord->Flags & FRH_IN_USE))
133 {
134 Status = STATUS_OBJECT_PATH_NOT_FOUND;
135 break;
136 }
137
138 FileName = GetBestFileNameFromRecord(DeviceExt, MftRecord);
139 if (FileName == NULL)
140 {
141 DPRINT1("$FILE_NAME attribute not found for %I64x\n", Id);
142 Status = STATUS_OBJECT_PATH_NOT_FOUND;
143 break;
144 }
145
146 WritePosition -= FileName->NameLength;
147 ASSERT(WritePosition < MAX_PATH);
148 RtlCopyMemory(FullPath + WritePosition, FileName->Name, FileName->NameLength * sizeof(WCHAR));
149 WritePosition -= 1;
150 ASSERT(WritePosition < MAX_PATH);
151 FullPath[WritePosition] = L'\\';
152
153 Id = FileName->DirectoryFileReferenceNumber & NTFS_MFT_MASK;
154 if (Id == NTFS_FILE_ROOT)
155 break;
156 }
157
158 ExFreePoolWithTag(MftRecord, TAG_NTFS);
159
160 if (!NT_SUCCESS(Status))
161 return Status;
162
163 OutPath->Length = (MAX_PATH - WritePosition - 1) * sizeof(WCHAR);
164 OutPath->MaximumLength = (MAX_PATH - WritePosition) * sizeof(WCHAR);
165 OutPath->Buffer = ExAllocatePoolWithTag(NonPagedPool, OutPath->MaximumLength, TAG_NTFS);
166 if (OutPath->Buffer == NULL)
167 {
168 return STATUS_INSUFFICIENT_RESOURCES;
169 }
170 RtlCopyMemory(OutPath->Buffer, FullPath + WritePosition, OutPath->MaximumLength);
171
172 return Status;
173 }
174
175 static
176 NTSTATUS
177 NtfsOpenFileById(PDEVICE_EXTENSION DeviceExt,
178 PFILE_OBJECT FileObject,
179 ULONGLONG MftId,
180 PNTFS_FCB * FoundFCB)
181 {
182 NTSTATUS Status;
183 PNTFS_FCB FCB;
184 PFILE_RECORD_HEADER MftRecord;
185
186 DPRINT1("NtfsOpenFileById(%p, %p, %I64x, %p)\n", DeviceExt, FileObject, MftId, FoundFCB);
187
188 ASSERT(MftId < NTFS_FILE_FIRST_USER_FILE);
189 if (MftId > 0xb) /* No entries are used yet beyond this */
190 {
191 return STATUS_OBJECT_NAME_NOT_FOUND;
192 }
193
194 MftRecord = ExAllocatePoolWithTag(NonPagedPool,
195 DeviceExt->NtfsInfo.BytesPerFileRecord,
196 TAG_NTFS);
197 if (MftRecord == NULL)
198 {
199 return STATUS_INSUFFICIENT_RESOURCES;
200 }
201
202 Status = ReadFileRecord(DeviceExt, MftId, MftRecord);
203 if (!NT_SUCCESS(Status))
204 {
205 ExFreePoolWithTag(MftRecord, TAG_NTFS);
206 return Status;
207 }
208
209 if (!(MftRecord->Flags & FRH_IN_USE))
210 {
211 ExFreePoolWithTag(MftRecord, TAG_NTFS);
212 return STATUS_OBJECT_PATH_NOT_FOUND;
213 }
214
215 FCB = NtfsGrabFCBFromTable(DeviceExt, MftIdToName[MftId]);
216 if (FCB == NULL)
217 {
218 UNICODE_STRING Name;
219
220 RtlInitUnicodeString(&Name, MftIdToName[MftId]);
221 Status = NtfsMakeFCBFromDirEntry(DeviceExt, NULL, &Name, NULL, MftRecord, MftId, &FCB);
222 if (!NT_SUCCESS(Status))
223 {
224 ExFreePoolWithTag(MftRecord, TAG_NTFS);
225 return Status;
226 }
227 }
228
229 ASSERT(FCB != NULL);
230
231 ExFreePoolWithTag(MftRecord, TAG_NTFS);
232
233 Status = NtfsAttachFCBToFileObject(DeviceExt,
234 FCB,
235 FileObject);
236 *FoundFCB = FCB;
237
238 return Status;
239 }
240
241 /*
242 * FUNCTION: Opens a file
243 */
244 static
245 NTSTATUS
246 NtfsOpenFile(PDEVICE_EXTENSION DeviceExt,
247 PFILE_OBJECT FileObject,
248 PWSTR FileName,
249 BOOLEAN CaseSensitive,
250 PNTFS_FCB * FoundFCB)
251 {
252 PNTFS_FCB ParentFcb;
253 PNTFS_FCB Fcb;
254 NTSTATUS Status;
255 PWSTR AbsFileName = NULL;
256
257 DPRINT1("NtfsOpenFile(%p, %p, %S, %s, %p)\n",
258 DeviceExt,
259 FileObject,
260 FileName,
261 CaseSensitive ? "TRUE" : "FALSE",
262 FoundFCB);
263
264 *FoundFCB = NULL;
265
266 if (FileObject->RelatedFileObject)
267 {
268 DPRINT("Converting relative filename to absolute filename\n");
269
270 Status = NtfsMakeAbsoluteFilename(FileObject->RelatedFileObject,
271 FileName,
272 &AbsFileName);
273 if (AbsFileName) FileName = AbsFileName;
274 if (!NT_SUCCESS(Status))
275 {
276 return Status;
277 }
278 }
279
280 //FIXME: Get canonical path name (remove .'s, ..'s and extra separators)
281
282 DPRINT("PathName to open: %S\n", FileName);
283
284 /* try first to find an existing FCB in memory */
285 DPRINT("Checking for existing FCB in memory\n");
286 Fcb = NtfsGrabFCBFromTable(DeviceExt,
287 FileName);
288 if (Fcb == NULL)
289 {
290 DPRINT("No existing FCB found, making a new one if file exists.\n");
291 Status = NtfsGetFCBForFile(DeviceExt,
292 &ParentFcb,
293 &Fcb,
294 FileName,
295 CaseSensitive);
296 if (ParentFcb != NULL)
297 {
298 NtfsReleaseFCB(DeviceExt,
299 ParentFcb);
300 }
301
302 if (!NT_SUCCESS(Status))
303 {
304 DPRINT("Could not make a new FCB, status: %x\n", Status);
305
306 if (AbsFileName)
307 ExFreePoolWithTag(AbsFileName, TAG_NTFS);
308
309 return Status;
310 }
311 }
312
313 DPRINT("Attaching FCB to fileObject\n");
314 Status = NtfsAttachFCBToFileObject(DeviceExt,
315 Fcb,
316 FileObject);
317
318 if (AbsFileName)
319 ExFreePool(AbsFileName);
320
321 *FoundFCB = Fcb;
322
323 return Status;
324 }
325
326
327 /*
328 * FUNCTION: Opens a file
329 */
330 static
331 NTSTATUS
332 NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
333 PNTFS_IRP_CONTEXT IrpContext)
334 {
335 PDEVICE_EXTENSION DeviceExt;
336 PIO_STACK_LOCATION Stack;
337 PFILE_OBJECT FileObject;
338 ULONG RequestedDisposition;
339 ULONG RequestedOptions;
340 PNTFS_FCB Fcb = NULL;
341 // PWSTR FileName;
342 NTSTATUS Status;
343 UNICODE_STRING FullPath;
344 PIRP Irp = IrpContext->Irp;
345
346 DPRINT1("NtfsCreateFile(%p, %p) called\n", DeviceObject, IrpContext);
347
348 DeviceExt = DeviceObject->DeviceExtension;
349 ASSERT(DeviceExt);
350 Stack = IoGetCurrentIrpStackLocation(Irp);
351 ASSERT(Stack);
352
353 RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
354 RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
355 // PagingFileCreate = (Stack->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE;
356 if (RequestedOptions & FILE_DIRECTORY_FILE &&
357 RequestedDisposition == FILE_SUPERSEDE)
358 {
359 return STATUS_INVALID_PARAMETER;
360 }
361
362 /* Deny create if the volume is locked */
363 if (DeviceExt->Flags & VCB_VOLUME_LOCKED)
364 {
365 return STATUS_ACCESS_DENIED;
366 }
367
368 FileObject = Stack->FileObject;
369
370 if ((RequestedOptions & FILE_OPEN_BY_FILE_ID) == FILE_OPEN_BY_FILE_ID)
371 {
372 ULONGLONG MFTId;
373
374 if (FileObject->FileName.Length != sizeof(ULONGLONG))
375 return STATUS_INVALID_PARAMETER;
376
377 MFTId = (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK;
378 if (MFTId < NTFS_FILE_FIRST_USER_FILE)
379 {
380 Status = NtfsOpenFileById(DeviceExt, FileObject, MFTId, &Fcb);
381 }
382 else
383 {
384 Status = NtfsMoonWalkID(DeviceExt, MFTId, &FullPath);
385 }
386
387 if (!NT_SUCCESS(Status))
388 {
389 return Status;
390 }
391
392 DPRINT1("Open by ID: %I64x -> %wZ\n", (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK, &FullPath);
393 }
394
395 /* This a open operation for the volume itself */
396 if (FileObject->FileName.Length == 0 &&
397 (FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 != NULL))
398 {
399 if (RequestedDisposition != FILE_OPEN &&
400 RequestedDisposition != FILE_OPEN_IF)
401 {
402 return STATUS_ACCESS_DENIED;
403 }
404
405 if (RequestedOptions & FILE_DIRECTORY_FILE)
406 {
407 return STATUS_NOT_A_DIRECTORY;
408 }
409
410 NtfsAttachFCBToFileObject(DeviceExt, DeviceExt->VolumeFcb, FileObject);
411 DeviceExt->VolumeFcb->RefCount++;
412
413 Irp->IoStatus.Information = FILE_OPENED;
414 return STATUS_SUCCESS;
415 }
416
417 if (Fcb == NULL)
418 {
419 Status = NtfsOpenFile(DeviceExt,
420 FileObject,
421 ((RequestedOptions & FILE_OPEN_BY_FILE_ID) ? FullPath.Buffer : FileObject->FileName.Buffer),
422 BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE),
423 &Fcb);
424
425 if (RequestedOptions & FILE_OPEN_BY_FILE_ID)
426 {
427 ExFreePoolWithTag(FullPath.Buffer, TAG_NTFS);
428 }
429 }
430
431 if (NT_SUCCESS(Status))
432 {
433 if (RequestedDisposition == FILE_CREATE)
434 {
435 Irp->IoStatus.Information = FILE_EXISTS;
436 NtfsCloseFile(DeviceExt, FileObject);
437 return STATUS_OBJECT_NAME_COLLISION;
438 }
439
440 if (RequestedOptions & FILE_NON_DIRECTORY_FILE &&
441 NtfsFCBIsDirectory(Fcb))
442 {
443 NtfsCloseFile(DeviceExt, FileObject);
444 return STATUS_FILE_IS_A_DIRECTORY;
445 }
446
447 if (RequestedOptions & FILE_DIRECTORY_FILE &&
448 !NtfsFCBIsDirectory(Fcb))
449 {
450 NtfsCloseFile(DeviceExt, FileObject);
451 return STATUS_NOT_A_DIRECTORY;
452 }
453
454 /*
455 * If it is a reparse point & FILE_OPEN_REPARSE_POINT, then allow opening it
456 * as a normal file.
457 * Otherwise, attempt to read reparse data and hand them to the Io manager
458 * with status reparse to force a reparse.
459 */
460 if (NtfsFCBIsReparsePoint(Fcb) &&
461 ((RequestedOptions & FILE_OPEN_REPARSE_POINT) != FILE_OPEN_REPARSE_POINT))
462 {
463 PREPARSE_DATA_BUFFER ReparseData = NULL;
464
465 Status = NtfsReadFCBAttribute(DeviceExt, Fcb,
466 AttributeReparsePoint, L"", 0,
467 (PVOID *)&Irp->Tail.Overlay.AuxiliaryBuffer);
468 if (NT_SUCCESS(Status))
469 {
470 ReparseData = (PREPARSE_DATA_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer;
471 if (ReparseData->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
472 {
473 Status = STATUS_REPARSE;
474 }
475 else
476 {
477 Status = STATUS_NOT_IMPLEMENTED;
478 ExFreePoolWithTag(ReparseData, TAG_NTFS);
479 }
480 }
481
482 Irp->IoStatus.Information = ((Status == STATUS_REPARSE) ? ReparseData->ReparseTag : 0);
483
484 NtfsCloseFile(DeviceExt, FileObject);
485 return Status;
486 }
487
488 if (RequestedDisposition == FILE_OVERWRITE ||
489 RequestedDisposition == FILE_OVERWRITE_IF ||
490 RequestedDisposition == FILE_SUPERSEDE)
491 {
492 PFILE_RECORD_HEADER fileRecord = NULL;
493 PNTFS_ATTR_CONTEXT dataContext = NULL;
494 ULONG DataAttributeOffset;
495 LARGE_INTEGER Zero;
496 Zero.QuadPart = 0;
497
498 if (!NtfsGlobalData->EnableWriteSupport)
499 {
500 DPRINT1("NTFS write-support is EXPERIMENTAL and is disabled by default!\n");
501 NtfsCloseFile(DeviceExt, FileObject);
502 return STATUS_ACCESS_DENIED;
503 }
504
505 // TODO: check for appropriate access
506
507 ExAcquireResourceExclusiveLite(&(Fcb->MainResource), TRUE);
508
509 fileRecord = ExAllocatePoolWithTag(NonPagedPool,
510 Fcb->Vcb->NtfsInfo.BytesPerFileRecord,
511 TAG_NTFS);
512 if (fileRecord)
513 {
514
515 Status = ReadFileRecord(Fcb->Vcb,
516 Fcb->MFTIndex,
517 fileRecord);
518 if (!NT_SUCCESS(Status))
519 goto DoneOverwriting;
520
521 // find the data attribute and set it's length to 0 (TODO: Handle Alternate Data Streams)
522 Status = FindAttribute(Fcb->Vcb, fileRecord, AttributeData, L"", 0, &dataContext, &DataAttributeOffset);
523 if (!NT_SUCCESS(Status))
524 goto DoneOverwriting;
525
526 Status = SetAttributeDataLength(FileObject, Fcb, dataContext, DataAttributeOffset, fileRecord, &Zero);
527 }
528 else
529 {
530 Status = STATUS_NO_MEMORY;
531 }
532
533 DoneOverwriting:
534 if (fileRecord)
535 ExFreePoolWithTag(fileRecord, TAG_NTFS);
536 if (dataContext)
537 ReleaseAttributeContext(dataContext);
538
539 ExReleaseResourceLite(&(Fcb->MainResource));
540
541 if (!NT_SUCCESS(Status))
542 {
543 NtfsCloseFile(DeviceExt, FileObject);
544 return Status;
545 }
546
547 if (RequestedDisposition == FILE_SUPERSEDE)
548 {
549 Irp->IoStatus.Information = FILE_SUPERSEDED;
550 }
551 else
552 {
553 Irp->IoStatus.Information = FILE_OVERWRITTEN;
554 }
555 }
556 }
557 else
558 {
559 /* HUGLY HACK: Can't create new files yet... */
560 if (RequestedDisposition == FILE_CREATE ||
561 RequestedDisposition == FILE_OPEN_IF ||
562 RequestedDisposition == FILE_OVERWRITE_IF ||
563 RequestedDisposition == FILE_SUPERSEDE)
564 {
565 if (!NtfsGlobalData->EnableWriteSupport)
566 {
567 DPRINT1("NTFS write-support is EXPERIMENTAL and is disabled by default!\n");
568 NtfsCloseFile(DeviceExt, FileObject);
569 return STATUS_ACCESS_DENIED;
570 }
571
572 // Was the user trying to create a directory?
573 if (RequestedOptions & FILE_DIRECTORY_FILE)
574 {
575 // Create the directory on disk
576 Status = NtfsCreateDirectory(DeviceExt,
577 FileObject,
578 BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE),
579 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT));
580 }
581 else
582 {
583 // Create the file record on disk
584 Status = NtfsCreateFileRecord(DeviceExt,
585 FileObject,
586 BooleanFlagOn(Stack->Flags, SL_CASE_SENSITIVE),
587 BooleanFlagOn(IrpContext->Flags, IRPCONTEXT_CANWAIT));
588 }
589
590 if (!NT_SUCCESS(Status))
591 {
592 DPRINT1("ERROR: Couldn't create file record!\n");
593 return Status;
594 }
595
596 // Before we open the file/directory we just created, we need to change the disposition (upper 8 bits of ULONG)
597 // from create to open, since we already created the file
598 Stack->Parameters.Create.Options = (ULONG)FILE_OPEN << 24 | RequestedOptions;
599
600 // Now we should be able to open the file using NtfsCreateFile()
601 Status = NtfsCreateFile(DeviceObject, IrpContext);
602 if (NT_SUCCESS(Status))
603 {
604 // We need to change Irp->IoStatus.Information to reflect creation
605 Irp->IoStatus.Information = FILE_CREATED;
606 }
607 return Status;
608 }
609 }
610
611 if (NT_SUCCESS(Status))
612 {
613 Fcb->OpenHandleCount++;
614 DeviceExt->OpenHandleCount++;
615 }
616
617 /*
618 * If the directory containing the file to open doesn't exist then
619 * fail immediately
620 */
621 Irp->IoStatus.Information = (NT_SUCCESS(Status)) ? FILE_OPENED : 0;
622
623 return Status;
624 }
625
626
627 NTSTATUS
628 NtfsCreate(PNTFS_IRP_CONTEXT IrpContext)
629 {
630 PDEVICE_EXTENSION DeviceExt;
631 NTSTATUS Status;
632 PDEVICE_OBJECT DeviceObject;
633
634 DeviceObject = IrpContext->DeviceObject;
635 if (DeviceObject == NtfsGlobalData->DeviceObject)
636 {
637 /* DeviceObject represents FileSystem instead of logical volume */
638 DPRINT("Opening file system\n");
639 IrpContext->Irp->IoStatus.Information = FILE_OPENED;
640 return STATUS_SUCCESS;
641 }
642
643 DeviceExt = DeviceObject->DeviceExtension;
644
645 if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT))
646 {
647 return NtfsMarkIrpContextForQueue(IrpContext);
648 }
649
650 ExAcquireResourceExclusiveLite(&DeviceExt->DirResource,
651 TRUE);
652 Status = NtfsCreateFile(DeviceObject,
653 IrpContext);
654 ExReleaseResourceLite(&DeviceExt->DirResource);
655
656 return Status;
657 }
658
659 /**
660 * @name NtfsCreateDirectory()
661 * @implemented
662 *
663 * Creates a file record for a new directory and saves it to the MFT. Adds the filename attribute of the
664 * created directory to the parent directory's index.
665 *
666 * @param DeviceExt
667 * Points to the target disk's DEVICE_EXTENSION
668 *
669 * @param FileObject
670 * Pointer to a FILE_OBJECT describing the directory to be created
671 *
672 * @param CaseSensitive
673 * Boolean indicating if the function should operate in case-sensitive mode. This will be TRUE
674 * if an application created the folder with the FILE_FLAG_POSIX_SEMANTICS flag.
675 *
676 * @param CanWait
677 * Boolean indicating if the function is allowed to wait for exclusive access to the master file table.
678 * This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged.
679 *
680 * @return
681 * STATUS_SUCCESS on success.
682 * STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file record.
683 * STATUS_CANT_WAIT if CanWait was FALSE and the function needed to resize the MFT but
684 * couldn't get immediate, exclusive access to it.
685 */
686 NTSTATUS
687 NtfsCreateDirectory(PDEVICE_EXTENSION DeviceExt,
688 PFILE_OBJECT FileObject,
689 BOOLEAN CaseSensitive,
690 BOOLEAN CanWait)
691 {
692
693 NTSTATUS Status = STATUS_SUCCESS;
694 PFILE_RECORD_HEADER FileRecord;
695 PNTFS_ATTR_RECORD NextAttribute;
696 PFILENAME_ATTRIBUTE FilenameAttribute;
697 ULONGLONG ParentMftIndex;
698 ULONGLONG FileMftIndex;
699 PB_TREE Tree;
700 PINDEX_ROOT_ATTRIBUTE NewIndexRoot;
701 ULONG MaxIndexRootSize;
702 ULONG RootLength;
703
704 DPRINT1("NtfsCreateFileRecord(%p, %p, %s, %s)\n",
705 DeviceExt,
706 FileObject,
707 CaseSensitive ? "TRUE" : "FALSE",
708 CanWait ? "TRUE" : "FALSE");
709
710 // Start with an empty file record
711 FileRecord = NtfsCreateEmptyFileRecord(DeviceExt);
712 if (!FileRecord)
713 {
714 DPRINT1("ERROR: Unable to allocate memory for file record!\n");
715 return STATUS_INSUFFICIENT_RESOURCES;
716 }
717
718 // Set the directory flag
719 FileRecord->Flags |= FRH_DIRECTORY;
720
721 // find where the first attribute will be added
722 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
723
724 // add first attribute, $STANDARD_INFORMATION
725 AddStandardInformation(FileRecord, NextAttribute);
726
727 // advance NextAttribute pointer to the next attribute
728 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
729
730 // Add the $FILE_NAME attribute
731 AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject, CaseSensitive, &ParentMftIndex);
732
733 // save a pointer to the filename attribute
734 FilenameAttribute = (PFILENAME_ATTRIBUTE)((ULONG_PTR)NextAttribute + NextAttribute->Resident.ValueOffset);
735
736 // advance NextAttribute pointer to the next attribute
737 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
738
739 // Create an empty b-tree to represent our new index
740 Status = CreateEmptyBTree(&Tree);
741 if (!NT_SUCCESS(Status))
742 {
743 DPRINT1("ERROR: Failed to create empty B-Tree!\n");
744 ExFreePoolWithTag(FileRecord, TAG_NTFS);
745 return Status;
746 }
747
748 // Calculate maximum size of index root
749 MaxIndexRootSize = DeviceExt->NtfsInfo.BytesPerFileRecord
750 - ((ULONG_PTR)NextAttribute - (ULONG_PTR)FileRecord)
751 - sizeof(ULONG) * 2;
752
753 // Create a new index record from the tree
754 Status = CreateIndexRootFromBTree(DeviceExt,
755 Tree,
756 MaxIndexRootSize,
757 &NewIndexRoot,
758 &RootLength);
759 if (!NT_SUCCESS(Status))
760 {
761 DPRINT1("ERROR: Unable to create empty index root!\n");
762 DestroyBTree(Tree);
763 ExFreePoolWithTag(FileRecord, TAG_NTFS);
764 return Status;
765 }
766
767 // We're done with the B-Tree
768 DestroyBTree(Tree);
769
770 // add the $INDEX_ROOT attribute
771 Status = AddIndexRoot(DeviceExt, FileRecord, NextAttribute, NewIndexRoot, RootLength, L"$I30", 4);
772 if (!NT_SUCCESS(Status))
773 {
774 DPRINT1("ERROR: Failed to add index root to new file record!\n");
775 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
776 ExFreePoolWithTag(FileRecord, TAG_NTFS);
777 return Status;
778 }
779
780
781 #ifndef NDEBUG
782 NtfsDumpFileRecord(DeviceExt, FileRecord);
783 #endif
784
785 // Now that we've built the file record in memory, we need to store it in the MFT.
786 Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait);
787 if (NT_SUCCESS(Status))
788 {
789 // The highest 2 bytes should be the sequence number, unless the parent happens to be root
790 if (FileMftIndex == NTFS_FILE_ROOT)
791 FileMftIndex = FileMftIndex + ((ULONGLONG)NTFS_FILE_ROOT << 48);
792 else
793 FileMftIndex = FileMftIndex + ((ULONGLONG)FileRecord->SequenceNumber << 48);
794
795 DPRINT1("New File Reference: 0x%016I64x\n", FileMftIndex);
796
797 // Add the filename attribute to the filename-index of the parent directory
798 Status = NtfsAddFilenameToDirectory(DeviceExt,
799 ParentMftIndex,
800 FileMftIndex,
801 FilenameAttribute,
802 CaseSensitive);
803 }
804
805 ExFreePoolWithTag(NewIndexRoot, TAG_NTFS);
806 ExFreePoolWithTag(FileRecord, TAG_NTFS);
807
808 return Status;
809 }
810
811 /**
812 * @name NtfsCreateEmptyFileRecord
813 * @implemented
814 *
815 * Creates a new, empty file record, with no attributes.
816 *
817 * @param DeviceExt
818 * Pointer to the DEVICE_EXTENSION of the target volume the file record will be stored on.
819 *
820 * @return
821 * A pointer to the newly-created FILE_RECORD_HEADER if the function succeeds, NULL otherwise.
822 */
823 PFILE_RECORD_HEADER
824 NtfsCreateEmptyFileRecord(PDEVICE_EXTENSION DeviceExt)
825 {
826 PFILE_RECORD_HEADER FileRecord;
827 PNTFS_ATTR_RECORD NextAttribute;
828
829 DPRINT1("NtfsCreateEmptyFileRecord(%p)\n", DeviceExt);
830
831 // allocate memory for file record
832 FileRecord = ExAllocatePoolWithTag(NonPagedPool,
833 DeviceExt->NtfsInfo.BytesPerFileRecord,
834 TAG_NTFS);
835 if (!FileRecord)
836 {
837 DPRINT1("ERROR: Unable to allocate memory for file record!\n");
838 return NULL;
839 }
840
841 RtlZeroMemory(FileRecord, DeviceExt->NtfsInfo.BytesPerFileRecord);
842
843 FileRecord->Ntfs.Type = NRH_FILE_TYPE;
844
845 // calculate USA offset and count
846 FileRecord->Ntfs.UsaOffset = FIELD_OFFSET(FILE_RECORD_HEADER, MFTRecordNumber) + sizeof(ULONG);
847
848 // size of USA (in ULONG's) will be 1 (for USA number) + 1 for every sector the file record uses
849 FileRecord->BytesAllocated = DeviceExt->NtfsInfo.BytesPerFileRecord;
850 FileRecord->Ntfs.UsaCount = (FileRecord->BytesAllocated / DeviceExt->NtfsInfo.BytesPerSector) + 1;
851
852 // setup other file record fields
853 FileRecord->SequenceNumber = 1;
854 FileRecord->AttributeOffset = FileRecord->Ntfs.UsaOffset + (2 * FileRecord->Ntfs.UsaCount);
855 FileRecord->AttributeOffset = ALIGN_UP_BY(FileRecord->AttributeOffset, ATTR_RECORD_ALIGNMENT);
856 FileRecord->Flags = FRH_IN_USE;
857 FileRecord->BytesInUse = FileRecord->AttributeOffset + sizeof(ULONG) * 2;
858
859 // find where the first attribute will be added
860 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
861
862 // mark the (temporary) end of the file-record
863 NextAttribute->Type = AttributeEnd;
864 NextAttribute->Length = FILE_RECORD_END;
865
866 return FileRecord;
867 }
868
869
870 /**
871 * @name NtfsCreateFileRecord()
872 * @implemented
873 *
874 * Creates a file record and saves it to the MFT. Adds the filename attribute of the
875 * created file to the parent directory's index.
876 *
877 * @param DeviceExt
878 * Points to the target disk's DEVICE_EXTENSION
879 *
880 * @param FileObject
881 * Pointer to a FILE_OBJECT describing the file to be created
882 *
883 * @param CanWait
884 * Boolean indicating if the function is allowed to wait for exclusive access to the master file table.
885 * This will only be relevant if the MFT doesn't have any free file records and needs to be enlarged.
886 *
887 * @return
888 * STATUS_SUCCESS on success.
889 * STATUS_INSUFFICIENT_RESOURCES if unable to allocate memory for the file record.
890 * STATUS_CANT_WAIT if CanWait was FALSE and the function needed to resize the MFT but
891 * couldn't get immediate, exclusive access to it.
892 */
893 NTSTATUS
894 NtfsCreateFileRecord(PDEVICE_EXTENSION DeviceExt,
895 PFILE_OBJECT FileObject,
896 BOOLEAN CaseSensitive,
897 BOOLEAN CanWait)
898 {
899 NTSTATUS Status = STATUS_SUCCESS;
900 PFILE_RECORD_HEADER FileRecord;
901 PNTFS_ATTR_RECORD NextAttribute;
902 PFILENAME_ATTRIBUTE FilenameAttribute;
903 ULONGLONG ParentMftIndex;
904 ULONGLONG FileMftIndex;
905
906 DPRINT1("NtfsCreateFileRecord(%p, %p, %s, %s)\n",
907 DeviceExt,
908 FileObject,
909 CaseSensitive ? "TRUE" : "FALSE",
910 CanWait ? "TRUE" : "FALSE");
911
912 // allocate memory for file record
913 FileRecord = NtfsCreateEmptyFileRecord(DeviceExt);
914 if (!FileRecord)
915 {
916 DPRINT1("ERROR: Unable to allocate memory for file record!\n");
917 return STATUS_INSUFFICIENT_RESOURCES;
918 }
919
920 // find where the first attribute will be added
921 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)FileRecord + FileRecord->AttributeOffset);
922
923 // add first attribute, $STANDARD_INFORMATION
924 AddStandardInformation(FileRecord, NextAttribute);
925
926 // advance NextAttribute pointer to the next attribute
927 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
928
929 // Add the $FILE_NAME attribute
930 AddFileName(FileRecord, NextAttribute, DeviceExt, FileObject, CaseSensitive, &ParentMftIndex);
931
932 // save a pointer to the filename attribute
933 FilenameAttribute = (PFILENAME_ATTRIBUTE)((ULONG_PTR)NextAttribute + NextAttribute->Resident.ValueOffset);
934
935 // advance NextAttribute pointer to the next attribute
936 NextAttribute = (PNTFS_ATTR_RECORD)((ULONG_PTR)NextAttribute + (ULONG_PTR)NextAttribute->Length);
937
938 // add the $DATA attribute
939 AddData(FileRecord, NextAttribute);
940
941 #ifndef NDEBUG
942 // dump file record in memory (for debugging)
943 NtfsDumpFileRecord(DeviceExt, FileRecord);
944 #endif
945
946 // Now that we've built the file record in memory, we need to store it in the MFT.
947 Status = AddNewMftEntry(FileRecord, DeviceExt, &FileMftIndex, CanWait);
948 if (NT_SUCCESS(Status))
949 {
950 // The highest 2 bytes should be the sequence number, unless the parent happens to be root
951 if (FileMftIndex == NTFS_FILE_ROOT)
952 FileMftIndex = FileMftIndex + ((ULONGLONG)NTFS_FILE_ROOT << 48);
953 else
954 FileMftIndex = FileMftIndex + ((ULONGLONG)FileRecord->SequenceNumber << 48);
955
956 DPRINT1("New File Reference: 0x%016I64x\n", FileMftIndex);
957
958 // Add the filename attribute to the filename-index of the parent directory
959 Status = NtfsAddFilenameToDirectory(DeviceExt,
960 ParentMftIndex,
961 FileMftIndex,
962 FilenameAttribute,
963 CaseSensitive);
964 }
965
966 ExFreePoolWithTag(FileRecord, TAG_NTFS);
967
968 return Status;
969 }
970
971 /* EOF */