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