reactos/drivers/filesystems/ext2/src/create.c

2195 lines
69 KiB
C

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