3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
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.
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * PROJECT: ReactOS kernel
22 * FILE: services/fs/vfat/create.c
23 * PURPOSE: VFAT Filesystem
24 * PROGRAMMER: Jason Filby (jasonfilby@yahoo.com)
28 /* INCLUDES *****************************************************************/
30 #include <ddk/ntddk.h>
39 /* GLOBALS *******************************************************************/
41 #define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof (FATDirEntry))
43 /* FUNCTIONS *****************************************************************/
45 void vfat8Dot3ToString (PFAT_DIR_ENTRY pEntry, PWSTR pName)
47 int fromIndex, toIndex;
49 fromIndex = toIndex = 0;
50 while (fromIndex < 8 && pEntry->Filename [fromIndex] != ' ')
52 if (pEntry->lCase & VFAT_CASE_LOWER_BASE)
54 pName [toIndex++] = tolower(pEntry->Filename [fromIndex++]);
58 pName [toIndex++] = pEntry->Filename [fromIndex++];
61 if (pEntry->Ext [0] != ' ')
63 pName [toIndex++] = L'.';
65 while (fromIndex < 3 && pEntry->Ext [fromIndex] != ' ')
67 if (pEntry->lCase & VFAT_CASE_LOWER_EXT)
69 pName [toIndex++] = tolower(pEntry->Ext [fromIndex++]);
73 pName [toIndex++] = pEntry->Ext [fromIndex++];
77 pName [toIndex] = L'\0';
80 static void vfat8Dot3ToVolumeLabel (PFAT_DIR_ENTRY pEntry, PWSTR pName)
82 int fromIndex, toIndex;
84 fromIndex = toIndex = 0;
85 while (fromIndex < 8 && pEntry->Filename [fromIndex] != ' ')
87 if (pEntry->lCase & VFAT_CASE_LOWER_BASE)
89 pName [toIndex++] = tolower(pEntry->Filename [fromIndex++]);
93 pName [toIndex++] = pEntry->Filename [fromIndex++];
96 if (pEntry->Ext [0] != ' ')
99 while (fromIndex < 3 && pEntry->Ext [fromIndex] != ' ')
101 if (pEntry->lCase & VFAT_CASE_LOWER_EXT)
103 pName [toIndex++] = tolower(pEntry->Ext [fromIndex++]);
107 pName [toIndex++] = pEntry->Ext [fromIndex++];
111 pName [toIndex] = L'\0';
115 ReadVolumeLabel (PDEVICE_EXTENSION DeviceExt, PVPB Vpb)
117 * FUNCTION: Read the volume label
120 PVOID Context = NULL;
124 LARGE_INTEGER FileOffset;
126 *(Vpb->VolumeLabel) = 0;
127 Vpb->VolumeLabelLength = 0;
129 pFcb = vfatOpenRootFCB (DeviceExt);
131 FileOffset.QuadPart = 0;
132 if (CcMapData(pFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, &Context, (PVOID*)&Entry))
136 if (vfatIsDirEntryVolume(Entry))
138 /* copy volume label */
139 vfat8Dot3ToVolumeLabel (Entry, Vpb->VolumeLabel);
140 Vpb->VolumeLabelLength = wcslen (Vpb->VolumeLabel) * sizeof(WCHAR);
143 if (vfatIsDirEntryEndMarker(Entry))
149 if ((DirIndex % ENTRIES_PER_PAGE) == 0)
151 CcUnpinData(Context);
152 FileOffset.u.LowPart += PAGE_SIZE;
153 if (!CcMapData(pFcb->FileObject, &FileOffset, PAGE_SIZE, TRUE, &Context, (PVOID*)&Entry))
162 CcUnpinData(Context);
165 vfatReleaseFCB (DeviceExt, pFcb);
167 return STATUS_SUCCESS;
171 FindFile (PDEVICE_EXTENSION DeviceExt,
178 * FUNCTION: Find a file
190 PVOID Context = NULL;
194 FATDirEntry fatDirEntry;
196 DPRINT ("FindFile(Parent %x, FileToFind '%S', DirIndex: %d)\n", Parent, FileToFind, pDirIndex ? *pDirIndex : 0);
197 DPRINT ("FindFile: old Pathname %x, old Objectname %x)\n",Fcb->PathName, Fcb->ObjectName);
201 if (wcslen (FileToFind) == 0)
204 TempStr[0] = (WCHAR) '*';
206 FileToFind = (PWSTR)&TempStr;
210 FirstCluster = vfatDirEntryGetFirstCluster(DeviceExt, &Parent->entry);
211 if (DeviceExt->FatInfo.FatType == FAT32)
213 if (FirstCluster == DeviceExt->FatInfo.RootCluster)
218 if (FirstCluster == 1)
226 if (DeviceExt->FatInfo.FatType == FAT32)
227 FirstCluster = DeviceExt->FatInfo.RootCluster;
231 if (FileToFind[0] == 0 || (FileToFind[0] == '\\' && FileToFind[1] == 0)
232 || (FileToFind[0] == '.' && FileToFind[1] == 0))
234 /* it's root : complete essentials fields then return ok */
236 memset (Fcb, 0, sizeof (VFATFCB));
237 memset (Fcb->entry.Filename, ' ', 11);
239 Fcb->PathName[0]='\\';
240 Fcb->ObjectName = &Fcb->PathName[1];
241 Fcb->entry.FileSize = DeviceExt->FatInfo.rootDirectorySectors * DeviceExt->FatInfo.BytesPerSector;
242 Fcb->entry.Attrib = FILE_ATTRIBUTE_DIRECTORY;
243 if (DeviceExt->FatInfo.FatType == FAT32)
245 Fcb->entry.FirstCluster = ((PUSHORT)FirstCluster)[0];
246 Fcb->entry.FirstClusterHigh = ((PUSHORT)FirstCluster)[1];
249 Fcb->entry.FirstCluster = 1;
254 DPRINT("FindFile: new Pathname %S, new Objectname %S)\n",Fcb->PathName, Fcb->ObjectName);
255 return (STATUS_SUCCESS);
260 DPRINT ("Parent->entry.FileSize %x\n", Parent->entry.FileSize);
261 FirstCluster = vfatDirEntryGetFirstCluster (DeviceExt, &Parent->entry);
263 if (pDirIndex && (*pDirIndex))
264 DirIndex = *pDirIndex;
266 if (NULL == wcschr(FileToFind, L'?') && NULL == wcschr(FileToFind, L'*'))
268 /* if there is no '*?' in the search name, than look first for an existing fcb */
269 len = wcslen(Parent->PathName);
270 memcpy(name, Parent->PathName, len * sizeof(WCHAR));
271 if (!vfatFCBIsRoot(Parent))
275 wcscpy(name + len, FileToFind);
276 rcFcb = vfatGrabFCBFromTable(DeviceExt, name);
279 if(rcFcb->startIndex >= DirIndex)
281 wcscpy(Fcb->PathName, name);
282 Fcb->ObjectName = &Fcb->PathName[len];
283 memcpy(&Fcb->entry, &rcFcb->entry, sizeof(FATDirEntry));
286 *pDirIndex = rcFcb->dirIndex;
290 *pDirIndex2 = rcFcb->startIndex;
292 DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d (%d)\n",Fcb->PathName, Fcb->ObjectName, rcFcb->dirIndex, rcFcb->startIndex);
293 vfatReleaseFCB(DeviceExt, rcFcb);
294 return STATUS_SUCCESS;
298 vfatReleaseFCB(DeviceExt, rcFcb);
299 return STATUS_UNSUCCESSFUL;
301 vfatReleaseFCB(DeviceExt, rcFcb);
307 Status = vfatGetNextDirEntry(&Context, &Page, Parent, &DirIndex, name, &fatDirEntry, pDirIndex2);
308 if (Status == STATUS_NO_MORE_ENTRIES)
312 if (vfatIsDirEntryVolume(&fatDirEntry))
317 vfat8Dot3ToString(&fatDirEntry, name2);
318 if (wstrcmpjoki (name, FileToFind) || wstrcmpjoki (name2, FileToFind))
320 if (Parent && Parent->PathName)
322 len = wcslen(Parent->PathName);
324 memcpy(Fcb->PathName, Parent->PathName, len*sizeof(WCHAR));
325 Fcb->ObjectName=&Fcb->PathName[len];
326 if (len != 1 || Fcb->PathName[0] != '\\')
328 Fcb->ObjectName[0] = '\\';
329 Fcb->ObjectName = &Fcb->ObjectName[1];
334 Fcb->ObjectName=Fcb->PathName;
335 Fcb->ObjectName[0]='\\';
336 Fcb->ObjectName=&Fcb->ObjectName[1];
338 memcpy(&Fcb->entry, &fatDirEntry, sizeof(FATDirEntry));
339 wcsncpy(Fcb->ObjectName, *name == 0 ? name2 : name, MAX_PATH);
341 *pDirIndex = DirIndex;
342 DPRINT("FindFile: new Pathname %S, new Objectname %S, DirIndex %d\n",Fcb->PathName, Fcb->ObjectName, DirIndex);
345 CcUnpinData(Context);
347 return STATUS_SUCCESS;
352 *pDirIndex = DirIndex;
355 CcUnpinData(Context);
357 return (STATUS_UNSUCCESSFUL);
361 vfatMakeAbsoluteFilename (PFILE_OBJECT pFileObject,
362 PWSTR pRelativeFileName,
363 PWSTR *pAbsoluteFilename)
369 DPRINT ("try related for %S\n", pRelativeFileName);
370 ccb = pFileObject->FsContext2;
375 /* verify related object is a directory and target name
376 don't start with \. */
377 if (!(fcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY)
378 || (pRelativeFileName[0] == L'\\'))
380 return STATUS_INVALID_PARAMETER;
383 /* construct absolute path name */
384 assert (wcslen (fcb->PathName) + 1 + wcslen (pRelativeFileName) + 1
386 rcName = ExAllocatePool (NonPagedPool, MAX_PATH * sizeof(WCHAR));
389 return STATUS_INSUFFICIENT_RESOURCES;
391 wcscpy (rcName, fcb->PathName);
392 if (!vfatFCBIsRoot(fcb))
393 wcscat (rcName, L"\\");
394 wcscat (rcName, pRelativeFileName);
395 *pAbsoluteFilename = rcName;
397 return STATUS_SUCCESS;
401 VfatOpenFile (PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
404 * FUNCTION: Opens a file
410 PWSTR AbsFileName = NULL;
412 DPRINT ("VfatOpenFile(%08lx, %08lx, %S)\n", DeviceExt, FileObject, FileName);
414 if (FileObject->RelatedFileObject)
416 DPRINT ("Converting relative filename to absolute filename\n");
417 Status = vfatMakeAbsoluteFilename (FileObject->RelatedFileObject,
420 FileName = AbsFileName;
421 if (!NT_SUCCESS(Status))
427 //FIXME: Get cannonical path name (remove .'s, ..'s and extra separators)
429 DPRINT ("PathName to open: %S\n", FileName);
431 /* try first to find an existing FCB in memory */
432 DPRINT ("Checking for existing FCB in memory\n");
433 Fcb = vfatGrabFCBFromTable (DeviceExt, FileName);
436 DPRINT ("No existing FCB found, making a new one if file exists.\n");
437 Status = vfatGetFCBForFile (DeviceExt, &ParentFcb, &Fcb, FileName);
438 if (ParentFcb != NULL)
440 vfatReleaseFCB (DeviceExt, ParentFcb);
442 if (!NT_SUCCESS (Status))
444 DPRINT ("Could not make a new FCB, status: %x\n", Status);
447 ExFreePool (AbsFileName);
452 if (Fcb->Flags & FCB_DELETE_PENDING)
454 vfatReleaseFCB (DeviceExt, Fcb);
456 ExFreePool (AbsFileName);
457 return STATUS_DELETE_PENDING;
459 DPRINT ("Attaching FCB to fileObject\n");
460 Status = vfatAttachFCBToFileObject (DeviceExt, Fcb, FileObject);
463 ExFreePool (AbsFileName);
469 VfatPagingFileCreate(PDEVICE_EXTENSION DeviceExt, PVFATFCB Fcb)
471 ULONG CurrentCluster, NextCluster, i;
474 Fcb->Flags |= FCB_IS_PAGE_FILE;
476 ((Fcb->entry.FileSize + DeviceExt->FatInfo.BytesPerCluster - 1) /
477 DeviceExt->FatInfo.BytesPerCluster);
478 if (Fcb->FatChainSize)
481 ExAllocatePool(NonPagedPool, Fcb->FatChainSize * sizeof(ULONG));
484 if (DeviceExt->FatInfo.FatType == FAT32)
486 CurrentCluster = Fcb->entry.FirstCluster +
487 Fcb->entry.FirstClusterHigh * 65536;
491 CurrentCluster = Fcb->entry.FirstCluster;
495 if (Fcb->FatChainSize)
497 while (CurrentCluster != 0xffffffff)
499 Fcb->FatChain[i] = CurrentCluster;
500 Status = GetNextCluster (DeviceExt, CurrentCluster,
501 &NextCluster, FALSE);
503 CurrentCluster = NextCluster;
509 VfatSupersedeFile(PDEVICE_EXTENSION DeviceExt, PFILE_OBJECT FileObject,
512 ULONG Cluster, NextCluster;
515 Fcb->entry.FileSize = 0;
516 if (DeviceExt->FatInfo.FatType == FAT32)
518 Cluster = Fcb->entry.FirstCluster + Fcb->entry.FirstClusterHigh * 65536;
522 Cluster = Fcb->entry.FirstCluster;
524 Fcb->entry.FirstCluster = 0;
525 Fcb->entry.FirstClusterHigh = 0;
526 VfatUpdateEntry (DeviceExt, FileObject);
527 if (Fcb->RFCB.FileSize.QuadPart > 0)
529 Fcb->RFCB.AllocationSize.QuadPart = 0;
530 Fcb->RFCB.FileSize.QuadPart = 0;
531 Fcb->RFCB.ValidDataLength.QuadPart = 0;
532 /* Notify cache manager about the change in file size if caching is
533 initialized on the file stream */
534 if (FileObject->SectionObjectPointers->SharedCacheMap != NULL)
536 CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->RFCB.AllocationSize);
539 while (Cluster != 0xffffffff && Cluster > 1)
541 Status = GetNextCluster (DeviceExt, Cluster, &NextCluster, FALSE);
542 WriteCluster (DeviceExt, Cluster, 0);
543 Cluster = NextCluster;
548 VfatCreateFile (PDEVICE_OBJECT DeviceObject, PIRP Irp)
550 * FUNCTION: Create or open a file
553 PIO_STACK_LOCATION Stack;
554 PFILE_OBJECT FileObject;
555 NTSTATUS Status = STATUS_SUCCESS;
556 PDEVICE_EXTENSION DeviceExt;
557 ULONG RequestedDisposition, RequestedOptions;
561 BOOLEAN PagingFileCreate = FALSE;
562 LARGE_INTEGER AllocationSize;
564 /* Unpack the various parameters. */
565 Stack = IoGetCurrentIrpStackLocation (Irp);
566 RequestedDisposition = ((Stack->Parameters.Create.Options >> 24) & 0xff);
568 Stack->Parameters.Create.Options & FILE_VALID_OPTION_FLAGS;
569 PagingFileCreate = (Stack->Flags & SL_OPEN_PAGING_FILE) ? TRUE : FALSE;
570 FileObject = Stack->FileObject;
571 DeviceExt = DeviceObject->DeviceExtension;
573 /* Check their validity. */
574 if (RequestedOptions & FILE_DIRECTORY_FILE &&
575 RequestedDisposition == FILE_SUPERSEDE)
577 return(STATUS_INVALID_PARAMETER);
580 /* This a open operation for the volume itself */
581 if (FileObject->FileName.Length == 0 &&
582 FileObject->RelatedFileObject == NULL)
584 if (RequestedDisposition == FILE_CREATE ||
585 RequestedDisposition == FILE_OVERWRITE_IF ||
586 RequestedDisposition == FILE_SUPERSEDE)
588 return(STATUS_ACCESS_DENIED);
590 if (RequestedOptions & FILE_DIRECTORY_FILE)
592 return(STATUS_NOT_A_DIRECTORY);
594 pFcb = DeviceExt->VolumeFcb;
595 pCcb = ExAllocateFromNPagedLookasideList(&VfatGlobalData->CcbLookasideList);
598 return (STATUS_INSUFFICIENT_RESOURCES);
600 memset(pCcb, 0, sizeof(VFATCCB));
601 FileObject->Flags |= FO_FCB_IS_VALID;
602 FileObject->SectionObjectPointers = &pFcb->SectionObjectPointers;
603 FileObject->FsContext = (PVOID) &pFcb->RFCB;
604 FileObject->FsContext2 = pCcb;
606 pCcb->PtrFileObject = FileObject;
607 pFcb->pDevExt = DeviceExt;
610 Irp->IoStatus.Information = FILE_OPENED;
611 return(STATUS_SUCCESS);
615 * Check for illegal characters in the file name
617 c = FileObject->FileName.Buffer;
620 if (*c == L'*' || *c == L'?' || (*c == L'\\' && c[1] == L'\\'))
622 return(STATUS_OBJECT_NAME_INVALID);
627 /* Try opening the file. */
628 Status = VfatOpenFile (DeviceExt, FileObject, FileObject->FileName.Buffer);
631 * If the directory containing the file to open doesn't exist then
634 if (Status == STATUS_OBJECT_PATH_NOT_FOUND ||
635 Status == STATUS_INVALID_PARAMETER ||
636 Status == STATUS_DELETE_PENDING)
642 * If the file open failed then create the required file
644 if (!NT_SUCCESS (Status))
646 if (RequestedDisposition == FILE_CREATE ||
647 RequestedDisposition == FILE_OPEN_IF ||
648 RequestedDisposition == FILE_OVERWRITE_IF ||
649 RequestedDisposition == FILE_SUPERSEDE)
652 Attributes = Stack->Parameters.Create.FileAttributes;
653 Status = VfatAddEntry (DeviceExt, FileObject, RequestedOptions,
654 Attributes & FILE_ATTRIBUTE_VALID_FLAGS);
655 if (NT_SUCCESS (Status))
657 pCcb = FileObject->FsContext2;
659 Irp->IoStatus.Information = FILE_CREATED;
660 VfatSetAllocationSizeInformation(FileObject,
663 &Irp->Overlay.AllocationSize);
664 VfatSetExtendedAttributes(FileObject,
665 Irp->AssociatedIrp.SystemBuffer,
666 Stack->Parameters.Create.EaLength);
667 IoSetShareAccess(0 /*DesiredAccess*/,
668 Stack->Parameters.Create.ShareAccess,
670 &pFcb->FCBShareAccess);
684 /* Otherwise fail if the caller wanted to create a new file */
685 if (RequestedDisposition == FILE_CREATE)
687 Irp->IoStatus.Information = FILE_EXISTS;
688 VfatCloseFile (DeviceExt, FileObject);
689 return(STATUS_OBJECT_NAME_COLLISION);
692 pCcb = FileObject->FsContext2;
696 * Check the file has the requested attributes
698 if (RequestedOptions & FILE_NON_DIRECTORY_FILE &&
699 pFcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY)
701 VfatCloseFile (DeviceExt, FileObject);
702 return(STATUS_FILE_IS_A_DIRECTORY);
704 if (RequestedOptions & FILE_DIRECTORY_FILE &&
705 !(pFcb->entry.Attrib & FILE_ATTRIBUTE_DIRECTORY))
707 VfatCloseFile (DeviceExt, FileObject);
708 return(STATUS_NOT_A_DIRECTORY);
711 if (RequestedDisposition == FILE_OVERWRITE ||
712 RequestedDisposition == FILE_OVERWRITE_IF)
714 AllocationSize.QuadPart = 0;
715 Status = VfatSetAllocationSizeInformation (FileObject,
719 if (!NT_SUCCESS (Status))
721 VfatCloseFile (DeviceExt, FileObject);
727 /* Supersede the file */
728 if (RequestedDisposition == FILE_SUPERSEDE)
730 VfatSupersedeFile(DeviceExt, FileObject, pFcb);
731 Irp->IoStatus.Information = FILE_SUPERSEDED;
735 Irp->IoStatus.Information = FILE_OPENED;
740 * If this create was for a paging file then make sure all the
741 * information needed to manipulate it is locked in memory.
743 if (PagingFileCreate)
745 VfatPagingFileCreate(DeviceExt, pFcb);
748 /* FIXME : test share access */
749 /* FIXME : test write access if requested */
755 NTSTATUS VfatCreate (PVFAT_IRP_CONTEXT IrpContext)
757 * FUNCTION: Create or open a file
764 if (IrpContext->DeviceObject == VfatGlobalData->DeviceObject)
766 /* DeviceObject represents FileSystem instead of logical volume */
767 DPRINT ("FsdCreate called with file system\n");
768 IrpContext->Irp->IoStatus.Information = FILE_OPENED;
769 IrpContext->Irp->IoStatus.Status = STATUS_SUCCESS;
770 IoCompleteRequest (IrpContext->Irp, IO_DISK_INCREMENT);
771 VfatFreeIrpContext(IrpContext);
772 return(STATUS_SUCCESS);
775 if (!(IrpContext->Flags & IRPCONTEXT_CANWAIT))
777 return(VfatQueueRequest (IrpContext));
780 IrpContext->Irp->IoStatus.Information = 0;
781 ExAcquireResourceExclusiveLite (&IrpContext->DeviceExt->DirResource, TRUE);
782 Status = VfatCreateFile (IrpContext->DeviceObject, IrpContext->Irp);
783 ExReleaseResourceLite (&IrpContext->DeviceExt->DirResource);
785 IrpContext->Irp->IoStatus.Status = Status;
786 IoCompleteRequest (IrpContext->Irp,
787 NT_SUCCESS(Status) ? IO_DISK_INCREMENT : IO_NO_INCREMENT);
788 VfatFreeIrpContext(IrpContext);