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