/* * COPYRIGHT: See COPYRIGHT.TXT * PROJECT: Ext2 File System Driver for WinNT/2K/XP * FILE: create.c * PROGRAMMER: Matt Wu * HOMEPAGE: http://www.ext2fsd.com * UPDATE HISTORY: */ /* INCLUDES *****************************************************************/ #include "ext2fs.h" #include /* GLOBALS *****************************************************************/ extern PEXT2_GLOBAL Ext2Global; /* DEFINITIONS *************************************************************/ #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, Ext2IsNameValid) #pragma alloc_text(PAGE, Ext2FollowLink) #pragma alloc_text(PAGE, Ext2IsSpecialSystemFile) #pragma alloc_text(PAGE, Ext2LookupFile) #pragma alloc_text(PAGE, Ext2ScanDir) #pragma alloc_text(PAGE, Ext2CreateFile) #pragma alloc_text(PAGE, Ext2CreateVolume) #pragma alloc_text(PAGE, Ext2Create) #pragma alloc_text(PAGE, Ext2CreateInode) #pragma alloc_text(PAGE, Ext2SupersedeOrOverWriteFile) #endif BOOLEAN Ext2IsNameValid(PUNICODE_STRING FileName) { USHORT i = 0; PUSHORT pName = (PUSHORT) FileName->Buffer; if (FileName == NULL) { return FALSE; } while (i < (FileName->Length / sizeof(WCHAR))) { if (pName[i] == 0) { break; } if (pName[i] == L'|' || pName[i] == L':' || pName[i] == L'/' || pName[i] == L'*' || pName[i] == L'?' || pName[i] == L'\"' || pName[i] == L'<' || pName[i] == L'>' ) { return FALSE; } i++; } return TRUE; } NTSTATUS Ext2FollowLink ( IN PEXT2_IRP_CONTEXT IrpContext, IN PEXT2_VCB Vcb, IN PEXT2_MCB Parent, IN PEXT2_MCB Mcb, IN ULONG Linkdep ) { NTSTATUS Status = STATUS_LINK_FAILED; UNICODE_STRING UniName; OEM_STRING OemName; BOOLEAN bOemBuffer = FALSE; PEXT2_MCB Target = NULL; USHORT i; _SEH2_TRY { RtlZeroMemory(&UniName, sizeof(UNICODE_STRING)); RtlZeroMemory(&OemName, sizeof(OEM_STRING)); /* exit if we jump into a possible symlink forever loop */ if ((Linkdep + 1) > EXT2_MAX_NESTED_LINKS || IoGetRemainingStackSize() < 1024) { _SEH2_LEAVE; } /* read the symlink target path */ if (!Mcb->Inode.i_blocks) { OemName.Buffer = (PUCHAR) (&Mcb->Inode.i_block[0]); OemName.Length = (USHORT)Mcb->Inode.i_size; OemName.MaximumLength = OemName.Length + 1; } else { OemName.Length = (USHORT)Mcb->Inode.i_size; OemName.MaximumLength = OemName.Length + 1; OemName.Buffer = Ext2AllocatePool(PagedPool, OemName.MaximumLength, 'NL2E'); if (OemName.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } bOemBuffer = TRUE; RtlZeroMemory(OemName.Buffer, OemName.MaximumLength); Status = Ext2ReadSymlink( IrpContext, Vcb, Mcb, OemName.Buffer, (ULONG)(Mcb->Inode.i_size), NULL); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } } /* convert Linux slash to Windows backslash */ for (i=0; i < OemName.Length; i++) { if (OemName.Buffer[i] == '/') { OemName.Buffer[i] = '\\'; } } /* convert oem string to unicode string */ UniName.MaximumLength = (USHORT)Ext2OEMToUnicodeSize(Vcb, &OemName); if (UniName.MaximumLength <= 0) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } UniName.MaximumLength += 2; UniName.Buffer = Ext2AllocatePool(PagedPool, UniName.MaximumLength, 'NL2E'); if (UniName.Buffer == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } RtlZeroMemory(UniName.Buffer, UniName.MaximumLength); Status = Ext2OEMToUnicode(Vcb, &UniName, &OemName); if (!NT_SUCCESS(Status)) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } /* search the real target */ Status = Ext2LookupFile( IrpContext, Vcb, &UniName, Parent, &Target, Linkdep ); if (Target == NULL) { Status = STATUS_LINK_FAILED; } if (Target == NULL /* link target doesn't exist */ || Target == Mcb /* symlink points to itself */ || IsMcbSpecialFile(Target) /* target not resolved*/ || IsFileDeleted(Target) /* target deleted */ ) { if (Target) { ASSERT(Target->Refercount > 0); Ext2DerefMcb(Target); } ClearLongFlag(Mcb->Flags, MCB_TYPE_SYMLINK); SetLongFlag(Mcb->Flags, MCB_TYPE_SPECIAL); Mcb->Target = NULL; } else if (IsMcbSymLink(Target)) { ASSERT(Target->Refercount > 0); ASSERT(Target->Target != NULL); Ext2ReferMcb(Target->Target); Mcb->Target = Target->Target; Ext2DerefMcb(Target); ASSERT(!IsMcbSymLink(Target->Target)); SetLongFlag(Mcb->Flags, MCB_TYPE_SYMLINK); ClearLongFlag(Mcb->Flags, MCB_TYPE_SPECIAL); ASSERT(Mcb->Target->Refercount > 0); } else { Mcb->Target = Target; SetLongFlag(Mcb->Flags, MCB_TYPE_SYMLINK); ClearLongFlag(Mcb->Flags, MCB_TYPE_SPECIAL); ASSERT(Mcb->Target->Refercount > 0); } /* add directory flag to file attribute */ if (Mcb->Target && IsMcbDirectory(Mcb->Target)) { Mcb->FileAttr |= FILE_ATTRIBUTE_DIRECTORY; } } _SEH2_FINALLY { if (bOemBuffer) { Ext2FreePool(OemName.Buffer, 'NL2E'); } if (UniName.Buffer) { Ext2FreePool(UniName.Buffer, 'NL2E'); } } _SEH2_END; return Status; } BOOLEAN Ext2IsSpecialSystemFile( IN PUNICODE_STRING FileName, IN BOOLEAN bDirectory ) { PWSTR SpecialFileList[] = { L"pagefile.sys", L"swapfile.sys", L"hiberfil.sys", NULL }; PWSTR SpecialDirList[] = { L"Recycled", L"RECYCLER", L"$RECYCLE.BIN", NULL }; PWSTR entryName; ULONG length; int i; for (i = 0; TRUE; i++) { if (bDirectory) { entryName = SpecialDirList[i]; } else { entryName = SpecialFileList[i]; } if (NULL == entryName) { break; } length = wcslen(entryName) * sizeof(WCHAR); if (FileName->Length == length) { if ( 0 == _wcsnicmp( entryName, FileName->Buffer, length / sizeof(WCHAR) )) { return TRUE; } } } return FALSE; } NTSTATUS Ext2LookupFile ( IN PEXT2_IRP_CONTEXT IrpContext, IN PEXT2_VCB Vcb, IN PUNICODE_STRING FullName, IN PEXT2_MCB Parent, OUT PEXT2_MCB * Ext2Mcb, IN ULONG Linkdep ) { NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND; UNICODE_STRING FileName; PEXT2_MCB Mcb = NULL; struct dentry *de = NULL; USHORT i = 0, End; ULONG Inode; BOOLEAN bParent = FALSE; BOOLEAN bDirectory = FALSE; BOOLEAN LockAcquired = FALSE; BOOLEAN bNotFollow = FALSE; _SEH2_TRY { ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); LockAcquired = TRUE; bNotFollow = IsFlagOn(Linkdep, EXT2_LOOKUP_NOT_FOLLOW); #ifndef __REACTOS__ Linkdep = ClearFlag(Linkdep, EXT2_LOOKUP_FLAG_MASK); #else ClearFlag(Linkdep, EXT2_LOOKUP_FLAG_MASK); #endif *Ext2Mcb = NULL; DEBUG(DL_RES, ("Ext2LookupFile: %wZ\n", FullName)); /* check names and parameters */ if (FullName->Buffer[0] == L'\\') { Parent = Vcb->McbTree; } else if (Parent) { bParent = TRUE; } else { Parent = Vcb->McbTree; } /* make sure the parent is NULL */ if (!IsMcbDirectory(Parent)) { Status = STATUS_NOT_A_DIRECTORY; _SEH2_LEAVE; } /* use symlink's target as parent directory */ if (IsMcbSymLink(Parent)) { Parent = Parent->Target; ASSERT(!IsMcbSymLink(Parent)); if (IsFileDeleted(Parent)) { Status = STATUS_NOT_A_DIRECTORY; _SEH2_LEAVE; } } if (NULL == Parent) { Status = STATUS_NOT_A_DIRECTORY; _SEH2_LEAVE; } /* default is the parent Mcb*/ Ext2ReferMcb(Parent); Mcb = Parent; /* is empty file name or root node */ End = FullName->Length/sizeof(WCHAR); if ( (End == 0) || (End == 1 && FullName->Buffer[0] == L'\\')) { Status = STATUS_SUCCESS; _SEH2_LEAVE; } /* is a directory expected ? */ while (FullName->Buffer[End - 1] == L'\\') { bDirectory = TRUE; End -= 1; } /* loop with every sub name */ while (i < End) { USHORT Start = 0; /* zero the prefix '\' */ while (i < End && FullName->Buffer[i] == L'\\') i++; Start = i; /* zero the suffix '\' */ while (i < End && (FullName->Buffer[i] != L'\\')) i++; if (i > Start) { FileName = *FullName; FileName.Buffer += Start; FileName.Length = (USHORT)((i - Start) * 2); /* make sure the parent is NULL */ if (!IsMcbDirectory(Parent)) { Status = STATUS_NOT_A_DIRECTORY; Ext2DerefMcb(Parent); break; } if (IsMcbSymLink(Parent)) { if (IsFileDeleted(Parent->Target)) { Status = STATUS_NOT_A_DIRECTORY; Ext2DerefMcb(Parent); break; } else { Ext2ReferMcb(Parent->Target); Ext2DerefMcb(Parent); Parent = Parent->Target; } } /* search cached Mcb nodes */ Mcb = Ext2SearchMcbWithoutLock(Parent, &FileName); if (Mcb) { /* derefer the parent Mcb */ Ext2DerefMcb(Parent); Status = STATUS_SUCCESS; Parent = Mcb; if (IsMcbSymLink(Mcb) && IsFileDeleted(Mcb->Target) && Mcb->Refercount == 1) { ASSERT(Mcb->Target); ASSERT(Mcb->Target->Refercount > 0); Ext2DerefMcb(Mcb->Target); Mcb->Target = NULL; ClearLongFlag(Mcb->Flags, MCB_TYPE_SYMLINK); SetLongFlag(Mcb->Flags, MCB_TYPE_SPECIAL); Mcb->FileAttr = FILE_ATTRIBUTE_NORMAL; } } else { /* need create new Mcb node */ /* is a valid ext2 name */ if (!Ext2IsNameValid(&FileName)) { Status = STATUS_OBJECT_NAME_INVALID; Ext2DerefMcb(Parent); break; } /* seach the disk */ de = NULL; Status = Ext2ScanDir ( IrpContext, Vcb, Parent, &FileName, &Inode, &de); if (NT_SUCCESS(Status)) { /* check it's real parent */ ASSERT (!IsMcbSymLink(Parent)); /* allocate Mcb ... */ Mcb = Ext2AllocateMcb(Vcb, &FileName, &Parent->FullName, 0); if (!Mcb) { Status = STATUS_INSUFFICIENT_RESOURCES; Ext2DerefMcb(Parent); break; } Mcb->de = de; Mcb->de->d_inode = &Mcb->Inode; Mcb->Inode.i_ino = Inode; Mcb->Inode.i_sb = &Vcb->sb; de = NULL; /* load inode information */ if (!Ext2LoadInode(Vcb, &Mcb->Inode)) { Status = STATUS_CANT_WAIT; Ext2DerefMcb(Parent); Ext2FreeMcb(Vcb, Mcb); break; } /* set inode attribute */ if (!Ext2CheckFileAccess(Vcb, Mcb, Ext2FileCanWrite)) { SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_READONLY); } if (S_ISDIR(Mcb->Inode.i_mode)) { SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_DIRECTORY); } else { if (S_ISREG(Mcb->Inode.i_mode)) { SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_NORMAL); } else if (S_ISLNK(Mcb->Inode.i_mode)) { SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_REPARSE_POINT); } else { SetLongFlag(Mcb->Flags, MCB_TYPE_SPECIAL); } } /* process special files under root directory */ if (IsMcbRoot(Parent)) { /* set hidden and system attributes for Recycled / RECYCLER / pagefile.sys */ BOOLEAN IsDirectory = IsMcbDirectory(Mcb); if (Ext2IsSpecialSystemFile(&Mcb->ShortName, IsDirectory)) { SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_HIDDEN); SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_SYSTEM); } } Mcb->CreationTime = Ext2NtTime(Mcb->Inode.i_ctime); Mcb->LastAccessTime = Ext2NtTime(Mcb->Inode.i_atime); Mcb->LastWriteTime = Ext2NtTime(Mcb->Inode.i_mtime); Mcb->ChangeTime = Ext2NtTime(Mcb->Inode.i_mtime); /* process symlink */ if (S_ISLNK(Mcb->Inode.i_mode) && !bNotFollow) { Ext2FollowLink( IrpContext, Vcb, Parent, Mcb, Linkdep+1 ); } /* add reference ... */ Ext2ReferMcb(Mcb); /* add Mcb to it's parent tree*/ Ext2InsertMcb(Vcb, Parent, Mcb); /* it's safe to deref Parent Mcb */ Ext2DerefMcb(Parent); /* linking this Mcb*/ Ext2LinkTailMcb(Vcb, Mcb); /* set parent to preare re-scan */ Parent = Mcb; } else { /* derefernce it's parent */ Ext2DerefMcb(Parent); break; } } } else { /* there seems too many \ or / */ /* Mcb should be already set to Parent */ ASSERT(Mcb == Parent); Status = STATUS_SUCCESS; break; } } } _SEH2_FINALLY { if (de) { Ext2FreeEntry(de); } if (NT_SUCCESS(Status)) { if (bDirectory) { if (IsMcbDirectory(Mcb)) { *Ext2Mcb = Mcb; } else { Ext2DerefMcb(Mcb); Status = STATUS_NOT_A_DIRECTORY; } } else { *Ext2Mcb = Mcb; } } if (LockAcquired) { ExReleaseResourceLite(&Vcb->McbLock); } } _SEH2_END; return Status; } NTSTATUS Ext2ScanDir ( IN PEXT2_IRP_CONTEXT IrpContext, IN PEXT2_VCB Vcb, IN PEXT2_MCB Parent, IN PUNICODE_STRING FileName, OUT PULONG Inode, OUT struct dentry **dentry ) { struct ext3_dir_entry_2 *dir_entry = NULL; struct buffer_head *bh = NULL; struct dentry *de = NULL; NTSTATUS Status = STATUS_NO_SUCH_FILE; DEBUG(DL_RES, ("Ext2ScanDir: %wZ\\%wZ\n", &Parent->FullName, FileName)); _SEH2_TRY { /* grab parent's reference first */ Ext2ReferMcb(Parent); /* bad request ! Can a man be pregnant ? Maybe:) */ if (!IsMcbDirectory(Parent)) { Status = STATUS_NOT_A_DIRECTORY; _SEH2_LEAVE; } /* parent is a symlink ? */ if IsMcbSymLink(Parent) { if (Parent->Target) { Ext2ReferMcb(Parent->Target); Ext2DerefMcb(Parent); Parent = Parent->Target; ASSERT(!IsMcbSymLink(Parent)); } else { DbgBreak(); Status = STATUS_NOT_A_DIRECTORY; _SEH2_LEAVE; } } de = Ext2BuildEntry(Vcb, Parent, FileName); if (!de) { DEBUG(DL_ERR, ( "Ex2ScanDir: failed to allocate dentry.\n")); Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } bh = ext3_find_entry(IrpContext, de, &dir_entry); if (dir_entry) { Status = STATUS_SUCCESS; *Inode = dir_entry->inode; *dentry = de; } } _SEH2_FINALLY { Ext2DerefMcb(Parent); if (bh) __brelse(bh); if (!NT_SUCCESS(Status)) { if (de) Ext2FreeEntry(de); } } _SEH2_END; return Status; } NTSTATUS Ext2AddDotEntries(struct ext2_icb *icb, struct inode *dir, struct inode *inode) { struct ext3_dir_entry_2 * de; struct buffer_head * bh; ext3_lblk_t block = 0; int rc = 0; bh = ext3_append(icb, inode, &block, &rc); if (!bh) { goto errorout; } de = (struct ext3_dir_entry_2 *) bh->b_data; de->inode = cpu_to_le32(inode->i_ino); de->name_len = 1; de->rec_len = cpu_to_le16(EXT3_DIR_REC_LEN(de->name_len)); strcpy (de->name, "."); ext3_set_de_type(inode->i_sb, de, S_IFDIR); de = (struct ext3_dir_entry_2 *) ((char *) de + le16_to_cpu(de->rec_len)); de->inode = cpu_to_le32(dir->i_ino); de->rec_len = cpu_to_le16(inode->i_sb->s_blocksize-EXT3_DIR_REC_LEN(1)); de->name_len = 2; strcpy (de->name, ".."); ext3_set_de_type(inode->i_sb, de, S_IFDIR); inode->i_nlink = 2; set_buffer_dirty(bh); ext3_mark_inode_dirty(icb, inode); errorout: if (bh) __brelse (bh); return Ext2WinntError(rc); } // // Any call to this routine must have Fcb's MainResource and FcbLock acquired. // NTSTATUS Ext2OverwriteEa( PEXT2_IRP_CONTEXT IrpContext, PEXT2_VCB Vcb, PEXT2_FCB Fcb, PIO_STATUS_BLOCK Iosb ) { PEXT2_MCB Mcb = NULL; PIRP Irp; PIO_STACK_LOCATION IrpSp; struct ext4_xattr_ref xattr_ref; BOOLEAN XattrRefAcquired = FALSE; NTSTATUS Status = STATUS_UNSUCCESSFUL; PFILE_FULL_EA_INFORMATION FullEa; PCHAR EaBuffer; ULONG EaBufferLength; _SEH2_TRY { Irp = IrpContext->Irp; IrpSp = IoGetCurrentIrpStackLocation(Irp); Mcb = Fcb->Mcb; EaBuffer = Irp->AssociatedIrp.SystemBuffer; EaBufferLength = IrpSp->Parameters.Create.EaLength; if (!Mcb) _SEH2_LEAVE; // // Return peacefully if there is no EaBuffer provided. // if (!EaBuffer) { Status = STATUS_SUCCESS; _SEH2_LEAVE; } // // If the caller specifies an EaBuffer, but has no knowledge about Ea, // we reject the request. // if (EaBuffer != NULL && FlagOn(IrpSp->Parameters.Create.Options, FILE_NO_EA_KNOWLEDGE)) { Status = STATUS_ACCESS_DENIED; _SEH2_LEAVE; } // // Check Ea Buffer validity. // Status = IoCheckEaBufferValidity((PFILE_FULL_EA_INFORMATION)EaBuffer, EaBufferLength, (PULONG)&Iosb->Information); if (!NT_SUCCESS(Status)) _SEH2_LEAVE; Status = Ext2WinntError(ext4_fs_get_xattr_ref(IrpContext, Vcb, Fcb->Mcb, &xattr_ref)); if (!NT_SUCCESS(Status)) { DbgPrint("ext4_fs_get_xattr_ref() failed!\n"); _SEH2_LEAVE; } XattrRefAcquired = TRUE; // // Remove all existing EA entries. // ext4_xattr_purge_items(&xattr_ref); xattr_ref.dirty = TRUE; Status = STATUS_SUCCESS; // Iterate the whole EA buffer to do inspection for (FullEa = (PFILE_FULL_EA_INFORMATION)EaBuffer; FullEa < (PFILE_FULL_EA_INFORMATION)&EaBuffer[EaBufferLength]; FullEa = (PFILE_FULL_EA_INFORMATION)(FullEa->NextEntryOffset == 0 ? &EaBuffer[EaBufferLength] : (PCHAR)FullEa + FullEa->NextEntryOffset)) { OEM_STRING EaName; EaName.MaximumLength = EaName.Length = FullEa->EaNameLength; EaName.Buffer = &FullEa->EaName[0]; // Check if EA's name is valid if (!Ext2IsEaNameValid(EaName)) { Status = STATUS_INVALID_EA_NAME; _SEH2_LEAVE; } } // Now add EA entries to the inode for (FullEa = (PFILE_FULL_EA_INFORMATION)EaBuffer; FullEa < (PFILE_FULL_EA_INFORMATION)&EaBuffer[EaBufferLength]; FullEa = (PFILE_FULL_EA_INFORMATION)(FullEa->NextEntryOffset == 0 ? &EaBuffer[EaBufferLength] : (PCHAR)FullEa + FullEa->NextEntryOffset)) { int ret; OEM_STRING EaName; EaName.MaximumLength = EaName.Length = FullEa->EaNameLength; EaName.Buffer = &FullEa->EaName[0]; Status = Ext2WinntError(ret = ext4_fs_set_xattr(&xattr_ref, EXT4_XATTR_INDEX_USER, EaName.Buffer, EaName.Length, &FullEa->EaName[0] + FullEa->EaNameLength + 1, FullEa->EaValueLength, TRUE)); if (!NT_SUCCESS(Status) && ret != -ENODATA) _SEH2_LEAVE; if (ret == -ENODATA) { Status = Ext2WinntError( ext4_fs_set_xattr(&xattr_ref, EXT4_XATTR_INDEX_USER, EaName.Buffer, EaName.Length, &FullEa->EaName[0] + FullEa->EaNameLength + 1, FullEa->EaValueLength, FALSE)); if (!NT_SUCCESS(Status)) _SEH2_LEAVE; } } } _SEH2_FINALLY { if (XattrRefAcquired) { if (!NT_SUCCESS(Status)) { xattr_ref.dirty = FALSE; ext4_fs_put_xattr_ref(&xattr_ref); } else { Status = Ext2WinntError(ext4_fs_put_xattr_ref(&xattr_ref)); } } } _SEH2_END; return Status; } NTSTATUS Ext2CreateFile( PEXT2_IRP_CONTEXT IrpContext, PEXT2_VCB Vcb, PBOOLEAN OpPostIrp ) { NTSTATUS Status = STATUS_UNSUCCESSFUL; PIO_STACK_LOCATION IrpSp; PEXT2_FCB Fcb = NULL; PEXT2_MCB Mcb = NULL; PEXT2_MCB SymLink = NULL; PEXT2_CCB Ccb = NULL; PEXT2_FCB ParentFcb = NULL; PEXT2_MCB ParentMcb = NULL; UNICODE_STRING FileName; PIRP Irp; ULONG Options; ULONG CreateDisposition; BOOLEAN bParentFcbCreated = FALSE; #ifndef __REACTOS__ BOOLEAN bDir = FALSE; #endif BOOLEAN bFcbAllocated = FALSE; BOOLEAN bCreated = FALSE; BOOLEAN bMainResourceAcquired = FALSE; BOOLEAN bFcbLockAcquired = FALSE; BOOLEAN OpenDirectory; BOOLEAN OpenTargetDirectory; BOOLEAN CreateDirectory; BOOLEAN SequentialOnly; BOOLEAN NoIntermediateBuffering; BOOLEAN IsPagingFile; BOOLEAN DirectoryFile; BOOLEAN NonDirectoryFile; BOOLEAN NoEaKnowledge; BOOLEAN DeleteOnClose; BOOLEAN TemporaryFile; BOOLEAN CaseSensitive; BOOLEAN OpenReparsePoint; ACCESS_MASK DesiredAccess; ULONG ShareAccess; ULONG CcbFlags = 0; RtlZeroMemory(&FileName, sizeof(UNICODE_STRING)); Irp = IrpContext->Irp; IrpSp = IoGetCurrentIrpStackLocation(Irp); Options = IrpSp->Parameters.Create.Options; DirectoryFile = IsFlagOn(Options, FILE_DIRECTORY_FILE); OpenTargetDirectory = IsFlagOn(IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY); NonDirectoryFile = IsFlagOn(Options, FILE_NON_DIRECTORY_FILE); SequentialOnly = IsFlagOn(Options, FILE_SEQUENTIAL_ONLY); NoIntermediateBuffering = IsFlagOn( Options, FILE_NO_INTERMEDIATE_BUFFERING ); NoEaKnowledge = IsFlagOn(Options, FILE_NO_EA_KNOWLEDGE); DeleteOnClose = IsFlagOn(Options, FILE_DELETE_ON_CLOSE); /* Try to open reparse point (symlink) itself ? */ OpenReparsePoint = IsFlagOn(Options, FILE_OPEN_REPARSE_POINT); CaseSensitive = IsFlagOn(IrpSp->Flags, SL_CASE_SENSITIVE); TemporaryFile = IsFlagOn(IrpSp->Parameters.Create.FileAttributes, FILE_ATTRIBUTE_TEMPORARY ); CreateDisposition = (Options >> 24) & 0x000000ff; IsPagingFile = IsFlagOn(IrpSp->Flags, SL_OPEN_PAGING_FILE); CreateDirectory = (BOOLEAN)(DirectoryFile && ((CreateDisposition == FILE_CREATE) || (CreateDisposition == FILE_OPEN_IF))); OpenDirectory = (BOOLEAN)(DirectoryFile && ((CreateDisposition == FILE_OPEN) || (CreateDisposition == FILE_OPEN_IF))); DesiredAccess = IrpSp->Parameters.Create.SecurityContext->DesiredAccess; ShareAccess = IrpSp->Parameters.Create.ShareAccess; *OpPostIrp = FALSE; _SEH2_TRY { FileName.MaximumLength = IrpSp->FileObject->FileName.MaximumLength; FileName.Length = IrpSp->FileObject->FileName.Length; if (IrpSp->FileObject->RelatedFileObject) { ParentFcb = (PEXT2_FCB)(IrpSp->FileObject->RelatedFileObject->FsContext); } if (ParentFcb) { ParentMcb = ParentFcb->Mcb; Ext2ReferMcb(ParentMcb); ParentFcb = NULL; } if (FileName.Length == 0) { if (ParentMcb) { Mcb = ParentMcb; Ext2ReferMcb(Mcb); Status = STATUS_SUCCESS; goto McbExisting; } else { DbgBreak(); Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } } FileName.Buffer = Ext2AllocatePool( PagedPool, FileName.MaximumLength, EXT2_FNAME_MAGIC ); if (!FileName.Buffer) { DEBUG(DL_ERR, ( "Ex2CreateFile: failed to allocate FileName.\n")); Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } INC_MEM_COUNT(PS_FILE_NAME, FileName.Buffer, FileName.MaximumLength); RtlZeroMemory(FileName.Buffer, FileName.MaximumLength); RtlCopyMemory(FileName.Buffer, IrpSp->FileObject->FileName.Buffer, FileName.Length); if (IrpSp->FileObject->RelatedFileObject && FileName.Buffer[0] == L'\\') { Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } if ((FileName.Length > sizeof(WCHAR)) && (FileName.Buffer[1] == L'\\') && (FileName.Buffer[0] == L'\\')) { FileName.Length -= sizeof(WCHAR); RtlMoveMemory( &FileName.Buffer[0], &FileName.Buffer[1], FileName.Length ); // // Bad Name if there are still beginning backslashes. // if ((FileName.Length > sizeof(WCHAR)) && (FileName.Buffer[1] == L'\\') && (FileName.Buffer[0] == L'\\')) { Status = STATUS_OBJECT_NAME_INVALID; _SEH2_LEAVE; } } if (IsFlagOn(Options, FILE_OPEN_BY_FILE_ID)) { Status = STATUS_NOT_IMPLEMENTED; _SEH2_LEAVE; } DEBUG(DL_INF, ( "Ext2CreateFile: %wZ Paging=%d Option: %xh:" "Dir=%d NonDir=%d OpenTarget=%d NC=%d DeleteOnClose=%d\n", &FileName, IsPagingFile, IrpSp->Parameters.Create.Options, DirectoryFile, NonDirectoryFile, OpenTargetDirectory, NoIntermediateBuffering, DeleteOnClose )); DEBUG(DL_RES, ("Ext2CreateFile: Lookup 1st: %wZ at %S\n", &FileName, ParentMcb ? ParentMcb->FullName.Buffer : L" ")); Status = Ext2LookupFile( IrpContext, Vcb, &FileName, ParentMcb, &Mcb, 0 /* always follow link */ ); McbExisting: if (!NT_SUCCESS(Status)) { UNICODE_STRING PathName; UNICODE_STRING RealName; UNICODE_STRING RemainName; #ifndef __REACTOS__ LONG i = 0; #endif PathName = FileName; Mcb = NULL; /* here we've found the target file, but it's not matched. */ if (STATUS_OBJECT_NAME_NOT_FOUND != Status && STATUS_NO_SUCH_FILE != Status) { _SEH2_LEAVE; } while (PathName.Length > 0 && PathName.Buffer[PathName.Length/2 - 1] == L'\\') { DirectoryFile = TRUE; PathName.Length -= 2; PathName.Buffer[PathName.Length / 2] = 0; } if (!ParentMcb) { if (PathName.Buffer[0] != L'\\') { Status = STATUS_OBJECT_PATH_NOT_FOUND; _SEH2_LEAVE; } else { ParentMcb = Vcb->McbTree; Ext2ReferMcb(ParentMcb); } } Dissecting: FsRtlDissectName(PathName, &RealName, &RemainName); if (((RemainName.Length != 0) && (RemainName.Buffer[0] == L'\\')) || (RealName.Length >= 256 * sizeof(WCHAR))) { Status = STATUS_OBJECT_NAME_INVALID; _SEH2_LEAVE; } if (RemainName.Length != 0) { PEXT2_MCB RetMcb = NULL; DEBUG(DL_RES, ("Ext2CreateFile: Lookup 2nd: %wZ\\%wZ\n", &ParentMcb->FullName, &RealName)); Status = Ext2LookupFile ( IrpContext, Vcb, &RealName, ParentMcb, &RetMcb, 0); /* quit name resolving loop */ if (!NT_SUCCESS(Status)) { if (Status == STATUS_NO_SUCH_FILE || Status == STATUS_OBJECT_NAME_NOT_FOUND) { Status = STATUS_OBJECT_PATH_NOT_FOUND; } _SEH2_LEAVE; } /* deref ParentMcb */ Ext2DerefMcb(ParentMcb); /* RetMcb is already refered */ ParentMcb = RetMcb; PathName = RemainName; /* symlink must use it's target */ if (IsMcbSymLink(ParentMcb)) { Ext2ReferMcb(ParentMcb->Target); Ext2DerefMcb(ParentMcb); ParentMcb = ParentMcb->Target; ASSERT(!IsMcbSymLink(ParentMcb)); } goto Dissecting; } /* is name valid */ if ( FsRtlDoesNameContainWildCards(&RealName) || !Ext2IsNameValid(&RealName)) { Status = STATUS_OBJECT_NAME_INVALID; _SEH2_LEAVE; } if (!bFcbLockAcquired) { ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); bFcbLockAcquired = TRUE; } /* get the ParentFcb, allocate it if needed ... */ ParentFcb = ParentMcb->Fcb; if (!ParentFcb) { ParentFcb = Ext2AllocateFcb(Vcb, ParentMcb); if (!ParentFcb) { Status = STATUS_INSUFFICIENT_RESOURCES; _SEH2_LEAVE; } bParentFcbCreated = TRUE; } Ext2ReferXcb(&ParentFcb->ReferenceCount); if (bFcbLockAcquired) { ExReleaseResourceLite(&Vcb->FcbLock); bFcbLockAcquired = FALSE; } // We need to create a new one ? if ((CreateDisposition == FILE_CREATE ) || (CreateDisposition == FILE_SUPERSEDE) || (CreateDisposition == FILE_OPEN_IF) || (CreateDisposition == FILE_OVERWRITE_IF)) { if (IsVcbReadOnly(Vcb)) { Status = STATUS_MEDIA_WRITE_PROTECTED; _SEH2_LEAVE; } if (!Ext2CheckFileAccess(Vcb, ParentMcb, Ext2FileCanWrite)) { Status = STATUS_ACCESS_DENIED; _SEH2_LEAVE; } if (IsFlagOn(Vcb->Flags, VCB_WRITE_PROTECTED)) { IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice ); SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); Ext2RaiseStatus(IrpContext, STATUS_MEDIA_WRITE_PROTECTED); } if (DirectoryFile) { if (TemporaryFile) { DbgBreak(); Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } } if (!ParentFcb) { Status = STATUS_OBJECT_PATH_NOT_FOUND; _SEH2_LEAVE; } /* allocate inode and construct entry for this file */ Status = Ext2CreateInode( IrpContext, Vcb, ParentFcb, DirectoryFile ? EXT2_FT_DIR : EXT2_FT_REG_FILE, IrpSp->Parameters.Create.FileAttributes, &RealName ); if (!NT_SUCCESS(Status)) { DbgBreak(); _SEH2_LEAVE; } bCreated = TRUE; DEBUG(DL_RES, ("Ext2CreateFile: Confirm creation: %wZ\\%wZ\n", &ParentMcb->FullName, &RealName)); Irp->IoStatus.Information = FILE_CREATED; Status = Ext2LookupFile ( IrpContext, Vcb, &RealName, ParentMcb, &Mcb, 0); if (!NT_SUCCESS(Status)) { DbgBreak(); } } else if (OpenTargetDirectory) { if (IsVcbReadOnly(Vcb)) { Status = STATUS_MEDIA_WRITE_PROTECTED; _SEH2_LEAVE; } if (!ParentFcb) { Status = STATUS_OBJECT_PATH_NOT_FOUND; _SEH2_LEAVE; } RtlZeroMemory( IrpSp->FileObject->FileName.Buffer, IrpSp->FileObject->FileName.MaximumLength); IrpSp->FileObject->FileName.Length = RealName.Length; RtlCopyMemory( IrpSp->FileObject->FileName.Buffer, RealName.Buffer, RealName.Length ); Fcb = ParentFcb; Mcb = Fcb->Mcb; Ext2ReferMcb(Mcb); Irp->IoStatus.Information = FILE_DOES_NOT_EXIST; Status = STATUS_SUCCESS; } else { Status = STATUS_OBJECT_NAME_NOT_FOUND; _SEH2_LEAVE; } } else { // File / Dir already exists. /* here already get Mcb referred */ if (OpenTargetDirectory) { UNICODE_STRING RealName = FileName; USHORT i = 0; while (RealName.Buffer[RealName.Length/2 - 1] == L'\\') { RealName.Length -= sizeof(WCHAR); RealName.Buffer[RealName.Length/2] = 0; } i = RealName.Length/2; while (i > 0 && RealName.Buffer[i - 1] != L'\\') i--; if (IsVcbReadOnly(Vcb)) { Status = STATUS_MEDIA_WRITE_PROTECTED; Ext2DerefMcb(Mcb); _SEH2_LEAVE; } Irp->IoStatus.Information = FILE_EXISTS; Status = STATUS_SUCCESS; RtlZeroMemory( IrpSp->FileObject->FileName.Buffer, IrpSp->FileObject->FileName.MaximumLength); IrpSp->FileObject->FileName.Length = RealName.Length - i * sizeof(WCHAR); RtlCopyMemory( IrpSp->FileObject->FileName.Buffer, &RealName.Buffer[i], IrpSp->FileObject->FileName.Length ); // use's it's parent since it's open-target operation Ext2ReferMcb(Mcb->Parent); Ext2DerefMcb(Mcb); Mcb = Mcb->Parent; goto Openit; } // We can not create if one exists if (CreateDisposition == FILE_CREATE) { Irp->IoStatus.Information = FILE_EXISTS; Status = STATUS_OBJECT_NAME_COLLISION; Ext2DerefMcb(Mcb); _SEH2_LEAVE; } /* directory forbits us to do the followings ... */ if (IsMcbDirectory(Mcb)) { if ((CreateDisposition != FILE_OPEN) && (CreateDisposition != FILE_OPEN_IF)) { Status = STATUS_OBJECT_NAME_COLLISION; Ext2DerefMcb(Mcb); _SEH2_LEAVE; } if (NonDirectoryFile) { Status = STATUS_FILE_IS_A_DIRECTORY; Ext2DerefMcb(Mcb); _SEH2_LEAVE; } if (Mcb->Inode.i_ino == EXT2_ROOT_INO) { if (OpenTargetDirectory) { DbgBreak(); Status = STATUS_INVALID_PARAMETER; Ext2DerefMcb(Mcb); _SEH2_LEAVE; } } } else { if (DirectoryFile) { Status = STATUS_NOT_A_DIRECTORY;; Ext2DerefMcb(Mcb); _SEH2_LEAVE; } } Irp->IoStatus.Information = FILE_OPENED; } Openit: if (!bFcbLockAcquired) { ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); bFcbLockAcquired = TRUE; } /* Mcb should already be referred and symlink is too */ if (Mcb) { ASSERT(Mcb->Refercount > 0); /* refer it's target if it's a symlink, so both refered */ if (IsMcbSymLink(Mcb)) { if (OpenReparsePoint) { /* set Ccb flag */ CcbFlags = CCB_OPEN_REPARSE_POINT; } else if (IsFileDeleted(Mcb->Target)) { DbgBreak(); SetLongFlag(Mcb->Flags, MCB_TYPE_SPECIAL); ClearLongFlag(Mcb->Flags, MCB_TYPE_SYMLINK); Ext2DerefMcb(Mcb->Target); Mcb->Target = NULL; } else { SymLink = Mcb; Mcb = Mcb->Target; Ext2ReferMcb(Mcb); ASSERT (!IsMcbSymLink(Mcb)); } } // Check readonly flag if (BooleanFlagOn(DesiredAccess, FILE_GENERIC_READ) && !Ext2CheckFileAccess(Vcb, Mcb, Ext2FileCanRead)) { Status = STATUS_ACCESS_DENIED; _SEH2_LEAVE; } if (!Ext2CheckFileAccess(Vcb, Mcb, Ext2FileCanWrite)) { if (BooleanFlagOn(DesiredAccess, FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_ADD_SUBDIRECTORY | FILE_DELETE_CHILD)) { Status = STATUS_ACCESS_DENIED; _SEH2_LEAVE; } else if (IsFlagOn(Options, FILE_DELETE_ON_CLOSE )) { Status = STATUS_CANNOT_DELETE; _SEH2_LEAVE; } } Fcb = Mcb->Fcb; if (Fcb == NULL) { /* allocate Fcb for this file */ Fcb = Ext2AllocateFcb (Vcb, Mcb); if (Fcb) { bFcbAllocated = TRUE; } else { Status = STATUS_INSUFFICIENT_RESOURCES; } } else { if (IsPagingFile) { Status = STATUS_SHARING_VIOLATION; Fcb = NULL; } } /* Now it's safe to defer Mcb */ Ext2DerefMcb(Mcb); } if (Fcb) { /* grab Fcb's reference first to avoid the race between Ext2Close (it could free the Fcb we are accessing) */ Ext2ReferXcb(&Fcb->ReferenceCount); } ExReleaseResourceLite(&Vcb->FcbLock); bFcbLockAcquired = FALSE; if (Fcb) { ExAcquireResourceExclusiveLite(&Fcb->MainResource, TRUE); bMainResourceAcquired = TRUE; /* Open target directory ? */ if (NULL == Mcb) { DbgBreak(); Mcb = Fcb->Mcb; } /* check Mcb reference */ ASSERT(Fcb->Mcb->Refercount > 0); /* file delted ? */ if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { Status = STATUS_FILE_DELETED; _SEH2_LEAVE; } if (DeleteOnClose && NULL == SymLink) { Status = Ext2IsFileRemovable(IrpContext, Vcb, Fcb, Ccb); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } } /* check access and oplock access for opened files */ if (!bFcbAllocated && !IsDirectory(Fcb)) { /* whether there's batch oplock grabed on the file */ if (FsRtlCurrentBatchOplock(&Fcb->Oplock)) { Irp->IoStatus.Information = FILE_OPBATCH_BREAK_UNDERWAY; /* break the batch lock if the sharing check fails */ Status = FsRtlCheckOplock( &Fcb->Oplock, IrpContext->Irp, IrpContext, Ext2OplockComplete, Ext2LockIrp ); if ( Status != STATUS_SUCCESS && Status != STATUS_OPLOCK_BREAK_IN_PROGRESS) { *OpPostIrp = TRUE; _SEH2_LEAVE; } } } if (bCreated) { // // This file is just created. // Status = Ext2OverwriteEa(IrpContext, Vcb, Fcb, &Irp->IoStatus); if (!NT_SUCCESS(Status)) { Ext2DeleteFile(IrpContext, Vcb, Fcb, Mcb); _SEH2_LEAVE; } if (DirectoryFile) { Status = Ext2AddDotEntries(IrpContext, &ParentMcb->Inode, &Mcb->Inode); if (!NT_SUCCESS(Status)) { Ext2DeleteFile(IrpContext, Vcb, Fcb, Mcb); _SEH2_LEAVE; } } else { if ((LONGLONG)ext3_free_blocks_count(SUPER_BLOCK) <= Ext2TotalBlocks(Vcb, &Irp->Overlay.AllocationSize, NULL)) { DbgBreak(); Status = STATUS_DISK_FULL; _SEH2_LEAVE; } /* disable data blocks allocation */ #if 0 Fcb->Header.AllocationSize.QuadPart = Irp->Overlay.AllocationSize.QuadPart; if (Fcb->Header.AllocationSize.QuadPart > 0) { Status = Ext2ExpandFile(IrpContext, Vcb, Fcb->Mcb, &(Fcb->Header.AllocationSize) ); SetLongFlag(Fcb->Flags, FCB_ALLOC_IN_CREATE); if (!NT_SUCCESS(Status)) { Fcb->Header.AllocationSize.QuadPart = 0; Ext2TruncateFile(IrpContext, Vcb, Fcb->Mcb, &Fcb->Header.AllocationSize); _SEH2_LEAVE; } } #endif } } else { // // This file alreayd exists. // if (DeleteOnClose) { if (IsVcbReadOnly(Vcb)) { Status = STATUS_MEDIA_WRITE_PROTECTED; _SEH2_LEAVE; } if (IsFlagOn(Vcb->Flags, VCB_WRITE_PROTECTED)) { Status = STATUS_MEDIA_WRITE_PROTECTED; IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice ); SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); Ext2RaiseStatus(IrpContext, STATUS_MEDIA_WRITE_PROTECTED); } } else { // // Just to Open file (Open/OverWrite ...) // if ((!IsDirectory(Fcb)) && (IsFlagOn(IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING))) { Fcb->Header.IsFastIoPossible = FastIoIsPossible; if (Fcb->SectionObject.DataSectionObject != NULL) { if (Fcb->NonCachedOpenCount == Fcb->OpenHandleCount) { if (!IsVcbReadOnly(Vcb)) { CcFlushCache(&Fcb->SectionObject, NULL, 0, NULL); ClearLongFlag(Fcb->Flags, FCB_FILE_MODIFIED); } CcPurgeCacheSection(&Fcb->SectionObject, NULL, 0, FALSE ); } } } } } if (!IsDirectory(Fcb)) { if (!IsVcbReadOnly(Vcb)) { if ((CreateDisposition == FILE_SUPERSEDE) && !IsPagingFile) { DesiredAccess |= DELETE; } else if (((CreateDisposition == FILE_OVERWRITE) || (CreateDisposition == FILE_OVERWRITE_IF)) && !IsPagingFile) { DesiredAccess |= (FILE_WRITE_DATA | FILE_WRITE_EA | FILE_WRITE_ATTRIBUTES ); } } if (!bFcbAllocated) { // // check the oplock state of the file // Status = FsRtlCheckOplock( &Fcb->Oplock, IrpContext->Irp, IrpContext, Ext2OplockComplete, Ext2LockIrp ); if ( Status != STATUS_SUCCESS && Status != STATUS_OPLOCK_BREAK_IN_PROGRESS) { *OpPostIrp = TRUE; _SEH2_LEAVE; } } } if (Fcb->OpenHandleCount > 0) { /* check the shrae access conflicts */ Status = IoCheckShareAccess( DesiredAccess, ShareAccess, IrpSp->FileObject, &(Fcb->ShareAccess), TRUE ); if (!NT_SUCCESS(Status)) { _SEH2_LEAVE; } } else { /* set share access rights */ IoSetShareAccess( DesiredAccess, ShareAccess, IrpSp->FileObject, &(Fcb->ShareAccess) ); } Ccb = Ext2AllocateCcb(CcbFlags, SymLink); if (!Ccb) { Status = STATUS_INSUFFICIENT_RESOURCES; DbgBreak(); _SEH2_LEAVE; } if (DeleteOnClose) SetLongFlag(Ccb->Flags, CCB_DELETE_ON_CLOSE); if (SymLink) Ccb->filp.f_dentry = SymLink->de; else Ccb->filp.f_dentry = Fcb->Mcb->de; Ccb->filp.f_version = Fcb->Mcb->Inode.i_version; Ext2ReferXcb(&Fcb->OpenHandleCount); Ext2ReferXcb(&Fcb->ReferenceCount); if (!IsDirectory(Fcb)) { if (NoIntermediateBuffering) { Fcb->NonCachedOpenCount++; SetFlag(IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED); } else { SetFlag(IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED); } } Ext2ReferXcb(&Vcb->OpenHandleCount); Ext2ReferXcb(&Vcb->ReferenceCount); IrpSp->FileObject->FsContext = (void*) Fcb; IrpSp->FileObject->FsContext2 = (void*) Ccb; IrpSp->FileObject->PrivateCacheMap = NULL; IrpSp->FileObject->SectionObjectPointer = &(Fcb->SectionObject); DEBUG(DL_INF, ( "Ext2CreateFile: %wZ OpenCount=%u ReferCount=%u NonCachedCount=%u\n", &Fcb->Mcb->FullName, Fcb->OpenHandleCount, Fcb->ReferenceCount, Fcb->NonCachedOpenCount)); Status = STATUS_SUCCESS; if (bCreated) { if (IsDirectory(Fcb)) { Ext2NotifyReportChange( IrpContext, Vcb, Fcb->Mcb, FILE_NOTIFY_CHANGE_DIR_NAME, FILE_ACTION_ADDED ); } else { Ext2NotifyReportChange( IrpContext, Vcb, Fcb->Mcb, FILE_NOTIFY_CHANGE_FILE_NAME, FILE_ACTION_ADDED ); } } else if (!IsDirectory(Fcb)) { if ( DeleteOnClose || IsFlagOn(DesiredAccess, FILE_WRITE_DATA) || (CreateDisposition == FILE_OVERWRITE) || (CreateDisposition == FILE_OVERWRITE_IF)) { if (!MmFlushImageSection( &Fcb->SectionObject, MmFlushForWrite )) { Status = DeleteOnClose ? STATUS_CANNOT_DELETE : STATUS_SHARING_VIOLATION; _SEH2_LEAVE; } } if ((CreateDisposition == FILE_SUPERSEDE) || (CreateDisposition == FILE_OVERWRITE) || (CreateDisposition == FILE_OVERWRITE_IF)) { if (IsDirectory(Fcb)) { Status = STATUS_FILE_IS_A_DIRECTORY; _SEH2_LEAVE; } if (SymLink != NULL) { DbgBreak(); Status = STATUS_INVALID_PARAMETER; _SEH2_LEAVE; } if (IsVcbReadOnly(Vcb)) { Status = STATUS_MEDIA_WRITE_PROTECTED; _SEH2_LEAVE; } if (IsFlagOn(Vcb->Flags, VCB_WRITE_PROTECTED)) { IoSetHardErrorOrVerifyDevice( IrpContext->Irp, Vcb->Vpb->RealDevice ); SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); Ext2RaiseStatus(IrpContext, STATUS_MEDIA_WRITE_PROTECTED); } Status = Ext2SupersedeOrOverWriteFile( IrpContext, IrpSp->FileObject, Vcb, Fcb, &Irp->Overlay.AllocationSize, CreateDisposition ); if (!NT_SUCCESS(Status)) { DbgBreak(); _SEH2_LEAVE; } Ext2NotifyReportChange( IrpContext, Vcb, Fcb->Mcb, FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_ATTRIBUTES | FILE_NOTIFY_CHANGE_SIZE, FILE_ACTION_MODIFIED ); if (CreateDisposition == FILE_SUPERSEDE) { Irp->IoStatus.Information = FILE_SUPERSEDED; } else { Irp->IoStatus.Information = FILE_OVERWRITTEN; } } } } else { DbgBreak(); _SEH2_LEAVE; } } _SEH2_FINALLY { if (bFcbLockAcquired) { ExReleaseResourceLite(&Vcb->FcbLock); } if (ParentMcb) { Ext2DerefMcb(ParentMcb); } /* cleanup Fcb and Ccb, Mcb if necessary */ if (!NT_SUCCESS(Status)) { if (Ccb != NULL) { DbgBreak(); ASSERT(Fcb != NULL); ASSERT(Fcb->Mcb != NULL); DEBUG(DL_ERR, ("Ext2CreateFile: failed to create %wZ status = %xh\n", &Fcb->Mcb->FullName, Status)); Ext2DerefXcb(&Fcb->OpenHandleCount); Ext2DerefXcb(&Fcb->ReferenceCount); if (!IsDirectory(Fcb)) { if (NoIntermediateBuffering) { Fcb->NonCachedOpenCount--; } else { ClearFlag(IrpSp->FileObject->Flags, FO_CACHE_SUPPORTED); } } Ext2DerefXcb(&Vcb->OpenHandleCount); Ext2DerefXcb(&Vcb->ReferenceCount); IoRemoveShareAccess(IrpSp->FileObject, &Fcb->ShareAccess); IrpSp->FileObject->FsContext = NULL; IrpSp->FileObject->FsContext2 = NULL; IrpSp->FileObject->PrivateCacheMap = NULL; IrpSp->FileObject->SectionObjectPointer = NULL; Ext2FreeCcb(Vcb, Ccb); } if (Fcb != NULL) { if (IsFlagOn(Fcb->Flags, FCB_ALLOC_IN_CREATE)) { LARGE_INTEGER Size; ExAcquireResourceExclusiveLite(&Fcb->PagingIoResource, TRUE); _SEH2_TRY { Size.QuadPart = 0; Ext2TruncateFile(IrpContext, Vcb, Fcb->Mcb, &Size); } _SEH2_FINALLY { ExReleaseResourceLite(&Fcb->PagingIoResource); } _SEH2_END; } if (bCreated) { Ext2DeleteFile(IrpContext, Vcb, Fcb, Mcb); } } } if (bMainResourceAcquired) { ExReleaseResourceLite(&Fcb->MainResource); } /* free file name buffer */ if (FileName.Buffer) { DEC_MEM_COUNT(PS_FILE_NAME, FileName.Buffer, FileName.MaximumLength); Ext2FreePool(FileName.Buffer, EXT2_FNAME_MAGIC); } /* dereference Fcb and parent */ if (Fcb) { Ext2ReleaseFcb(Fcb); } if (ParentFcb) { Ext2ReleaseFcb(ParentFcb); } /* drop SymLink's refer: If succeeds, Ext2AllocateCcb should refer it already. It fails, we need release the refer to let it freed */ if (SymLink) { Ext2DerefMcb(SymLink); } } _SEH2_END; return Status; } NTSTATUS Ext2CreateVolume(PEXT2_IRP_CONTEXT IrpContext, PEXT2_VCB Vcb) { PIO_STACK_LOCATION IrpSp; PIRP Irp; PEXT2_CCB Ccb; NTSTATUS Status; ACCESS_MASK DesiredAccess; ULONG ShareAccess; ULONG Options; BOOLEAN DirectoryFile; BOOLEAN OpenTargetDirectory; ULONG CreateDisposition; Irp = IrpContext->Irp; IrpSp = IoGetCurrentIrpStackLocation(Irp); Options = IrpSp->Parameters.Create.Options; DirectoryFile = IsFlagOn(Options, FILE_DIRECTORY_FILE); OpenTargetDirectory = IsFlagOn(IrpSp->Flags, SL_OPEN_TARGET_DIRECTORY); CreateDisposition = (Options >> 24) & 0x000000ff; DesiredAccess = IrpSp->Parameters.Create.SecurityContext->DesiredAccess; ShareAccess = IrpSp->Parameters.Create.ShareAccess; if (DirectoryFile) { return STATUS_NOT_A_DIRECTORY; } if (OpenTargetDirectory) { DbgBreak(); return STATUS_INVALID_PARAMETER; } if ( (CreateDisposition != FILE_OPEN) && (CreateDisposition != FILE_OPEN_IF) ) { return STATUS_ACCESS_DENIED; } if ( !FlagOn(ShareAccess, FILE_SHARE_READ) && Vcb->OpenVolumeCount != 0 ) { return STATUS_SHARING_VIOLATION; } Ccb = Ext2AllocateCcb(0, NULL); if (Ccb == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto errorout; } Status = STATUS_SUCCESS; if (Vcb->OpenVolumeCount > 0) { Status = IoCheckShareAccess( DesiredAccess, ShareAccess, IrpSp->FileObject, &(Vcb->ShareAccess), TRUE); if (!NT_SUCCESS(Status)) { goto errorout; } } else { IoSetShareAccess( DesiredAccess, ShareAccess, IrpSp->FileObject, &(Vcb->ShareAccess) ); } if (Vcb->OpenVolumeCount == 0 && !IsFlagOn(ShareAccess, FILE_SHARE_READ) && !IsFlagOn(ShareAccess, FILE_SHARE_WRITE) ){ if (!IsVcbReadOnly(Vcb)) { Ext2FlushFiles(IrpContext, Vcb, FALSE); Ext2FlushVolume(IrpContext, Vcb, FALSE); } SetLongFlag(Vcb->Flags, VCB_VOLUME_LOCKED); Vcb->LockFile = IrpSp->FileObject; } else { if (FlagOn(IrpSp->FileObject->Flags, FO_NO_INTERMEDIATE_BUFFERING) && FlagOn(DesiredAccess, FILE_READ_DATA | FILE_WRITE_DATA) ) { if (!IsVcbReadOnly(Vcb)) { Ext2FlushFiles(IrpContext, Vcb, FALSE); Ext2FlushVolume(IrpContext, Vcb, FALSE); } } } IrpSp->FileObject->Flags |= FO_NO_INTERMEDIATE_BUFFERING; IrpSp->FileObject->FsContext = Vcb; IrpSp->FileObject->FsContext2 = Ccb; IrpSp->FileObject->Vpb = Vcb->Vpb; Ext2ReferXcb(&Vcb->ReferenceCount); Ext2ReferXcb(&Vcb->OpenHandleCount); Ext2ReferXcb(&Vcb->OpenVolumeCount); Irp->IoStatus.Information = FILE_OPENED; errorout: return Status; } NTSTATUS Ext2Create (IN PEXT2_IRP_CONTEXT IrpContext) { PDEVICE_OBJECT DeviceObject; PIRP Irp; PIO_STACK_LOCATION IrpSp; PEXT2_VCB Vcb = 0; NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND; PEXT2_FCBVCB Xcb = NULL; BOOLEAN PostIrp = FALSE; BOOLEAN VcbResourceAcquired = FALSE; DeviceObject = IrpContext->DeviceObject; Irp = IrpContext->Irp; IrpSp = IoGetCurrentIrpStackLocation(Irp); Xcb = (PEXT2_FCBVCB) (IrpSp->FileObject->FsContext); if (IsExt2FsDevice(DeviceObject)) { DEBUG(DL_INF, ( "Ext2Create: Create on main device object.\n")); Status = STATUS_SUCCESS; Irp->IoStatus.Information = FILE_OPENED; Ext2CompleteIrpContext(IrpContext, Status); return Status; } _SEH2_TRY { Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; ASSERT(Vcb->Identifier.Type == EXT2VCB); IrpSp->FileObject->Vpb = Vcb->Vpb; if (!IsMounted(Vcb)) { DbgBreak(); if (IsFlagOn(Vcb->Flags, VCB_DEVICE_REMOVED)) { Status = STATUS_NO_SUCH_DEVICE; } else { Status = STATUS_VOLUME_DISMOUNTED; } _SEH2_LEAVE; } if (!ExAcquireResourceExclusiveLite( &Vcb->MainResource, TRUE)) { Status = STATUS_PENDING; _SEH2_LEAVE; } VcbResourceAcquired = TRUE; Ext2VerifyVcb(IrpContext, Vcb); if (FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED)) { Status = STATUS_ACCESS_DENIED; if (IsFlagOn(Vcb->Flags, VCB_DISMOUNT_PENDING)) { Status = STATUS_VOLUME_DISMOUNTED; } _SEH2_LEAVE; } if ( ((IrpSp->FileObject->FileName.Length == 0) && (IrpSp->FileObject->RelatedFileObject == NULL)) || (Xcb && Xcb->Identifier.Type == EXT2VCB) ) { Status = Ext2CreateVolume(IrpContext, Vcb); } else { Status = Ext2CreateFile(IrpContext, Vcb, &PostIrp); } } _SEH2_FINALLY { if (VcbResourceAcquired) { ExReleaseResourceLite(&Vcb->MainResource); } if (!IrpContext->ExceptionInProgress && !PostIrp) { if ( Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { Status = Ext2QueueRequest(IrpContext); } else { Ext2CompleteIrpContext(IrpContext, Status); } } } _SEH2_END; return Status; } NTSTATUS Ext2CreateInode( PEXT2_IRP_CONTEXT IrpContext, PEXT2_VCB Vcb, PEXT2_FCB Parent, ULONG Type, ULONG FileAttr, PUNICODE_STRING FileName) { NTSTATUS Status; ULONG iGrp; ULONG iNo; struct inode Inode = { 0 }; struct dentry *Dentry = NULL; struct ext3_super_block *es = EXT3_SB(&Vcb->sb)->s_es; LARGE_INTEGER SysTime; iGrp = (Parent->Inode->i_ino - 1) / BLOCKS_PER_GROUP; DEBUG(DL_INF, ("Ext2CreateInode: %S in %S(Inode=%xh)\n", FileName->Buffer, Parent->Mcb->ShortName.Buffer, Parent->Inode->i_ino)); Status = Ext2NewInode(IrpContext, Vcb, iGrp, Type, &iNo); if (!NT_SUCCESS(Status)) { goto errorout; } KeQuerySystemTime(&SysTime); Ext2ClearInode(IrpContext, Vcb, iNo); Inode.i_sb = &Vcb->sb; Inode.i_ino = iNo; Inode.i_ctime = Inode.i_mtime = Inode.i_atime = Ext2LinuxTime(SysTime); if (IsFlagOn(Vcb->Flags, VCB_USER_IDS)) { Inode.i_uid = Vcb->uid; Inode.i_gid = Vcb->gid; } else { Inode.i_uid = Parent->Mcb->Inode.i_uid; Inode.i_gid = Parent->Mcb->Inode.i_gid; } Inode.i_generation = Parent->Inode->i_generation; Inode.i_mode = S_IPERMISSION_MASK & Parent->Inode->i_mode; if (Type == EXT2_FT_DIR) { Inode.i_mode |= S_IFDIR; } else if (Type == EXT2_FT_REG_FILE) { Inode.i_mode &= S_IFATTR; Inode.i_mode |= S_IFREG; } else { DbgBreak(); } if (le16_to_cpu(es->s_want_extra_isize)) Inode.i_extra_isize = le16_to_cpu(es->s_want_extra_isize); /* Force using extent */ if (IsFlagOn(SUPER_BLOCK->s_feature_incompat, EXT4_FEATURE_INCOMPAT_EXTENTS)) { Inode.i_flags |= EXT2_EXTENTS_FL; ext4_ext_tree_init(IrpContext, NULL, &Inode); /* ext4_ext_tree_init will save inode body */ } else { /* save inode body to cache */ Ext2SaveInode(IrpContext, Vcb, &Inode); } /* add new entry to its parent */ Status = Ext2AddEntry( IrpContext, Vcb, Parent, &Inode, FileName, &Dentry ); if (!NT_SUCCESS(Status)) { DbgBreak(); Ext2FreeInode(IrpContext, Vcb, iNo, Type); goto errorout; } DEBUG(DL_INF, ("Ext2CreateInode: New Inode = %xh (Type=%xh)\n", Inode.i_ino, Type)); errorout: if (Dentry) Ext2FreeEntry(Dentry); return Status; } NTSTATUS Ext2SupersedeOrOverWriteFile( IN PEXT2_IRP_CONTEXT IrpContext, IN PFILE_OBJECT FileObject, IN PEXT2_VCB Vcb, IN PEXT2_FCB Fcb, IN PLARGE_INTEGER AllocationSize, IN ULONG Disposition ) { LARGE_INTEGER CurrentTime; LARGE_INTEGER Size; KeQuerySystemTime(&CurrentTime); Size.QuadPart = 0; if (!MmCanFileBeTruncated(&(Fcb->SectionObject), &(Size))) { return STATUS_USER_MAPPED_FILE; } /* purge all file cache and shrink cache windows size */ CcPurgeCacheSection(&Fcb->SectionObject, NULL, 0, FALSE); Fcb->Header.AllocationSize.QuadPart = Fcb->Header.FileSize.QuadPart = Fcb->Header.ValidDataLength.QuadPart = 0; CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize); Size.QuadPart = CEILING_ALIGNED(ULONGLONG, (ULONGLONG)AllocationSize->QuadPart, (ULONGLONG)BLOCK_SIZE); if ((loff_t)Size.QuadPart > Fcb->Inode->i_size) { Ext2ExpandFile(IrpContext, Vcb, Fcb->Mcb, &Size); } else { Ext2TruncateFile(IrpContext, Vcb, Fcb->Mcb, &Size); } Fcb->Header.AllocationSize = Size; if (Fcb->Header.AllocationSize.QuadPart > 0) { SetLongFlag(Fcb->Flags, FCB_ALLOC_IN_CREATE); CcSetFileSizes(FileObject, (PCC_FILE_SIZES)&Fcb->Header.AllocationSize ); } /* remove all extent mappings */ DEBUG(DL_EXT, ("Ext2SuperSede ...: %wZ\n", &Fcb->Mcb->FullName)); Fcb->Inode->i_size = 0; if (Disposition == FILE_SUPERSEDE) { Fcb->Inode->i_ctime = Ext2LinuxTime(CurrentTime); } Fcb->Inode->i_atime = Fcb->Inode->i_mtime = Ext2LinuxTime(CurrentTime); Ext2SaveInode(IrpContext, Vcb, Fcb->Inode); // See if we need to overwrite EA of the file return Ext2OverwriteEa(IrpContext, Vcb, Fcb, &IrpContext->Irp->IoStatus); }