[fastfat_new]
[reactos.git] / reactos / drivers / filesystems / fastfat_new / create.c
1 /*
2 * PROJECT: ReactOS FAT file system driver
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: drivers/filesystems/fastfat/create.c
5 * PURPOSE: Create routines
6 * PROGRAMMERS: Aleksey Bragin (aleksey@reactos.org)
7 */
8
9 /* INCLUDES *****************************************************************/
10
11 #define NDEBUG
12 #include "fastfat.h"
13
14 NTSYSAPI
15 NTSTATUS
16 NTAPI
17 RtlUpcaseUnicodeStringToCountedOemString(
18 IN OUT POEM_STRING DestinationString,
19 IN PCUNICODE_STRING SourceString,
20 IN BOOLEAN AllocateDestinationString
21 );
22
23
24 /* FUNCTIONS *****************************************************************/
25
26 IO_STATUS_BLOCK
27 NTAPI
28 FatiOpenRootDcb(IN PFAT_IRP_CONTEXT IrpContext,
29 IN PFILE_OBJECT FileObject,
30 IN PVCB Vcb,
31 IN PACCESS_MASK DesiredAccess,
32 IN USHORT ShareAccess,
33 IN ULONG CreateDisposition)
34 {
35 IO_STATUS_BLOCK Iosb;
36
37 DPRINT1("Opening root directory\n");
38
39 Iosb.Status = STATUS_NOT_IMPLEMENTED;
40
41 return Iosb;
42 }
43
44 NTSTATUS
45 NTAPI
46 FatiCreate(IN PFAT_IRP_CONTEXT IrpContext,
47 IN PIRP Irp)
48 {
49 /* Boolean options */
50 BOOLEAN CreateDirectory;
51 BOOLEAN SequentialOnly;
52 BOOLEAN NoIntermediateBuffering;
53 BOOLEAN OpenDirectory;
54 BOOLEAN IsPagingFile;
55 BOOLEAN OpenTargetDirectory;
56 BOOLEAN DirectoryFile;
57 BOOLEAN NonDirectoryFile;
58 BOOLEAN NoEaKnowledge;
59 BOOLEAN DeleteOnClose;
60 BOOLEAN TemporaryFile;
61 ULONG CreateDisposition;
62
63 /* Control blocks */
64 PVCB Vcb, DecodedVcb;
65 PFCB Fcb, NextFcb;
66 PCCB Ccb;
67 PFCB ParentDcb;
68
69 /* IRP data */
70 PFILE_OBJECT FileObject;
71 PFILE_OBJECT RelatedFO;
72 UNICODE_STRING FileName;
73 ULONG AllocationSize;
74 PFILE_FULL_EA_INFORMATION EaBuffer;
75 PACCESS_MASK DesiredAccess;
76 ULONG Options;
77 UCHAR FileAttributes;
78 USHORT ShareAccess;
79 ULONG EaLength;
80
81 /* Misc */
82 NTSTATUS Status;
83 IO_STATUS_BLOCK Iosb;
84 PIO_STACK_LOCATION IrpSp;
85 BOOLEAN EndBackslash = FALSE, OpenedAsDos;
86 UNICODE_STRING RemainingPart, FirstName, NextName;
87 OEM_STRING AnsiFirstName;
88
89 Iosb.Status = STATUS_SUCCESS;
90
91 /* Get current IRP stack location */
92 IrpSp = IoGetCurrentIrpStackLocation(Irp);
93
94 DPRINT1("FatCommonCreate\n", 0 );
95 DPRINT1("Irp = %08lx\n", Irp );
96 DPRINT1("\t->Flags = %08lx\n", Irp->Flags );
97 DPRINT1("\t->FileObject = %08lx\n", IrpSp->FileObject );
98 DPRINT1("\t->RelatedFileObject = %08lx\n", IrpSp->FileObject->RelatedFileObject );
99 DPRINT1("\t->FileName = %wZ\n", &IrpSp->FileObject->FileName );
100 DPRINT1("\t->AllocationSize.LowPart = %08lx\n", Irp->Overlay.AllocationSize.LowPart );
101 DPRINT1("\t->AllocationSize.HighPart = %08lx\n", Irp->Overlay.AllocationSize.HighPart );
102 DPRINT1("\t->SystemBuffer = %08lx\n", Irp->AssociatedIrp.SystemBuffer );
103 DPRINT1("\t->DesiredAccess = %08lx\n", IrpSp->Parameters.Create.SecurityContext->DesiredAccess );
104 DPRINT1("\t->Options = %08lx\n", IrpSp->Parameters.Create.Options );
105 DPRINT1("\t->FileAttributes = %04x\n", IrpSp->Parameters.Create.FileAttributes );
106 DPRINT1("\t->ShareAccess = %04x\n", IrpSp->Parameters.Create.ShareAccess );
107 DPRINT1("\t->EaLength = %08lx\n", IrpSp->Parameters.Create.EaLength );
108
109 /* Apply a special hack for Win32, idea taken from FASTFAT reference driver from WDK */
110 if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) &&
111 (IrpSp->FileObject->FileName.Buffer[1] == L'\\') &&
112 (IrpSp->FileObject->FileName.Buffer[0] == L'\\'))
113 {
114 /* Remove a leading slash */
115 IrpSp->FileObject->FileName.Length -= sizeof(WCHAR);
116 RtlMoveMemory(&IrpSp->FileObject->FileName.Buffer[0],
117 &IrpSp->FileObject->FileName.Buffer[1],
118 IrpSp->FileObject->FileName.Length );
119
120 /* Check again: if there are still two leading slashes,
121 exit with an error */
122 if ((IrpSp->FileObject->FileName.Length > sizeof(WCHAR)) &&
123 (IrpSp->FileObject->FileName.Buffer[1] == L'\\') &&
124 (IrpSp->FileObject->FileName.Buffer[0] == L'\\'))
125 {
126 FatCompleteRequest( IrpContext, Irp, STATUS_OBJECT_NAME_INVALID );
127
128 DPRINT1("FatiCreate: STATUS_OBJECT_NAME_INVALID\n");
129 return STATUS_OBJECT_NAME_INVALID;
130 }
131 }
132
133 /* Make sure we have SecurityContext */
134 ASSERT(IrpSp->Parameters.Create.SecurityContext != NULL);
135
136 /* Get necessary data out of IRP */
137 FileObject = IrpSp->FileObject;
138 FileName = FileObject->FileName;
139 RelatedFO = FileObject->RelatedFileObject;
140 AllocationSize = Irp->Overlay.AllocationSize.LowPart;
141 EaBuffer = Irp->AssociatedIrp.SystemBuffer;
142 DesiredAccess = &IrpSp->Parameters.Create.SecurityContext->DesiredAccess;
143 Options = IrpSp->Parameters.Create.Options;
144 FileAttributes = (UCHAR)(IrpSp->Parameters.Create.FileAttributes & ~FILE_ATTRIBUTE_NORMAL);
145 ShareAccess = IrpSp->Parameters.Create.ShareAccess;
146 EaLength = IrpSp->Parameters.Create.EaLength;
147
148 /* Set VPB to related object's VPB if it exists */
149 if (RelatedFO)
150 FileObject->Vpb = RelatedFO->Vpb;
151
152 /* Prepare file attributes mask */
153 FileAttributes &= (FILE_ATTRIBUTE_READONLY |
154 FILE_ATTRIBUTE_HIDDEN |
155 FILE_ATTRIBUTE_SYSTEM |
156 FILE_ATTRIBUTE_ARCHIVE);
157
158 /* Get the volume control object */
159 Vcb = &((PVOLUME_DEVICE_OBJECT)IrpSp->DeviceObject)->Vcb;
160
161 /* Get options */
162 DirectoryFile = BooleanFlagOn(Options, FILE_DIRECTORY_FILE);
163 NonDirectoryFile = BooleanFlagOn(Options, FILE_NON_DIRECTORY_FILE);
164 SequentialOnly = BooleanFlagOn(Options, FILE_SEQUENTIAL_ONLY);
165 NoIntermediateBuffering = BooleanFlagOn(Options, FILE_NO_INTERMEDIATE_BUFFERING);
166 NoEaKnowledge = BooleanFlagOn(Options, FILE_NO_EA_KNOWLEDGE);
167 DeleteOnClose = BooleanFlagOn(Options, FILE_DELETE_ON_CLOSE);
168 TemporaryFile = BooleanFlagOn(IrpSp->Parameters.Create.FileAttributes,
169 FILE_ATTRIBUTE_TEMPORARY );
170 IsPagingFile = BooleanFlagOn(IrpSp->Flags, SL_OPEN_PAGING_FILE);
171 OpenTargetDirectory = BooleanFlagOn(IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY);
172
173 /* Calculate create disposition */
174 CreateDisposition = (Options >> 24) & 0x000000ff;
175
176 /* Get Create/Open directory flags based on it */
177 CreateDirectory = (BOOLEAN)(DirectoryFile &&
178 ((CreateDisposition == FILE_CREATE) ||
179 (CreateDisposition == FILE_OPEN_IF)));
180
181 OpenDirectory = (BOOLEAN)(DirectoryFile &&
182 ((CreateDisposition == FILE_OPEN) ||
183 (CreateDisposition == FILE_OPEN_IF)));
184
185 /* Validate parameters: directory/nondirectory mismatch and
186 AllocationSize being more than 4GB */
187 if ((DirectoryFile && NonDirectoryFile) ||
188 Irp->Overlay.AllocationSize.HighPart != 0)
189 {
190 FatCompleteRequest(IrpContext, Irp, STATUS_INVALID_PARAMETER);
191
192 DPRINT1("FatiCreate: STATUS_INVALID_PARAMETER\n", 0);
193 return STATUS_INVALID_PARAMETER;
194 }
195
196 /* Acquire the VCB lock exclusively */
197 if (!FatAcquireExclusiveVcb(IrpContext, Vcb))
198 {
199 // TODO: Postpone the IRP for later processing
200 ASSERT(FALSE);
201 return STATUS_NOT_IMPLEMENTED;
202 }
203
204 // TODO: Verify the VCB
205
206 /* If VCB is locked, then no file openings are possible */
207 if (Vcb->State & VCB_STATE_FLAG_LOCKED)
208 {
209 DPRINT1("This volume is locked\n");
210 Status = STATUS_ACCESS_DENIED;
211
212 /* Cleanup and return */
213 FatReleaseVcb(IrpContext, Vcb);
214 return Status;
215 }
216
217 // TODO: Check if the volume is write protected and disallow DELETE_ON_CLOSE
218
219 // TODO: Make sure EAs aren't supported on FAT32
220
221 /* Check if it's a volume open request */
222 if (FileName.Length == 0)
223 {
224 /* Test related FO to be sure */
225 if (!RelatedFO ||
226 FatDecodeFileObject(RelatedFO, &DecodedVcb, &Fcb, &Ccb) == UserVolumeOpen)
227 {
228 /* It is indeed a volume open request */
229 DPRINT1("Volume open request, not implemented now!\n");
230 UNIMPLEMENTED;
231 }
232 }
233
234 /* Check if this is a relative open */
235 if (RelatedFO)
236 {
237 // RelatedFO will be a parent directory
238 UNIMPLEMENTED;
239 }
240 else
241 {
242 /* Absolute open */
243 if ((FileName.Length == sizeof(WCHAR)) &&
244 (FileName.Buffer[0] == L'\\'))
245 {
246 /* Check if it's ok to open it */
247 if (NonDirectoryFile)
248 {
249 DPRINT1("Trying to open root dir as a file\n");
250
251 /* Cleanup and return */
252 FatReleaseVcb(IrpContext, Vcb);
253 return STATUS_FILE_IS_A_DIRECTORY;
254 }
255
256 /* Check delete on close on a root dir */
257 if (DeleteOnClose)
258 {
259 /* Cleanup and return */
260 FatReleaseVcb(IrpContext, Vcb);
261 return STATUS_CANNOT_DELETE;
262 }
263
264 /* Call root directory open routine */
265 Iosb = FatiOpenRootDcb(IrpContext,
266 FileObject,
267 Vcb,
268 DesiredAccess,
269 ShareAccess,
270 CreateDisposition);
271
272 Irp->IoStatus.Information = Iosb.Information;
273
274 /* Cleanup and return */
275 FatReleaseVcb(IrpContext, Vcb);
276 return Iosb.Status;
277 }
278 else
279 {
280 /* Not a root dir */
281 ParentDcb = Vcb->RootDcb;
282 DPRINT1("ParentDcb %p\n", ParentDcb);
283 }
284
285 /* Check for backslash at the end */
286 if (FileName.Length &&
287 FileName.Buffer[FileName.Length / sizeof(WCHAR) - 1] == L'\\')
288 {
289 /* Cut it out */
290 FileName.Length -= sizeof(WCHAR);
291
292 /* Remember we cut it */
293 EndBackslash = TRUE;
294 }
295
296 /* Ensure the name is set */
297 if (!ParentDcb->FullFileName.Buffer)
298 {
299 DPRINT1("ParentDcb->FullFileName.Buffer is NULL\n");
300 }
301
302 /* Check max path length */
303 if (ParentDcb->FullFileName.Length + FileName.Length + sizeof(WCHAR) <= FileName.Length)
304 {
305 DPRINT1("Max length is way off\n");
306 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
307 ASSERT(FALSE);
308 }
309
310 /* Loop through FCBs to find a good one */
311 while (TRUE)
312 {
313 Fcb = ParentDcb;
314
315 /* Dissect the name */
316 RemainingPart = FileName;
317 while (RemainingPart.Length)
318 {
319 FsRtlDissectName(RemainingPart, &FirstName, &NextName);
320
321 /* Check for validity */
322 if ((NextName.Length && NextName.Buffer[0] == L'\\') ||
323 (NextName.Length > 255 * sizeof(WCHAR)))
324 {
325 /* The name is invalid */
326 DPRINT1("Invalid name found\n");
327 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
328 ASSERT(FALSE);
329 }
330
331 /* Convert the name to ANSI */
332 AnsiFirstName.Buffer = ExAllocatePool(PagedPool, FirstName.Length);
333 AnsiFirstName.Length = 0;
334 AnsiFirstName.MaximumLength = FirstName.Length;
335 Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName, &FirstName, FALSE);
336
337 if (!NT_SUCCESS(Status))
338 {
339 DPRINT1("RtlUpcaseUnicodeStringToCountedOemString() failed with 0x%08x\n", Status);
340 ASSERT(FALSE);
341 NextFcb = NULL;
342 AnsiFirstName.Length = 0;
343 }
344 else
345 {
346 /* Find the coresponding FCB */
347 NextFcb = FatFindFcb(IrpContext,
348 &Fcb->Dcb.SplayLinksAnsi,
349 (PSTRING)&AnsiFirstName,
350 &OpenedAsDos);
351 }
352
353 /* Check if we found anything */
354 if (!NextFcb && Fcb->Dcb.SplayLinksUnicode)
355 {
356 ASSERT(FALSE);
357 }
358
359 /* Move to the next FCB */
360 if (NextFcb)
361 {
362 Fcb = NextFcb;
363 RemainingPart = NextName;
364 }
365
366 /* Break out of this loop if nothing can be found */
367 if (!NextFcb ||
368 NextName.Length == 0 ||
369 FatNodeType(NextFcb) == FAT_NTC_FCB)
370 {
371 break;
372 }
373 }
374
375 /* Ensure remaining name doesn't start from a backslash */
376 if (RemainingPart.Length &&
377 RemainingPart.Buffer[0] == L'\\')
378 {
379 /* Cut it */
380 RemainingPart.Buffer++;
381 RemainingPart.Length -= sizeof(WCHAR);
382 }
383
384 if (Fcb->Condition == FcbGood)
385 {
386 /* Good FCB, break out of the loop */
387 break;
388 }
389 else
390 {
391 ASSERT(FALSE);
392 }
393 }
394
395 /* We have a valid FCB now */
396 if (!RemainingPart.Length)
397 {
398 DPRINT1("It's possible to open an existing FCB\n");
399 ASSERT(FALSE);
400 }
401
402 /* During parsing we encountered a part which has no attached FCB/DCB.
403 Check that the parent is really DCB and not FCB */
404 if (FatNodeType(Fcb) != FAT_NTC_ROOT_DCB &&
405 FatNodeType(Fcb) != FAT_NTC_DCB)
406 {
407 DPRINT1("Weird FCB node type %x, expected DCB or root DCB\n", FatNodeType(Fcb));
408 ASSERT(FALSE);
409 }
410
411 /* Create additional DCBs for all path items */
412 ParentDcb = Fcb;
413 while (TRUE)
414 {
415 FsRtlDissectName(RemainingPart, &FirstName, &RemainingPart);
416
417 /* Check for validity */
418 if ((RemainingPart.Length && RemainingPart.Buffer[0] == L'\\') ||
419 (NextName.Length > 255 * sizeof(WCHAR)))
420 {
421 /* The name is invalid */
422 DPRINT1("Invalid name found\n");
423 Iosb.Status = STATUS_OBJECT_NAME_INVALID;
424 ASSERT(FALSE);
425 }
426
427 /* Convert the name to ANSI */
428 AnsiFirstName.Buffer = ExAllocatePool(PagedPool, FirstName.Length);
429 AnsiFirstName.Length = 0;
430 AnsiFirstName.MaximumLength = FirstName.Length;
431 Status = RtlUpcaseUnicodeStringToCountedOemString(&AnsiFirstName, &FirstName, FALSE);
432
433 if (!NT_SUCCESS(Status))
434 {
435 ASSERT(FALSE);
436 }
437
438 DPRINT1("FirstName %wZ, RemainingPart %wZ\n", &FirstName, &RemainingPart);
439
440 /* Break if came to the end */
441 if (!RemainingPart.Length) break;
442
443 // TODO: Create a DCB for this entry
444 }
445
446 // Simulate that we opened the file
447 //Iosb.Information = FILE_OPENED;
448 Irp->IoStatus.Information = FILE_OPENED;
449 FileObject->SectionObjectPointer = (PSECTION_OBJECT_POINTERS)0x1;
450 }
451
452 /* Complete the request */
453 FatCompleteRequest(IrpContext, Irp, Iosb.Status);
454
455 return Iosb.Status;
456 }
457
458 NTSTATUS
459 NTAPI
460 FatCreate(PDEVICE_OBJECT DeviceObject, PIRP Irp)
461 {
462 PFAT_IRP_CONTEXT IrpContext;
463 NTSTATUS Status;
464 //PVOLUME_DEVICE_OBJECT VolumeDO = (PVOLUME_DEVICE_OBJECT)DeviceObject;
465
466 DPRINT1("FatCreate()\n");
467
468 /* If it's called with our Disk FS device object - it's always open */
469 // TODO: Add check for CDROM FS device object
470 if (DeviceObject == FatGlobalData.DiskDeviceObject)
471 {
472 /* Complete the request and return success */
473 Irp->IoStatus.Status = STATUS_SUCCESS;
474 Irp->IoStatus.Information = FILE_OPENED;
475
476 IoCompleteRequest(Irp, IO_DISK_INCREMENT);
477
478 return STATUS_SUCCESS;
479 }
480
481 /* Enter FsRtl critical region */
482 FsRtlEnterFileSystem();
483
484 /* Build an irp context */
485 IrpContext = FatBuildIrpContext(Irp, TRUE);
486
487 /* Call internal function */
488 Status = FatiCreate(IrpContext, Irp);
489
490 /* Leave FsRtl critical region */
491 FsRtlExitFileSystem();
492
493 return Status;
494 }
495
496 /* EOF */