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