diff --git a/Ext4Fsd/create.c b/Ext4Fsd/create.c index 0ebde76..b67064b 100644 --- a/Ext4Fsd/create.c +++ b/Ext4Fsd/create.c @@ -1,2186 +1,2196 @@ -/* - * 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; - - __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) { - __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; - __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)) { - __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; - __leave; - } - - UniName.MaximumLength += 2; - UniName.Buffer = Ext2AllocatePool(PagedPool, - UniName.MaximumLength, - 'NL2E'); - if (UniName.Buffer == NULL) { - Status = STATUS_INSUFFICIENT_RESOURCES; - __leave; - } - RtlZeroMemory(UniName.Buffer, UniName.MaximumLength); - Status = Ext2OEMToUnicode(Vcb, &UniName, &OemName); - if (!NT_SUCCESS(Status)) { - Status = STATUS_INSUFFICIENT_RESOURCES; - __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; - } - - } __finally { - - if (bOemBuffer) { - Ext2FreePool(OemName.Buffer, 'NL2E'); - } - - if (UniName.Buffer) { - Ext2FreePool(UniName.Buffer, 'NL2E'); - } - } - - 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; - - __try { - - ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); - LockAcquired = TRUE; - - bNotFollow = IsFlagOn(Linkdep, EXT2_LOOKUP_NOT_FOLLOW); - Linkdep = ClearFlag(Linkdep, EXT2_LOOKUP_FLAG_MASK); - - *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; - __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; - __leave; - } - } - - if (NULL == Parent) { - Status = STATUS_NOT_A_DIRECTORY; - __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; - __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; - } - } - - } __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); - } - } - - 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)); - - __try { - - /* grab parent's reference first */ - Ext2ReferMcb(Parent); - - /* bad request ! Can a man be pregnant ? Maybe:) */ - if (!IsMcbDirectory(Parent)) { - Status = STATUS_NOT_A_DIRECTORY; - __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; - __leave; - } - } - - de = Ext2BuildEntry(Vcb, Parent, FileName); - if (!de) { - DEBUG(DL_ERR, ( "Ex2ScanDir: failed to allocate dentry.\n")); - Status = STATUS_INSUFFICIENT_RESOURCES; - __leave; - } - - bh = ext3_find_entry(IrpContext, de, &dir_entry); - if (dir_entry) { - Status = STATUS_SUCCESS; - *Inode = dir_entry->inode; - *dentry = de; - } - - } __finally { - - Ext2DerefMcb(Parent); - - if (bh) - __brelse(bh); - - if (!NT_SUCCESS(Status)) { - if (de) - Ext2FreeEntry(de); - } - } - - 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; - - __try { - - Irp = IrpContext->Irp; - IrpSp = IoGetCurrentIrpStackLocation(Irp); - Mcb = Fcb->Mcb; - - EaBuffer = Irp->AssociatedIrp.SystemBuffer; - EaBufferLength = IrpSp->Parameters.Create.EaLength; - - if (!Mcb) - __leave; - - // - // Return peacefully if there is no EaBuffer provided. - // - if (!EaBuffer) { - Status = STATUS_SUCCESS; - __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; - __leave; - } - - // - // Check Ea Buffer validity. - // - Status = IoCheckEaBufferValidity((PFILE_FULL_EA_INFORMATION)EaBuffer, - EaBufferLength, (PULONG)&Iosb->Information); - if (!NT_SUCCESS(Status)) - __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"); - __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; - __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) - __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)) - __leave; - - } - } - } - __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)); - } - } - } - 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; - BOOLEAN bDir = FALSE; - 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; - - __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; - __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; - __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; - __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; - __leave; - } - } - - if (IsFlagOn(Options, FILE_OPEN_BY_FILE_ID)) { - Status = STATUS_NOT_IMPLEMENTED; - __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; - - LONG i = 0; - - 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) { - __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; - __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; - __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; - } - __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; - __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; - __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; - __leave; - } - - if (!Ext2CheckFileAccess(Vcb, ParentMcb, Ext2FileCanWrite)) { - Status = STATUS_ACCESS_DENIED; - __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; - __leave; - } - } - - if (!ParentFcb) { - Status = STATUS_OBJECT_PATH_NOT_FOUND; - __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(); - __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; - __leave; - } - - if (!ParentFcb) { - Status = STATUS_OBJECT_PATH_NOT_FOUND; - __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; - __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); - __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); - __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); - __leave; - } - - if (NonDirectoryFile) { - Status = STATUS_FILE_IS_A_DIRECTORY; - Ext2DerefMcb(Mcb); - __leave; - } - - if (Mcb->Inode.i_ino == EXT2_ROOT_INO) { - - if (OpenTargetDirectory) { - DbgBreak(); - Status = STATUS_INVALID_PARAMETER; - Ext2DerefMcb(Mcb); - __leave; - } - } - - } else { - - if (DirectoryFile) { - Status = STATUS_NOT_A_DIRECTORY;; - Ext2DerefMcb(Mcb); - __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; - __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; - __leave; - } else if (IsFlagOn(Options, FILE_DELETE_ON_CLOSE )) { - Status = STATUS_CANNOT_DELETE; - __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; - __leave; - } - - if (DeleteOnClose && NULL == SymLink) { - Status = Ext2IsFileRemovable(IrpContext, Vcb, Fcb, Ccb); - if (!NT_SUCCESS(Status)) { - __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; - __leave; - } - } - } - - if (bCreated) { - - // - // This file is just created. - // - - Status = Ext2OverwriteEa(IrpContext, Vcb, Fcb, &Irp->IoStatus); - if (!NT_SUCCESS(Status)) { - Ext2DeleteFile(IrpContext, Vcb, Fcb, Mcb); - __leave; - } - - if (DirectoryFile) { - - Status = Ext2AddDotEntries(IrpContext, &ParentMcb->Inode, &Mcb->Inode); - if (!NT_SUCCESS(Status)) { - Ext2DeleteFile(IrpContext, Vcb, Fcb, Mcb); - __leave; - } - - } else { - - if ((LONGLONG)ext3_free_blocks_count(SUPER_BLOCK) <= - Ext2TotalBlocks(Vcb, &Irp->Overlay.AllocationSize, NULL)) { - DbgBreak(); - Status = STATUS_DISK_FULL; - __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); - __leave; - } - } -#endif - } - - } else { - - // - // This file alreayd exists. - // - - if (DeleteOnClose) { - - if (IsVcbReadOnly(Vcb)) { - Status = STATUS_MEDIA_WRITE_PROTECTED; - __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; - __leave; - } - } - } - - if (Fcb->OpenHandleCount > 0) { - - /* check the shrae access conflicts */ - Status = IoCheckShareAccess( DesiredAccess, - ShareAccess, - IrpSp->FileObject, - &(Fcb->ShareAccess), - TRUE ); - if (!NT_SUCCESS(Status)) { - __leave; - } - - } else { - - /* set share access rights */ - IoSetShareAccess( DesiredAccess, - ShareAccess, - IrpSp->FileObject, - &(Fcb->ShareAccess) ); - } - - Ccb = Ext2AllocateCcb(CcbFlags, SymLink); - if (!Ccb) { - Status = STATUS_INSUFFICIENT_RESOURCES; - DbgBreak(); - __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; - __leave; - } - } - - if ((CreateDisposition == FILE_SUPERSEDE) || - (CreateDisposition == FILE_OVERWRITE) || - (CreateDisposition == FILE_OVERWRITE_IF)) { - - if (IsDirectory(Fcb)) { - Status = STATUS_FILE_IS_A_DIRECTORY; - __leave; - } - - if (SymLink != NULL) { - DbgBreak(); - Status = STATUS_INVALID_PARAMETER; - __leave; - } - - if (IsVcbReadOnly(Vcb)) { - Status = STATUS_MEDIA_WRITE_PROTECTED; - __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(); - __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(); - __leave; - } - - } __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); - __try { - Size.QuadPart = 0; - Ext2TruncateFile(IrpContext, Vcb, Fcb->Mcb, &Size); - } __finally { - ExReleaseResourceLite(&Fcb->PagingIoResource); - } - } - - 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); - } - } - - 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; - } - - __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; - } - __leave; - } - - if (!ExAcquireResourceExclusiveLite( - &Vcb->MainResource, TRUE)) { - Status = STATUS_PENDING; - __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; - } - __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); - } - - } __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); - } - } - } - - 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); -} +/* + * 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; + + __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) { + __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; + __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)) { + __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; + __leave; + } + + UniName.MaximumLength += 2; + UniName.Buffer = Ext2AllocatePool(PagedPool, + UniName.MaximumLength, + 'NL2E'); + if (UniName.Buffer == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + __leave; + } + RtlZeroMemory(UniName.Buffer, UniName.MaximumLength); + Status = Ext2OEMToUnicode(Vcb, &UniName, &OemName); + if (!NT_SUCCESS(Status)) { + Status = STATUS_INSUFFICIENT_RESOURCES; + __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; + } + + } __finally { + + if (bOemBuffer) { + Ext2FreePool(OemName.Buffer, 'NL2E'); + } + + if (UniName.Buffer) { + Ext2FreePool(UniName.Buffer, 'NL2E'); + } + } + + 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; + + __try { + + ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); + LockAcquired = TRUE; + + bNotFollow = IsFlagOn(Linkdep, EXT2_LOOKUP_NOT_FOLLOW); + Linkdep = ClearFlag(Linkdep, EXT2_LOOKUP_FLAG_MASK); + + *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; + __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; + __leave; + } + } + + if (NULL == Parent) { + Status = STATUS_NOT_A_DIRECTORY; + __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; + __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; + } + } + + } __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); + } + } + + 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)); + + __try { + + /* grab parent's reference first */ + Ext2ReferMcb(Parent); + + /* bad request ! Can a man be pregnant ? Maybe:) */ + if (!IsMcbDirectory(Parent)) { + Status = STATUS_NOT_A_DIRECTORY; + __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; + __leave; + } + } + + de = Ext2BuildEntry(Vcb, Parent, FileName); + if (!de) { + DEBUG(DL_ERR, ( "Ex2ScanDir: failed to allocate dentry.\n")); + Status = STATUS_INSUFFICIENT_RESOURCES; + __leave; + } + + bh = ext3_find_entry(IrpContext, de, &dir_entry); + if (dir_entry) { + Status = STATUS_SUCCESS; + *Inode = dir_entry->inode; + *dentry = de; + } + + } __finally { + + Ext2DerefMcb(Parent); + + if (bh) + __brelse(bh); + + if (!NT_SUCCESS(Status)) { + if (de) + Ext2FreeEntry(de); + } + } + + return Status; +} + +NTSTATUS Ext2AddDotEntries(struct ext2_icb *icb, struct inode *dir, + struct inode *inode) +{ + struct ext3_dir_entry_2 * de; + struct buffer_head * bh; + struct ext4_dir_entry_tail *t; + ext3_lblk_t block = 0; + unsigned int blocksize = dir->i_sb->s_blocksize; + int csum_size = 0; + int rc = 0; + + if (ext4_has_metadata_csum(dir->i_sb)) + csum_size = sizeof(struct ext4_dir_entry_tail); + + 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; + if (csum_size) { + t = EXT4_DIRENT_TAIL(bh->b_data, blocksize); + initialize_dirent_tail(t, blocksize); + } + 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; + + __try { + + Irp = IrpContext->Irp; + IrpSp = IoGetCurrentIrpStackLocation(Irp); + Mcb = Fcb->Mcb; + + EaBuffer = Irp->AssociatedIrp.SystemBuffer; + EaBufferLength = IrpSp->Parameters.Create.EaLength; + + if (!Mcb) + __leave; + + // + // Return peacefully if there is no EaBuffer provided. + // + if (!EaBuffer) { + Status = STATUS_SUCCESS; + __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; + __leave; + } + + // + // Check Ea Buffer validity. + // + Status = IoCheckEaBufferValidity((PFILE_FULL_EA_INFORMATION)EaBuffer, + EaBufferLength, (PULONG)&Iosb->Information); + if (!NT_SUCCESS(Status)) + __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"); + __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; + __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) + __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)) + __leave; + + } + } + } + __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)); + } + } + } + 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; + BOOLEAN bDir = FALSE; + 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; + + __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; + __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; + __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; + __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; + __leave; + } + } + + if (IsFlagOn(Options, FILE_OPEN_BY_FILE_ID)) { + Status = STATUS_NOT_IMPLEMENTED; + __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; + + LONG i = 0; + + 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) { + __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; + __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; + __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; + } + __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; + __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; + __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; + __leave; + } + + if (!Ext2CheckFileAccess(Vcb, ParentMcb, Ext2FileCanWrite)) { + Status = STATUS_ACCESS_DENIED; + __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; + __leave; + } + } + + if (!ParentFcb) { + Status = STATUS_OBJECT_PATH_NOT_FOUND; + __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(); + __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; + __leave; + } + + if (!ParentFcb) { + Status = STATUS_OBJECT_PATH_NOT_FOUND; + __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; + __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); + __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); + __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); + __leave; + } + + if (NonDirectoryFile) { + Status = STATUS_FILE_IS_A_DIRECTORY; + Ext2DerefMcb(Mcb); + __leave; + } + + if (Mcb->Inode.i_ino == EXT2_ROOT_INO) { + + if (OpenTargetDirectory) { + DbgBreak(); + Status = STATUS_INVALID_PARAMETER; + Ext2DerefMcb(Mcb); + __leave; + } + } + + } else { + + if (DirectoryFile) { + Status = STATUS_NOT_A_DIRECTORY;; + Ext2DerefMcb(Mcb); + __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; + __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; + __leave; + } else if (IsFlagOn(Options, FILE_DELETE_ON_CLOSE )) { + Status = STATUS_CANNOT_DELETE; + __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; + __leave; + } + + if (DeleteOnClose && NULL == SymLink) { + Status = Ext2IsFileRemovable(IrpContext, Vcb, Fcb, Ccb); + if (!NT_SUCCESS(Status)) { + __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; + __leave; + } + } + } + + if (bCreated) { + + // + // This file is just created. + // + + Status = Ext2OverwriteEa(IrpContext, Vcb, Fcb, &Irp->IoStatus); + if (!NT_SUCCESS(Status)) { + Ext2DeleteFile(IrpContext, Vcb, Fcb, Mcb); + __leave; + } + + if (DirectoryFile) { + + Status = Ext2AddDotEntries(IrpContext, &ParentMcb->Inode, &Mcb->Inode); + if (!NT_SUCCESS(Status)) { + Ext2DeleteFile(IrpContext, Vcb, Fcb, Mcb); + __leave; + } + + } else { + + if ((LONGLONG)ext3_free_blocks_count(SUPER_BLOCK) <= + Ext2TotalBlocks(Vcb, &Irp->Overlay.AllocationSize, NULL)) { + DbgBreak(); + Status = STATUS_DISK_FULL; + __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); + __leave; + } + } +#endif + } + + } else { + + // + // This file alreayd exists. + // + + if (DeleteOnClose) { + + if (IsVcbReadOnly(Vcb)) { + Status = STATUS_MEDIA_WRITE_PROTECTED; + __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; + __leave; + } + } + } + + if (Fcb->OpenHandleCount > 0) { + + /* check the shrae access conflicts */ + Status = IoCheckShareAccess( DesiredAccess, + ShareAccess, + IrpSp->FileObject, + &(Fcb->ShareAccess), + TRUE ); + if (!NT_SUCCESS(Status)) { + __leave; + } + + } else { + + /* set share access rights */ + IoSetShareAccess( DesiredAccess, + ShareAccess, + IrpSp->FileObject, + &(Fcb->ShareAccess) ); + } + + Ccb = Ext2AllocateCcb(CcbFlags, SymLink); + if (!Ccb) { + Status = STATUS_INSUFFICIENT_RESOURCES; + DbgBreak(); + __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; + __leave; + } + } + + if ((CreateDisposition == FILE_SUPERSEDE) || + (CreateDisposition == FILE_OVERWRITE) || + (CreateDisposition == FILE_OVERWRITE_IF)) { + + if (IsDirectory(Fcb)) { + Status = STATUS_FILE_IS_A_DIRECTORY; + __leave; + } + + if (SymLink != NULL) { + DbgBreak(); + Status = STATUS_INVALID_PARAMETER; + __leave; + } + + if (IsVcbReadOnly(Vcb)) { + Status = STATUS_MEDIA_WRITE_PROTECTED; + __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(); + __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(); + __leave; + } + + } __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); + __try { + Size.QuadPart = 0; + Ext2TruncateFile(IrpContext, Vcb, Fcb->Mcb, &Size); + } __finally { + ExReleaseResourceLite(&Fcb->PagingIoResource); + } + } + + 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); + } + } + + 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; + } + + __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; + } + __leave; + } + + if (!ExAcquireResourceExclusiveLite( + &Vcb->MainResource, TRUE)) { + Status = STATUS_PENDING; + __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; + } + __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); + } + + } __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); + } + } + } + + 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); +}