[NTFS]
[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 < 0x10);
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 PNTFS_FCB * FoundFCB)
250 {
251 PNTFS_FCB ParentFcb;
252 PNTFS_FCB Fcb;
253 NTSTATUS Status;
254 PWSTR AbsFileName = NULL;
255
256 DPRINT1("NtfsOpenFile(%p, %p, %S, %p)\n", DeviceExt, FileObject, FileName, FoundFCB);
257
258 *FoundFCB = NULL;
259
260 if (FileObject->RelatedFileObject)
261 {
262 DPRINT("Converting relative filename to absolute filename\n");
263
264 Status = NtfsMakeAbsoluteFilename(FileObject->RelatedFileObject,
265 FileName,
266 &AbsFileName);
267 if (AbsFileName) FileName = AbsFileName;
268 if (!NT_SUCCESS(Status))
269 {
270 return Status;
271 }
272 }
273
274 //FIXME: Get canonical path name (remove .'s, ..'s and extra separators)
275
276 DPRINT("PathName to open: %S\n", FileName);
277
278 /* try first to find an existing FCB in memory */
279 DPRINT("Checking for existing FCB in memory\n");
280 Fcb = NtfsGrabFCBFromTable(DeviceExt,
281 FileName);
282 if (Fcb == NULL)
283 {
284 DPRINT("No existing FCB found, making a new one if file exists.\n");
285 Status = NtfsGetFCBForFile(DeviceExt,
286 &ParentFcb,
287 &Fcb,
288 FileName);
289 if (ParentFcb != NULL)
290 {
291 NtfsReleaseFCB(DeviceExt,
292 ParentFcb);
293 }
294
295 if (!NT_SUCCESS (Status))
296 {
297 DPRINT("Could not make a new FCB, status: %x\n", Status);
298
299 if (AbsFileName)
300 ExFreePool(AbsFileName);
301
302 return Status;
303 }
304 }
305
306 DPRINT("Attaching FCB to fileObject\n");
307 Status = NtfsAttachFCBToFileObject(DeviceExt,
308 Fcb,
309 FileObject);
310
311 if (AbsFileName)
312 ExFreePool(AbsFileName);
313
314 *FoundFCB = Fcb;
315
316 return Status;
317 }
318
319
320 /*
321 * FUNCTION: Opens a file
322 */
323 static
324 NTSTATUS
325 NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
326 PIRP Irp)
327 {
328 PDEVICE_EXTENSION DeviceExt;
329 PIO_STACK_LOCATION Stack;
330 PFILE_OBJECT FileObject;
331 ULONG RequestedDisposition;
332 ULONG RequestedOptions;
333 PNTFS_FCB Fcb = NULL;
334 // PWSTR FileName;
335 NTSTATUS Status;
336 UNICODE_STRING FullPath;
337
338 DPRINT1("NtfsCreateFile(%p, %p) called\n", DeviceObject, Irp);
339
340 DeviceExt = DeviceObject->DeviceExtension;
341 ASSERT(DeviceExt);
342 Stack = IoGetCurrentIrpStackLocation (Irp);
343 ASSERT(Stack);
344
345 RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
346 RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
347 // PagingFileCreate = (Stack->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE;
348 if (RequestedOptions & FILE_DIRECTORY_FILE &&
349 RequestedDisposition == FILE_SUPERSEDE)
350 {
351 return STATUS_INVALID_PARAMETER;
352 }
353
354 /* Deny create if the volume is locked */
355 if (DeviceExt->Flags & VCB_VOLUME_LOCKED)
356 {
357 return STATUS_ACCESS_DENIED;
358 }
359
360 FileObject = Stack->FileObject;
361
362 if ((RequestedOptions & FILE_OPEN_BY_FILE_ID) == FILE_OPEN_BY_FILE_ID)
363 {
364 ULONGLONG MFTId;
365
366 if (FileObject->FileName.Length != sizeof(ULONGLONG))
367 return STATUS_INVALID_PARAMETER;
368
369 MFTId = (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK;
370 if (MFTId < 0x10)
371 {
372 Status = NtfsOpenFileById(DeviceExt, FileObject, MFTId, &Fcb);
373 }
374 else
375 {
376 Status = NtfsMoonWalkID(DeviceExt, MFTId, &FullPath);
377 }
378
379 if (!NT_SUCCESS(Status))
380 {
381 return Status;
382 }
383
384 DPRINT1("Open by ID: %I64x -> %wZ\n", (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK, &FullPath);
385 }
386
387 /* This a open operation for the volume itself */
388 if (FileObject->FileName.Length == 0 &&
389 (FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 != NULL))
390 {
391 if (RequestedDisposition != FILE_OPEN &&
392 RequestedDisposition != FILE_OPEN_IF)
393 {
394 return STATUS_ACCESS_DENIED;
395 }
396
397 if (RequestedOptions & FILE_DIRECTORY_FILE)
398 {
399 return STATUS_NOT_A_DIRECTORY;
400 }
401
402 NtfsAttachFCBToFileObject(DeviceExt, DeviceExt->VolumeFcb, FileObject);
403 DeviceExt->VolumeFcb->RefCount++;
404
405 Irp->IoStatus.Information = FILE_OPENED;
406 return STATUS_SUCCESS;
407 }
408
409 if (Fcb == NULL)
410 {
411 Status = NtfsOpenFile(DeviceExt,
412 FileObject,
413 ((RequestedOptions & FILE_OPEN_BY_FILE_ID) ? FullPath.Buffer : FileObject->FileName.Buffer),
414 &Fcb);
415
416 if (RequestedOptions & FILE_OPEN_BY_FILE_ID)
417 {
418 ExFreePoolWithTag(FullPath.Buffer, TAG_NTFS);
419 }
420 }
421
422 if (NT_SUCCESS(Status))
423 {
424 if (RequestedDisposition == FILE_CREATE)
425 {
426 Irp->IoStatus.Information = FILE_EXISTS;
427 NtfsCloseFile(DeviceExt, FileObject);
428 return STATUS_OBJECT_NAME_COLLISION;
429 }
430
431 if (RequestedOptions & FILE_NON_DIRECTORY_FILE &&
432 NtfsFCBIsDirectory(Fcb))
433 {
434 NtfsCloseFile(DeviceExt, FileObject);
435 return STATUS_FILE_IS_A_DIRECTORY;
436 }
437
438 if (RequestedOptions & FILE_DIRECTORY_FILE &&
439 !NtfsFCBIsDirectory(Fcb))
440 {
441 NtfsCloseFile(DeviceExt, FileObject);
442 return STATUS_NOT_A_DIRECTORY;
443 }
444
445 /*
446 * If it is a reparse point & FILE_OPEN_REPARSE_POINT, then allow opening it
447 * as a normal file.
448 * Otherwise, attempt to read reparse data and hand them to the Io manager
449 * with status reparse to force a reparse.
450 */
451 if (NtfsFCBIsReparsePoint(Fcb) &&
452 ((RequestedOptions & FILE_OPEN_REPARSE_POINT) != FILE_OPEN_REPARSE_POINT))
453 {
454 PREPARSE_DATA_BUFFER ReparseData = NULL;
455
456 Status = NtfsReadFCBAttribute(DeviceExt, Fcb,
457 AttributeReparsePoint, L"", 0,
458 (PVOID *)&Irp->Tail.Overlay.AuxiliaryBuffer);
459 if (NT_SUCCESS(Status))
460 {
461 ReparseData = (PREPARSE_DATA_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer;
462 if (ReparseData->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
463 {
464 Status = STATUS_REPARSE;
465 }
466 else
467 {
468 Status = STATUS_NOT_IMPLEMENTED;
469 ExFreePoolWithTag(ReparseData, TAG_NTFS);
470 }
471 }
472
473 Irp->IoStatus.Information = ((Status == STATUS_REPARSE) ? ReparseData->ReparseTag : 0);
474
475 NtfsCloseFile(DeviceExt, FileObject);
476 return Status;
477 }
478
479 if (RequestedDisposition == FILE_OVERWRITE ||
480 RequestedDisposition == FILE_OVERWRITE_IF ||
481 RequestedDisposition == FILE_SUPERSEDE)
482 {
483 PFILE_RECORD_HEADER fileRecord = NULL;
484 PNTFS_ATTR_CONTEXT dataContext = NULL;
485 ULONG DataAttributeOffset;
486 LARGE_INTEGER Zero;
487 Zero.QuadPart = 0;
488
489 // TODO: check for appropriate access
490
491 ExAcquireResourceExclusiveLite(&(Fcb->MainResource), TRUE);
492
493 fileRecord = ExAllocatePoolWithTag(NonPagedPool,
494 Fcb->Vcb->NtfsInfo.BytesPerFileRecord,
495 TAG_NTFS);
496 if (fileRecord)
497 {
498
499 Status = ReadFileRecord(Fcb->Vcb,
500 Fcb->MFTIndex,
501 fileRecord);
502 if (!NT_SUCCESS(Status))
503 goto DoneOverwriting;
504
505 // find the data attribute and set it's length to 0 (TODO: Handle Alternate Data Streams)
506 Status = FindAttribute(Fcb->Vcb, fileRecord, AttributeData, L"", 0, &dataContext, &DataAttributeOffset);
507 if (!NT_SUCCESS(Status))
508 goto DoneOverwriting;
509
510 Status = SetAttributeDataLength(FileObject, Fcb, dataContext, DataAttributeOffset, fileRecord, &Zero);
511 }
512 else
513 {
514 Status = STATUS_NO_MEMORY;
515 }
516
517 DoneOverwriting:
518 if (fileRecord)
519 ExFreePool(fileRecord);
520 if (dataContext)
521 ReleaseAttributeContext(dataContext);
522
523 ExReleaseResourceLite(&(Fcb->MainResource));
524
525 if (!NT_SUCCESS(Status))
526 {
527 NtfsCloseFile(DeviceExt, FileObject);
528 return Status;
529 }
530
531 if (RequestedDisposition == FILE_SUPERSEDE)
532 {
533 Irp->IoStatus.Information = FILE_SUPERSEDED;
534 }
535 else
536 {
537 Irp->IoStatus.Information = FILE_OVERWRITTEN;
538 }
539 }
540 }
541 else
542 {
543 /* HUGLY HACK: Can't create new files yet... */
544 if (RequestedDisposition == FILE_CREATE ||
545 RequestedDisposition == FILE_OPEN_IF ||
546 RequestedDisposition == FILE_OVERWRITE_IF ||
547 RequestedDisposition == FILE_SUPERSEDE)
548 {
549 DPRINT1("Denying file creation request on NTFS volume\n");
550 return STATUS_CANNOT_MAKE;
551 }
552 }
553
554 if (NT_SUCCESS(Status))
555 {
556 Fcb->OpenHandleCount++;
557 DeviceExt->OpenHandleCount++;
558 }
559
560 /*
561 * If the directory containing the file to open doesn't exist then
562 * fail immediately
563 */
564 Irp->IoStatus.Information = (NT_SUCCESS(Status)) ? FILE_OPENED : 0;
565
566 return Status;
567 }
568
569
570 NTSTATUS
571 NtfsCreate(PNTFS_IRP_CONTEXT IrpContext)
572 {
573 PDEVICE_EXTENSION DeviceExt;
574 NTSTATUS Status;
575 PDEVICE_OBJECT DeviceObject;
576
577 DeviceObject = IrpContext->DeviceObject;
578 if (DeviceObject == NtfsGlobalData->DeviceObject)
579 {
580 /* DeviceObject represents FileSystem instead of logical volume */
581 DPRINT("Opening file system\n");
582 IrpContext->Irp->IoStatus.Information = FILE_OPENED;
583 return STATUS_SUCCESS;
584 }
585
586 DeviceExt = DeviceObject->DeviceExtension;
587
588 if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT))
589 {
590 return NtfsMarkIrpContextForQueue(IrpContext);
591 }
592
593 ExAcquireResourceExclusiveLite(&DeviceExt->DirResource,
594 TRUE);
595 Status = NtfsCreateFile(DeviceObject,
596 IrpContext->Irp);
597 ExReleaseResourceLite(&DeviceExt->DirResource);
598
599 return Status;
600 }
601
602 /* EOF */