[NTFS]
[reactos.git] / reactos / 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 /* FUNCTIONS ****************************************************************/
35
36 static
37 NTSTATUS
38 NtfsMakeAbsoluteFilename(PFILE_OBJECT pFileObject,
39 PWSTR pRelativeFileName,
40 PWSTR *pAbsoluteFilename)
41 {
42 PWSTR rcName;
43 PNTFS_FCB Fcb;
44
45 DPRINT("try related for %S\n", pRelativeFileName);
46 Fcb = pFileObject->FsContext;
47 ASSERT(Fcb);
48
49 if (Fcb->Flags & FCB_IS_VOLUME)
50 {
51 /* This is likely to be an opening by ID, return ourselves */
52 if (pRelativeFileName[0] == L'\\')
53 {
54 *pAbsoluteFilename = NULL;
55 return STATUS_SUCCESS;
56 }
57
58 return STATUS_INVALID_PARAMETER;
59 }
60
61 /* verify related object is a directory and target name
62 don't start with \. */
63 if (NtfsFCBIsDirectory(Fcb) == FALSE ||
64 pRelativeFileName[0] == L'\\')
65 {
66 return STATUS_INVALID_PARAMETER;
67 }
68
69 /* construct absolute path name */
70 ASSERT(wcslen (Fcb->PathName) + 1 + wcslen (pRelativeFileName) + 1 <= MAX_PATH);
71 rcName = ExAllocatePoolWithTag(NonPagedPool, MAX_PATH * sizeof(WCHAR), TAG_NTFS);
72 if (!rcName)
73 {
74 return STATUS_INSUFFICIENT_RESOURCES;
75 }
76
77 wcscpy(rcName, Fcb->PathName);
78 if (!NtfsFCBIsRoot(Fcb))
79 wcscat (rcName, L"\\");
80 wcscat (rcName, pRelativeFileName);
81 *pAbsoluteFilename = rcName;
82
83 return STATUS_SUCCESS;
84 }
85
86
87 static
88 NTSTATUS
89 NtfsMoonWalkID(PDEVICE_EXTENSION DeviceExt,
90 ULONGLONG Id,
91 PUNICODE_STRING OutPath)
92 {
93 NTSTATUS Status;
94 PFILE_RECORD_HEADER MftRecord;
95 PFILENAME_ATTRIBUTE FileName;
96 WCHAR FullPath[MAX_PATH];
97 ULONG WritePosition = MAX_PATH - 1;
98
99 DPRINT1("NtfsMoonWalkID(%p, %I64x, %p)\n", DeviceExt, Id, OutPath);
100
101 Id = Id & NTFS_MFT_MASK;
102
103 RtlZeroMemory(FullPath, sizeof(FullPath));
104 MftRecord = ExAllocatePoolWithTag(NonPagedPool,
105 DeviceExt->NtfsInfo.BytesPerFileRecord,
106 TAG_NTFS);
107 if (MftRecord == NULL)
108 {
109 return STATUS_INSUFFICIENT_RESOURCES;
110 }
111
112 while (TRUE)
113 {
114 Status = ReadFileRecord(DeviceExt, Id, MftRecord);
115 if (!NT_SUCCESS(Status))
116 break;
117
118 ASSERT(MftRecord->Ntfs.Type == NRH_FILE_TYPE);
119
120 FileName = GetBestFileNameFromRecord(MftRecord);
121 WritePosition -= FileName->NameLength;
122 ASSERT(WritePosition < MAX_PATH);
123 RtlCopyMemory(FullPath + WritePosition, FileName->Name, FileName->NameLength * sizeof(WCHAR));
124 WritePosition -= 1;
125 ASSERT(WritePosition < MAX_PATH);
126 FullPath[WritePosition] = L'\\';
127
128 Id = FileName->DirectoryFileReferenceNumber & NTFS_MFT_MASK;
129 if (Id == NTFS_FILE_ROOT)
130 break;
131 }
132
133 ExFreePoolWithTag(MftRecord, TAG_NTFS);
134
135 OutPath->Length = (MAX_PATH - WritePosition - 1) * sizeof(WCHAR);
136 OutPath->MaximumLength = (MAX_PATH - WritePosition) * sizeof(WCHAR);
137 OutPath->Buffer = ExAllocatePoolWithTag(NonPagedPool, OutPath->MaximumLength, TAG_NTFS);
138 if (OutPath->Buffer == NULL)
139 {
140 return STATUS_INSUFFICIENT_RESOURCES;
141 }
142 RtlCopyMemory(OutPath->Buffer, FullPath + WritePosition, OutPath->MaximumLength);
143
144 return Status;
145 }
146
147 /*
148 * FUNCTION: Opens a file
149 */
150 static
151 NTSTATUS
152 NtfsOpenFile(PDEVICE_EXTENSION DeviceExt,
153 PFILE_OBJECT FileObject,
154 PWSTR FileName,
155 PNTFS_FCB * FoundFCB)
156 {
157 PNTFS_FCB ParentFcb;
158 PNTFS_FCB Fcb;
159 NTSTATUS Status;
160 PWSTR AbsFileName = NULL;
161
162 DPRINT1("NtfsOpenFile(%p, %p, %S, %p)\n", DeviceExt, FileObject, FileName, FoundFCB);
163
164 *FoundFCB = NULL;
165
166 if (FileObject->RelatedFileObject)
167 {
168 DPRINT("Converting relative filename to absolute filename\n");
169
170 Status = NtfsMakeAbsoluteFilename(FileObject->RelatedFileObject,
171 FileName,
172 &AbsFileName);
173 if (AbsFileName) FileName = AbsFileName;
174 if (!NT_SUCCESS(Status))
175 {
176 return Status;
177 }
178 }
179
180 //FIXME: Get cannonical path name (remove .'s, ..'s and extra separators)
181
182 DPRINT("PathName to open: %S\n", FileName);
183
184 /* try first to find an existing FCB in memory */
185 DPRINT("Checking for existing FCB in memory\n");
186 Fcb = NtfsGrabFCBFromTable(DeviceExt,
187 FileName);
188 if (Fcb == NULL)
189 {
190 DPRINT("No existing FCB found, making a new one if file exists.\n");
191 Status = NtfsGetFCBForFile(DeviceExt,
192 &ParentFcb,
193 &Fcb,
194 FileName);
195 if (ParentFcb != NULL)
196 {
197 NtfsReleaseFCB(DeviceExt,
198 ParentFcb);
199 }
200
201 if (!NT_SUCCESS (Status))
202 {
203 DPRINT("Could not make a new FCB, status: %x\n", Status);
204
205 if (AbsFileName)
206 ExFreePool(AbsFileName);
207
208 return Status;
209 }
210 }
211
212 DPRINT("Attaching FCB to fileObject\n");
213 Status = NtfsAttachFCBToFileObject(DeviceExt,
214 Fcb,
215 FileObject);
216
217 if (AbsFileName)
218 ExFreePool(AbsFileName);
219
220 *FoundFCB = Fcb;
221
222 return Status;
223 }
224
225
226 /*
227 * FUNCTION: Opens a file
228 */
229 static
230 NTSTATUS
231 NtfsCreateFile(PDEVICE_OBJECT DeviceObject,
232 PIRP Irp)
233 {
234 PDEVICE_EXTENSION DeviceExt;
235 PIO_STACK_LOCATION Stack;
236 PFILE_OBJECT FileObject;
237 ULONG RequestedDisposition;
238 ULONG RequestedOptions;
239 PNTFS_FCB Fcb;
240 // PWSTR FileName;
241 NTSTATUS Status;
242 UNICODE_STRING FullPath;
243
244 DPRINT1("NtfsCreateFile(%p, %p) called\n", DeviceObject, Irp);
245
246 DeviceExt = DeviceObject->DeviceExtension;
247 ASSERT(DeviceExt);
248 Stack = IoGetCurrentIrpStackLocation (Irp);
249 ASSERT(Stack);
250
251 RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
252 RequestedOptions = Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
253 // PagingFileCreate = (Stack->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE;
254 if (RequestedOptions & FILE_DIRECTORY_FILE &&
255 RequestedDisposition == FILE_SUPERSEDE)
256 {
257 return STATUS_INVALID_PARAMETER;
258 }
259
260 FileObject = Stack->FileObject;
261
262 if (RequestedDisposition == FILE_CREATE ||
263 RequestedDisposition == FILE_OVERWRITE_IF ||
264 RequestedDisposition == FILE_SUPERSEDE)
265 {
266 return STATUS_ACCESS_DENIED;
267 }
268
269 if ((RequestedOptions & FILE_OPEN_BY_FILE_ID) == FILE_OPEN_BY_FILE_ID)
270 {
271 if (FileObject->FileName.Length != sizeof(ULONGLONG))
272 return STATUS_INVALID_PARAMETER;
273
274 Status = NtfsMoonWalkID(DeviceExt, (*(PULONGLONG)FileObject->FileName.Buffer), &FullPath);
275 if (!NT_SUCCESS(Status))
276 {
277 return Status;
278 }
279
280 DPRINT1("Open by ID: %I64x -> %wZ\n", (*(PULONGLONG)FileObject->FileName.Buffer) & NTFS_MFT_MASK, &FullPath);
281 }
282
283 /* This a open operation for the volume itself */
284 if (FileObject->FileName.Length == 0 &&
285 (FileObject->RelatedFileObject == NULL || FileObject->RelatedFileObject->FsContext2 != NULL))
286 {
287 if (RequestedDisposition != FILE_OPEN &&
288 RequestedDisposition != FILE_OPEN_IF)
289 {
290 return STATUS_ACCESS_DENIED;
291 }
292
293 if (RequestedOptions & FILE_DIRECTORY_FILE)
294 {
295 return STATUS_NOT_A_DIRECTORY;
296 }
297
298 NtfsAttachFCBToFileObject(DeviceExt, DeviceExt->VolumeFcb, FileObject);
299 DeviceExt->VolumeFcb->RefCount++;
300
301 Irp->IoStatus.Information = FILE_OPENED;
302 return STATUS_SUCCESS;
303 }
304
305 Status = NtfsOpenFile(DeviceExt,
306 FileObject,
307 ((RequestedOptions & FILE_OPEN_BY_FILE_ID) ? FullPath.Buffer : FileObject->FileName.Buffer),
308 &Fcb);
309
310 if (RequestedOptions & FILE_OPEN_BY_FILE_ID)
311 {
312 ExFreePoolWithTag(FullPath.Buffer, TAG_NTFS);
313 }
314
315 if (NT_SUCCESS(Status))
316 {
317 if (RequestedDisposition == FILE_CREATE)
318 {
319 Irp->IoStatus.Information = FILE_EXISTS;
320 NtfsCloseFile(DeviceExt, FileObject);
321 return STATUS_OBJECT_NAME_COLLISION;
322 }
323
324 if (RequestedOptions & FILE_NON_DIRECTORY_FILE &&
325 NtfsFCBIsDirectory(Fcb))
326 {
327 NtfsCloseFile(DeviceExt, FileObject);
328 return STATUS_FILE_IS_A_DIRECTORY;
329 }
330
331 if (RequestedOptions & FILE_DIRECTORY_FILE &&
332 !NtfsFCBIsDirectory(Fcb))
333 {
334 NtfsCloseFile(DeviceExt, FileObject);
335 return STATUS_NOT_A_DIRECTORY;
336 }
337
338 /*
339 * If it is a reparse point & FILE_OPEN_REPARSE_POINT, then allow opening it
340 * as a normal file.
341 * Otherwise, attempt to read reparse data and hand them to the Io manager
342 * with status reparse to force a reparse.
343 */
344 if (NtfsFCBIsReparsePoint(Fcb) &&
345 ((RequestedOptions & FILE_OPEN_REPARSE_POINT) != FILE_OPEN_REPARSE_POINT))
346 {
347 PREPARSE_DATA_BUFFER ReparseData = NULL;
348
349 Status = NtfsReadFCBAttribute(DeviceExt, Fcb,
350 AttributeReparsePoint, L"", 0,
351 (PVOID *)&Irp->Tail.Overlay.AuxiliaryBuffer);
352 if (NT_SUCCESS(Status))
353 {
354 ReparseData = (PREPARSE_DATA_BUFFER)Irp->Tail.Overlay.AuxiliaryBuffer;
355 if (ReparseData->ReparseTag == IO_REPARSE_TAG_MOUNT_POINT)
356 {
357 Status = STATUS_REPARSE;
358 }
359 else
360 {
361 Status = STATUS_NOT_IMPLEMENTED;
362 ExFreePoolWithTag(ReparseData, TAG_NTFS);
363 }
364 }
365
366 Irp->IoStatus.Information = ((Status == STATUS_REPARSE) ? ReparseData->ReparseTag : 0);
367
368 NtfsCloseFile(DeviceExt, FileObject);
369 return Status;
370 }
371
372 /* HUGLY HACK: remain RO so far... */
373 if (RequestedDisposition == FILE_OVERWRITE ||
374 RequestedDisposition == FILE_OVERWRITE_IF ||
375 RequestedDisposition == FILE_SUPERSEDE)
376 {
377 DPRINT1("Denying write request on NTFS volume\n");
378 NtfsCloseFile(DeviceExt, FileObject);
379 return STATUS_ACCESS_DENIED;
380 }
381 }
382 else
383 {
384 /* HUGLY HACK: remain RO so far... */
385 if (RequestedDisposition == FILE_CREATE ||
386 RequestedDisposition == FILE_OPEN_IF ||
387 RequestedDisposition == FILE_OVERWRITE_IF ||
388 RequestedDisposition == FILE_SUPERSEDE)
389 {
390 DPRINT1("Denying write request on NTFS volume\n");
391 return STATUS_ACCESS_DENIED;
392 }
393 }
394
395 /*
396 * If the directory containing the file to open doesn't exist then
397 * fail immediately
398 */
399 Irp->IoStatus.Information = (NT_SUCCESS(Status)) ? FILE_OPENED : 0;
400 Irp->IoStatus.Status = Status;
401
402 return Status;
403 }
404
405
406 NTSTATUS
407 NTAPI
408 NtfsFsdCreate(PDEVICE_OBJECT DeviceObject,
409 PIRP Irp)
410 {
411 PDEVICE_EXTENSION DeviceExt;
412 NTSTATUS Status;
413
414 if (DeviceObject == NtfsGlobalData->DeviceObject)
415 {
416 /* DeviceObject represents FileSystem instead of logical volume */
417 DPRINT("Opening file system\n");
418 Irp->IoStatus.Information = FILE_OPENED;
419 Status = STATUS_SUCCESS;
420 goto ByeBye;
421 }
422
423 DeviceExt = DeviceObject->DeviceExtension;
424
425 FsRtlEnterFileSystem();
426 ExAcquireResourceExclusiveLite(&DeviceExt->DirResource,
427 TRUE);
428 Status = NtfsCreateFile(DeviceObject,
429 Irp);
430 ExReleaseResourceLite(&DeviceExt->DirResource);
431 FsRtlExitFileSystem();
432
433 ByeBye:
434 Irp->IoStatus.Status = Status;
435 IoCompleteRequest(Irp,
436 NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
437
438 return Status;
439 }
440
441 /* EOF */