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