diff options
author | Matt Wu <matt@ext2fsd.com> | 2016-03-13 16:23:04 +0300 |
---|---|---|
committer | Matt Wu <matt@ext2fsd.com> | 2016-03-13 16:23:04 +0300 |
commit | 1fd42c2bde2c1a8e4bac4f1bcf90e9ae92ca4909 (patch) | |
tree | 1197b586b32eef1d7cb39540c89e70dfaca9231f | |
parent | cb7d54c1165f7af04fdafcc61992cd7e7afc3a7e (diff) |
Feature: symlink supported
merged and optimized upon ngkaho123's implmentation
-rwxr-xr-x | Ext3Fsd/create.c | 64 | ||||
-rw-r--r-- | Ext3Fsd/dirctl.c | 30 | ||||
-rwxr-xr-x | Ext3Fsd/ext3/generic.c | 63 | ||||
-rw-r--r-- | Ext3Fsd/fsctl.c | 602 | ||||
-rwxr-xr-x | Ext3Fsd/include/ext2fs.h | 49 | ||||
-rwxr-xr-x | Ext3Fsd/memory.c | 7 | ||||
-rwxr-xr-x | Ext3Fsd/read.c | 9 | ||||
-rwxr-xr-x | Ext3Fsd/volinfo.c | 3 | ||||
-rwxr-xr-x | Ext3Fsd/write.c | 7 |
9 files changed, 790 insertions, 44 deletions
diff --git a/Ext3Fsd/create.c b/Ext3Fsd/create.c index 8b28418..fcaba3a 100755 --- a/Ext3Fsd/create.c +++ b/Ext3Fsd/create.c @@ -68,7 +68,7 @@ Ext2FollowLink ( IN PEXT2_VCB Vcb, IN PEXT2_MCB Parent, IN PEXT2_MCB Mcb, - IN USHORT Linkdep + IN ULONG Linkdep ) { NTSTATUS Status = STATUS_LINK_FAILED; @@ -88,12 +88,12 @@ Ext2FollowLink ( /* exit if we jump into a possible symlink forever loop */ if ((Linkdep + 1) > EXT2_MAX_NESTED_LINKS || - IoGetRemainingStackSize() < 1024) { + IoGetRemainingStackSize() < 1024) { __leave; } /* read the symlink target path */ - if (Mcb->Inode.i_size < EXT2_LINKLEN_IN_INODE) { + if (!Mcb->Inode.i_blocks) { OemName.Buffer = (PUCHAR) (&Mcb->Inode.i_block[0]); OemName.Length = (USHORT)Mcb->Inode.i_size; @@ -113,14 +113,12 @@ Ext2FollowLink ( bOemBuffer = TRUE; RtlZeroMemory(OemName.Buffer, OemName.MaximumLength); - Status = Ext2ReadInode( + Status = Ext2ReadSymlink( IrpContext, Vcb, Mcb, - (ULONGLONG)0, OemName.Buffer, (ULONG)(Mcb->Inode.i_size), - FALSE, NULL); if (!NT_SUCCESS(Status)) { __leave; @@ -170,9 +168,9 @@ Ext2FollowLink ( } if (Target == NULL /* link target doesn't exist */ || - Target == Mcb /* symlink points to itself */ || - IsMcbSpecialFile(Target) /* target not resolved*/ || - IsFileDeleted(Target) /* target deleted */ ) { + Target == Mcb /* symlink points to itself */ || + IsMcbSpecialFile(Target) /* target not resolved*/ || + IsFileDeleted(Target) /* target deleted */ ) { if (Target) { ASSERT(Target->Refercount > 0); @@ -180,7 +178,6 @@ Ext2FollowLink ( } ClearLongFlag(Mcb->Flags, MCB_TYPE_SYMLINK); SetLongFlag(Mcb->Flags, MCB_TYPE_SPECIAL); - Mcb->FileAttr = FILE_ATTRIBUTE_NORMAL; Mcb->Target = NULL; } else if (IsMcbSymLink(Target)) { @@ -194,15 +191,18 @@ Ext2FollowLink ( SetLongFlag(Mcb->Flags, MCB_TYPE_SYMLINK); ClearLongFlag(Mcb->Flags, MCB_TYPE_SPECIAL); ASSERT(Mcb->Target->Refercount > 0); - Mcb->FileAttr = Target->FileAttr; - + } else { Mcb->Target = Target; SetLongFlag(Mcb->Flags, MCB_TYPE_SYMLINK); ClearLongFlag(Mcb->Flags, MCB_TYPE_SPECIAL); ASSERT(Mcb->Target->Refercount > 0); - Mcb->FileAttr = Target->FileAttr; + } + + /* add directory flag to file attribute */ + if (Mcb->Target && IsMcbDirectory(Mcb->Target)) { + Mcb->FileAttr |= FILE_ATTRIBUTE_DIRECTORY; } } __finally { @@ -275,7 +275,7 @@ Ext2LookupFile ( IN PUNICODE_STRING FullName, IN PEXT2_MCB Parent, OUT PEXT2_MCB * Ext2Mcb, - IN USHORT Linkdep + IN ULONG Linkdep ) { NTSTATUS Status = STATUS_OBJECT_NAME_NOT_FOUND; @@ -289,12 +289,16 @@ Ext2LookupFile ( 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)); @@ -394,7 +398,7 @@ Ext2LookupFile ( Parent = Mcb; if (IsMcbSymLink(Mcb) && IsFileDeleted(Mcb->Target) && - (Mcb->Refercount == 1)) { + Mcb->Refercount == 1) { ASSERT(Mcb->Target); ASSERT(Mcb->Target->Refercount > 0); @@ -460,9 +464,11 @@ Ext2LookupFile ( if (S_ISDIR(Mcb->Inode.i_mode)) { SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_DIRECTORY); } else { - SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_NORMAL); - if (!S_ISREG(Mcb->Inode.i_mode) && - !S_ISLNK(Mcb->Inode.i_mode)) { + 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); } } @@ -484,7 +490,7 @@ Ext2LookupFile ( Mcb->ChangeTime = Ext2NtTime(Mcb->Inode.i_mtime); /* process symlink */ - if (S_ISLNK(Mcb->Inode.i_mode)) { + if (S_ISLNK(Mcb->Inode.i_mode) && !bNotFollow) { Ext2FollowLink( IrpContext, Vcb, Parent, @@ -706,9 +712,11 @@ Ext2CreateFile( BOOLEAN DeleteOnClose; BOOLEAN TemporaryFile; BOOLEAN CaseSensitive; + BOOLEAN OpenReparsePoint; ACCESS_MASK DesiredAccess; ULONG ShareAccess; + ULONG CcbFlags = 0; RtlZeroMemory(&FileName, sizeof(UNICODE_STRING)); @@ -726,6 +734,9 @@ Ext2CreateFile( 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, @@ -841,7 +852,8 @@ Ext2CreateFile( &FileName, ParentMcb, &Mcb, - 0 ); + 0 /* always follow link */ + ); McbExisting: if (!NT_SUCCESS(Status)) { @@ -1101,7 +1113,7 @@ Dissecting: if (IsMcbDirectory(Mcb)) { if ((CreateDisposition != FILE_OPEN) && - (CreateDisposition != FILE_OPEN_IF)) { + (CreateDisposition != FILE_OPEN_IF)) { Status = STATUS_OBJECT_NAME_COLLISION; Ext2DerefMcb(Mcb); @@ -1144,7 +1156,11 @@ Openit: /* refer it's target if it's a symlink, so both refered */ if (IsMcbSymLink(Mcb)) { - if (IsFileDeleted(Mcb->Target)) { + + 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); @@ -1396,7 +1412,7 @@ Openit: &(Fcb->ShareAccess) ); } - Ccb = Ext2AllocateCcb(SymLink); + Ccb = Ext2AllocateCcb(CcbFlags, SymLink); if (!Ccb) { Status = STATUS_INSUFFICIENT_RESOURCES; DbgBreak(); @@ -1682,7 +1698,7 @@ Ext2CreateVolume(PEXT2_IRP_CONTEXT IrpContext, PEXT2_VCB Vcb) return STATUS_SHARING_VIOLATION; } - Ccb = Ext2AllocateCcb(NULL); + Ccb = Ext2AllocateCcb(0, NULL); if (Ccb == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto errorout; diff --git a/Ext3Fsd/dirctl.c b/Ext3Fsd/dirctl.c index 611c515..6840dc0 100644 --- a/Ext3Fsd/dirctl.c +++ b/Ext3Fsd/dirctl.c @@ -94,6 +94,8 @@ Ext2ProcessEntry( LONGLONG AllocationSize; ULONG FileAttributes = 0; + BOOLEAN IsEntrySymlink = FALSE; + *EntrySize = 0; NameLength = pName->Length; ASSERT((UsedLength & 7) == 0); @@ -118,7 +120,12 @@ Ext2ProcessEntry( DEBUG(DL_CP, ("Ext2ProcessDirEntry: %wZ in %wZ\n", pName, &Dcb->Mcb->FullName )); Mcb = Ext2SearchMcb(Vcb, Dcb->Mcb, pName); - if (NULL == Mcb) { + if (NULL != Mcb) { + if (S_ISLNK(Mcb->Inode.i_mode) && NULL == Mcb->Target) { + Ext2FollowLink( IrpContext, Vcb, Dcb->Mcb, Mcb, 0); + } + + } else { Inode.i_ino = in; Inode.i_sb = &Vcb->sb; @@ -134,7 +141,7 @@ Ext2ProcessEntry( } else if (S_ISLNK(Inode.i_mode)) { DEBUG(DL_RES, ("Ext2ProcessDirEntry: SymLink: %wZ\\%wZ\n", &Dcb->Mcb->FullName, pName)); - Ext2LookupFile(IrpContext, Vcb, pName, Dcb->Mcb, &Mcb, 0); + Ext2LookupFile(IrpContext, Vcb, pName, Dcb->Mcb, &Mcb,0); if (Mcb && IsMcbSpecialFile(Mcb)) { Ext2DerefMcb(Mcb); @@ -154,6 +161,7 @@ Ext2ProcessEntry( ASSERT(!IsMcbSymLink(Target)); if (IsMcbDirectory(Target)) { FileSize = 0; + FileAttributes |= FILE_ATTRIBUTE_DIRECTORY; } else { FileSize = Target->Inode.i_size; } @@ -169,6 +177,10 @@ Ext2ProcessEntry( } } + if (IsInodeSymLink(&Mcb->Inode)) { + IsEntrySymlink = TRUE; + } + } else { if (S_ISDIR(Inode.i_mode)) { @@ -179,6 +191,9 @@ Ext2ProcessEntry( if (S_ISDIR(Inode.i_mode)) { FileAttributes = FILE_ATTRIBUTE_DIRECTORY; + } else if (S_ISLNK(Inode.i_mode)) { + FileAttributes = FILE_ATTRIBUTE_REPARSE_POINT; + IsEntrySymlink = TRUE; } else { FileAttributes = FILE_ATTRIBUTE_NORMAL; } @@ -252,8 +267,14 @@ Ext2ProcessEntry( if (FIF) { FIF->FileId.QuadPart = (LONGLONG) in; + if (IsEntrySymlink) { + FIF->EaSize = IO_REPARSE_TAG_SYMLINK; + } RtlCopyMemory(&FIF->FileName[0], &pName->Buffer[0], NameLength); } else if (FFI) { + if (IsEntrySymlink) { + FFI->EaSize = IO_REPARSE_TAG_SYMLINK; + } RtlCopyMemory(&FFI->FileName[0], &pName->Buffer[0], NameLength); } else { RtlCopyMemory(&FDI->FileName[0], &pName->Buffer[0], NameLength); @@ -300,6 +321,9 @@ Ext2ProcessEntry( if (FIB) { FIB->FileId.QuadPart = (LONGLONG)in; + if (IsEntrySymlink) { + FIB->EaSize = IO_REPARSE_TAG_SYMLINK; + } RtlCopyMemory(&FIB->FileName[0], &pName->Buffer[0], NameLength); } else { RtlCopyMemory(&FBI->FileName[0], &pName->Buffer[0], NameLength); @@ -1241,4 +1265,4 @@ Ext2IsDirectoryEmpty ( } return !!ext3_is_dir_empty(IrpContext, &Mcb->Inode); -}
\ No newline at end of file +} diff --git a/Ext3Fsd/ext3/generic.c b/Ext3Fsd/ext3/generic.c index d2565b0..2456a6a 100755 --- a/Ext3Fsd/ext3/generic.c +++ b/Ext3Fsd/ext3/generic.c @@ -1433,6 +1433,69 @@ Ext2AddEntry ( NTSTATUS +Ext2SetFileType ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_FCB Dcb, + IN PEXT2_MCB Mcb + ) +{ + struct inode *dir = Dcb->Inode; + struct buffer_head *bh = NULL; + struct ext3_dir_entry_2 *de; + struct inode *inode; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + BOOLEAN MainResourceAcquired = FALSE; + + if (!EXT3_HAS_INCOMPAT_FEATURE(dir->i_sb, EXT3_FEATURE_INCOMPAT_FILETYPE)) { + return STATUS_SUCCESS; + } + + if (!IsDirectory(Dcb)) { + return STATUS_NOT_A_DIRECTORY; + } + + ExAcquireResourceExclusiveLite(&Dcb->MainResource, TRUE); + MainResourceAcquired = TRUE; + + __try { + + Ext2ReferXcb(&Dcb->ReferenceCount); + + bh = ext3_find_entry(IrpContext, Mcb->de, &de); + if (!bh) + __leave; + + inode = &Mcb->Inode; + if (le32_to_cpu(de->inode) != inode->i_ino) + __leave; + + ext3_set_de_type(inode->i_sb, de, inode->i_mode); + mark_buffer_dirty(bh); + + //if (!inode->i_nlink) + // ext3_orphan_add(handle, inode); + + dir->i_ctime = dir->i_mtime = ext3_current_time(dir); + ext3_mark_inode_dirty(IrpContext, dir); + + Status = STATUS_SUCCESS; + + } __finally { + + Ext2DerefXcb(&Dcb->ReferenceCount); + + if (MainResourceAcquired) + ExReleaseResourceLite(&Dcb->MainResource); + + if (bh) + brelse(bh); + } + + return Status; +} + +NTSTATUS Ext2RemoveEntry ( IN PEXT2_IRP_CONTEXT IrpContext, IN PEXT2_VCB Vcb, diff --git a/Ext3Fsd/fsctl.c b/Ext3Fsd/fsctl.c index 3dca92c..941ac5b 100644 --- a/Ext3Fsd/fsctl.c +++ b/Ext3Fsd/fsctl.c @@ -1287,6 +1287,595 @@ Ext2GetRetrievalPointerBase ( return Status; } +NTSTATUS +Ext2InspectReparseData( + IN PREPARSE_DATA_BUFFER RDB, + IN ULONG InputBufferLength +) +{ + NTSTATUS Status = STATUS_SUCCESS; + + if (!RDB) { + Status = STATUS_INVALID_PARAMETER; + goto out; + } + + if (InputBufferLength < sizeof(REPARSE_DATA_BUFFER)) { + Status = STATUS_BUFFER_OVERFLOW; + goto out; + } + + if (InputBufferLength < RDB->ReparseDataLength) { + Status = STATUS_BUFFER_OVERFLOW; + goto out; + } + + if (RDB->ReparseTag != IO_REPARSE_TAG_SYMLINK) { + Status = STATUS_NOT_IMPLEMENTED; + goto out; + } + + if ((PUCHAR)RDB->SymbolicLinkReparseBuffer.PathBuffer + + RDB->SymbolicLinkReparseBuffer.SubstituteNameOffset + + RDB->SymbolicLinkReparseBuffer.SubstituteNameLength + > (PUCHAR)RDB + InputBufferLength ) { + Status = STATUS_BUFFER_OVERFLOW; + goto out; + } + + if ((PUCHAR)RDB->SymbolicLinkReparseBuffer.PathBuffer + + RDB->SymbolicLinkReparseBuffer.PrintNameOffset + + RDB->SymbolicLinkReparseBuffer.PrintNameLength + > (PUCHAR)RDB + InputBufferLength) { + Status = STATUS_BUFFER_OVERFLOW; + goto out; + } + + if (RDB->SymbolicLinkReparseBuffer.Flags != SYMLINK_FLAG_RELATIVE) { + Status = STATUS_NOT_IMPLEMENTED; + goto out; + } + +out: + return Status; +} + +VOID +Ext2InitializeReparseData(IN PREPARSE_DATA_BUFFER RDB, USHORT PathBufferLength) +{ + ASSERT(FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.SubstituteNameOffset) == + REPARSE_DATA_BUFFER_HEADER_SIZE); + RDB->ReparseTag = IO_REPARSE_TAG_SYMLINK; + RDB->ReparseDataLength = FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) - + FIELD_OFFSET(REPARSE_DATA_BUFFER, GenericReparseBuffer.DataBuffer) + + PathBufferLength * sizeof(WCHAR); + RDB->Reserved = 0; + RDB->SymbolicLinkReparseBuffer.SubstituteNameOffset = PathBufferLength; + RDB->SymbolicLinkReparseBuffer.SubstituteNameLength = PathBufferLength; + RDB->SymbolicLinkReparseBuffer.PrintNameOffset = 0; + RDB->SymbolicLinkReparseBuffer.PrintNameLength = PathBufferLength; + RDB->SymbolicLinkReparseBuffer.Flags = SYMLINK_FLAG_RELATIVE; + RtlZeroMemory(&RDB->SymbolicLinkReparseBuffer.PathBuffer, PathBufferLength * 2); +} + +NTSTATUS +Ext2ReadSymlink ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN PVOID Buffer, + IN ULONG Size, + OUT PULONG BytesRead + ) +{ + return Ext2ReadInode ( IrpContext, + Vcb, + Mcb, + 0, + Buffer, + Size, + FALSE, + BytesRead); +} + + + +NTSTATUS +Ext2GetReparsePoint (IN PEXT2_IRP_CONTEXT IrpContext) +{ + PIRP Irp = NULL; + PIO_STACK_LOCATION IrpSp; + PEXTENDED_IO_STACK_LOCATION EIrpSp; + + PDEVICE_OBJECT DeviceObject; + + PEXT2_VCB Vcb = NULL; + PEXT2_CCB Ccb = NULL; + PEXT2_MCB Mcb = NULL; + + NTSTATUS Status = STATUS_UNSUCCESSFUL; + BOOLEAN MainResourceAcquired = FALSE; + + PVOID OutputBuffer; + ULONG OutputBufferLength; + ULONG BytesRead = 0; + + PREPARSE_DATA_BUFFER RDB; + + UNICODE_STRING UniName; + OEM_STRING OemName; + + PCHAR OemNameBuffer = NULL; + int OemNameLength = 0, i; + + Ccb = IrpContext->Ccb; + ASSERT(Ccb != NULL); + ASSERT((Ccb->Identifier.Type == EXT2CCB) && + (Ccb->Identifier.Size == sizeof(EXT2_CCB))); + DeviceObject = IrpContext->DeviceObject; + Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; + Mcb = IrpContext->Fcb->Mcb; + Irp = IrpContext->Irp; + IrpSp = IoGetCurrentIrpStackLocation(Irp); + + __try { + + if (!Mcb || !IsInodeSymLink(&Mcb->Inode)) { + Status = STATUS_NOT_A_REPARSE_POINT; + __leave; + } + + OutputBuffer = (PVOID)Irp->AssociatedIrp.SystemBuffer; + OutputBufferLength = IrpSp->Parameters.FileSystemControl.OutputBufferLength; + + RDB = (PREPARSE_DATA_BUFFER)OutputBuffer; + if (!RDB) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + if (OutputBufferLength < sizeof(REPARSE_DATA_BUFFER)) { + Status = STATUS_BUFFER_OVERFLOW; + __leave; + } + + OemNameLength = (ULONG)Mcb->Inode.i_size; + if (OemNameLength > USHRT_MAX) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + OemName.Length = (USHORT)OemNameLength; + OemName.MaximumLength = OemNameLength + 1; + OemNameBuffer = OemName.Buffer = Ext2AllocatePool(NonPagedPool, + OemName.MaximumLength, + 'NL2E'); + if (!OemNameBuffer) { + Status = STATUS_INSUFFICIENT_RESOURCES; + __leave; + } + + Status = Ext2ReadSymlink(IrpContext, + Vcb, + Mcb, + OemNameBuffer, + OemNameLength, + &BytesRead + ); + OemName.Buffer[OemName.Length] = '\0'; + for (i = 0;i < OemName.Length;i++) { + if (OemName.Buffer[i] == '/') { + OemName.Buffer[i] = '\\'; + } + } + + if (OutputBufferLength - FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) > USHRT_MAX) { + UniName.Length = USHRT_MAX; + } else { + UniName.Length = (USHORT)OutputBufferLength - FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer); + } + UniName.MaximumLength = UniName.Length; + UniName.Length = (USHORT)Ext2OEMToUnicodeSize(Vcb, &OemName); + Irp->IoStatus.Information = FIELD_OFFSET(REPARSE_DATA_BUFFER, SymbolicLinkReparseBuffer.PathBuffer) + 2 * UniName.Length; + if (UniName.MaximumLength < 2*UniName.Length) { + Status = STATUS_BUFFER_TOO_SMALL; + __leave; + } + + Ext2InitializeReparseData(RDB, UniName.Length); + UniName.Buffer = RDB->SymbolicLinkReparseBuffer.PathBuffer; + /* + (PWCHAR)((PUCHAR)& + + RDB->SymbolicLinkReparseBuffer.SubstituteNameOffset); + */ + Ext2OEMToUnicode(Vcb, &UniName, &OemName); + RtlMoveMemory( (PUCHAR)RDB->SymbolicLinkReparseBuffer.PathBuffer + + RDB->SymbolicLinkReparseBuffer.SubstituteNameOffset, + UniName.Buffer, UniName.Length); + + Status = STATUS_SUCCESS; + + } __finally { + + if (OemNameBuffer) { + Ext2FreePool(OemNameBuffer, 'NL2E'); + } + + if (!AbnormalTermination()) { + if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { + Status = Ext2QueueRequest(IrpContext); + } else { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + } + + return Status; +} + + +NTSTATUS +Ext2WriteSymlink ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN PVOID Buffer, + IN ULONG Size, + OUT PULONG BytesWritten +) +{ + NTSTATUS Status = STATUS_SUCCESS; + PUCHAR Data = (PUCHAR)(&Mcb->Inode.i_block[0]); + + if (Size >= EXT2_LINKLEN_IN_INODE) { + + /* initialize inode i_block[] */ + if (0 == Mcb->Inode.i_blocks) { + memset(Data, 0, EXT2_LINKLEN_IN_INODE); + ClearFlag(Mcb->Inode.i_flags, EXT4_EXTENTS_FL); + Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); + } + + Status = Ext2WriteInode(IrpContext, Vcb, Mcb, + 0, Buffer, Size, + FALSE, BytesWritten); + if (!NT_SUCCESS(Status)) { + goto out; + } + + } else { + + /* free inode blocks before writing in line */ + if (Mcb->Inode.i_blocks) { + LARGE_INTEGER Zero = {0, 0}; + Ext2TruncateFile(IrpContext, Vcb, Mcb, &Zero); + } + + ClearFlag(Mcb->Inode.i_flags, EXT4_EXTENTS_FL); + memset(Data, 0, EXT2_LINKLEN_IN_INODE); + RtlCopyMemory(Data, Buffer, Size); + } + + Mcb->Inode.i_size = Size; + Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); + + if (BytesWritten) { + *BytesWritten = Size; + } + +out: + return Status; +} + + +NTSTATUS +Ext2SetReparsePoint (IN PEXT2_IRP_CONTEXT IrpContext) +{ + PIRP Irp = NULL; + PIO_STACK_LOCATION IrpSp; + + PDEVICE_OBJECT DeviceObject; + + PEXT2_VCB Vcb = NULL; + PEXT2_FCB Fcb = NULL; + PEXT2_CCB Ccb = NULL; + PEXT2_MCB Mcb = NULL; + + NTSTATUS Status = STATUS_UNSUCCESSFUL; + BOOLEAN bNewParentDcb = FALSE; + BOOLEAN MainResourceAcquired = FALSE; + + PVOID InputBuffer; + ULONG InputBufferLength; + ULONG BytesWritten = 0; + + PEXT2_FCB ParentDcb = NULL; /* Dcb of it's current parent */ + PEXT2_MCB ParentMcb = NULL; + + PREPARSE_DATA_BUFFER RDB; + + UNICODE_STRING UniName; + OEM_STRING OemName; + + PCHAR OemNameBuffer = NULL; + int OemNameLength = 0, i; + + + __try { + + Ccb = IrpContext->Ccb; + ASSERT(Ccb != NULL); + ASSERT((Ccb->Identifier.Type == EXT2CCB) && + (Ccb->Identifier.Size == sizeof(EXT2_CCB))); + DeviceObject = IrpContext->DeviceObject; + Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; + Fcb = IrpContext->Fcb; + Mcb = Fcb->Mcb; + Irp = IrpContext->Irp; + IrpSp = IoGetCurrentIrpStackLocation(Irp); + + ParentMcb = Mcb->Parent; + ParentDcb = ParentMcb->Fcb; + if (ParentDcb == NULL) { + ParentDcb = Ext2AllocateFcb(Vcb, ParentMcb); + if (ParentDcb) { + Ext2ReferXcb(&ParentDcb->ReferenceCount); + bNewParentDcb = TRUE; + } + } + + if (!Mcb) + __leave; + + if (!ExAcquireResourceSharedLite( + &Fcb->MainResource, + IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) )) { + Status = STATUS_PENDING; + __leave; + } + MainResourceAcquired = TRUE; + + InputBuffer = Irp->AssociatedIrp.SystemBuffer; + InputBufferLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; + + RDB = (PREPARSE_DATA_BUFFER)InputBuffer; + Status = Ext2InspectReparseData(RDB, InputBufferLength); + if (!NT_SUCCESS(Status)) { + __leave; + } + + UniName.Length = RDB->SymbolicLinkReparseBuffer.SubstituteNameLength; + UniName.MaximumLength = UniName.Length; + UniName.Buffer = + (PWCHAR)((PUCHAR)&RDB->SymbolicLinkReparseBuffer.PathBuffer + + RDB->SymbolicLinkReparseBuffer.SubstituteNameOffset); + + OemNameLength = Ext2UnicodeToOEMSize(Vcb, &UniName); + if (OemNameLength > USHRT_MAX) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + OemName.Length = (USHORT)OemNameLength; + OemName.MaximumLength = OemNameLength + 1; + OemNameBuffer = OemName.Buffer = Ext2AllocatePool(PagedPool, + OemName.MaximumLength, + 'NL2E'); + if (!OemNameBuffer) { + Status = STATUS_INSUFFICIENT_RESOURCES; + __leave; + } + + Ext2UnicodeToOEM(Vcb, &OemName, &UniName); + OemName.Buffer[OemName.Length] = '\0'; + for (i = 0;i < OemName.Length;i++) { + if (OemName.Buffer[i] == '\\') { + OemName.Buffer[i] = '/'; + } + } + + /* overwrite inode mode as type SYMLINK */ + Mcb->Inode.i_mode = S_IFLNK | S_IRWXUGO; + Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); + SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_REPARSE_POINT); + + Status = Ext2WriteSymlink(IrpContext, Vcb, Mcb, OemNameBuffer, + OemNameLength, &BytesWritten); + if (NT_SUCCESS(Status)) { + Status = Ext2SetFileType(IrpContext, Vcb, ParentDcb, Mcb); + } + + } __finally { + + if (MainResourceAcquired) { + ExReleaseResourceLite(&Fcb->MainResource); + } + + if (OemNameBuffer) { + Ext2FreePool(OemNameBuffer, 'NL2E'); + } + + if (NT_SUCCESS(Status)) { + Ext2NotifyReportChange( + IrpContext, + Vcb, + Mcb, + FILE_NOTIFY_CHANGE_ATTRIBUTES, + FILE_ACTION_MODIFIED ); + } + + if (bNewParentDcb) { + ASSERT(ParentDcb != NULL); + if (Ext2DerefXcb(&ParentDcb->ReferenceCount) == 0) { + Ext2FreeFcb(ParentDcb); + ParentDcb = NULL; + } else { + DEBUG(DL_RES, ( "Ext2SetRenameInfo: ParentDcb is resued by other threads.\n")); + } + } + + if (!AbnormalTermination()) { + if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { + Status = Ext2QueueRequest(IrpContext); + } else { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + } + + return Status; +} + +NTSTATUS +Ext2TruncateSymlink( + PEXT2_IRP_CONTEXT IrpContext, + PEXT2_VCB Vcb, + PEXT2_MCB Mcb, + ULONG Size + ) +{ + NTSTATUS status = STATUS_SUCCESS; + PUCHAR data = (PUCHAR)&Mcb->Inode.i_block; + ULONG len = (ULONG)Mcb->Inode.i_size; + LARGE_INTEGER NewSize; + + if (len < EXT2_LINKLEN_IN_INODE && !Mcb->Inode.i_blocks) { + + RtlZeroMemory(data + Size, EXT2_LINKLEN_IN_INODE - Size); + Mcb->Inode.i_size = Size; + Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); + + } else { + NewSize.QuadPart = Size; + status = Ext2TruncateFile(IrpContext, Vcb, Mcb, &NewSize); + if (!NT_SUCCESS(status)) { + goto out; + } + } + +out: + return status; +} + + +/* FIXME: We can only handle one reparse point right now. */ +NTSTATUS +Ext2DeleteReparsePoint (IN PEXT2_IRP_CONTEXT IrpContext) +{ + PIRP Irp = NULL; + + PDEVICE_OBJECT DeviceObject; + + PEXT2_VCB Vcb = NULL; + PEXT2_FCB Fcb = NULL; + PEXT2_CCB Ccb = NULL; + PEXT2_MCB Mcb = NULL; + + NTSTATUS Status = STATUS_UNSUCCESSFUL; + BOOLEAN bNewParentDcb = FALSE; + BOOLEAN bFcbAllocated = FALSE; + BOOLEAN MainResourceAcquired = FALSE; + + PEXT2_FCB ParentDcb = NULL; /* Dcb of it's current parent */ + PEXT2_MCB ParentMcb = NULL; + + __try { + + Ccb = IrpContext->Ccb; + ASSERT(Ccb != NULL); + ASSERT((Ccb->Identifier.Type == EXT2CCB) && + (Ccb->Identifier.Size == sizeof(EXT2_CCB))); + DeviceObject = IrpContext->DeviceObject; + Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; + Mcb = IrpContext->Fcb->Mcb; + Irp = IrpContext->Irp; + + ParentMcb = Mcb->Parent; + ParentDcb = ParentMcb->Fcb; + if (ParentDcb == NULL) { + ParentDcb = Ext2AllocateFcb(Vcb, ParentMcb); + if (ParentDcb) { + Ext2ReferXcb(&ParentDcb->ReferenceCount); + bNewParentDcb = TRUE; + } + } + + if (!Mcb) { + Status = STATUS_NOT_A_REPARSE_POINT; + __leave; + } + + Fcb = Ext2AllocateFcb (Vcb, Mcb); + if (Fcb) { + bFcbAllocated = TRUE; + } else { + Status = STATUS_INSUFFICIENT_RESOURCES; + __leave; + } + Ext2ReferXcb(&Fcb->ReferenceCount); + + if (!ExAcquireResourceSharedLite( + &Fcb->MainResource, + IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) )) { + Status = STATUS_PENDING; + __leave; + } + MainResourceAcquired = TRUE; + + Status = Ext2TruncateSymlink(IrpContext, Vcb, Mcb, 0); + if (!NT_SUCCESS(Status)) { + __leave; + } + if (IsFlagOn(SUPER_BLOCK->s_feature_incompat, EXT4_FEATURE_INCOMPAT_EXTENTS)) { + SetFlag(Mcb->Inode.i_flags, EXT4_EXTENTS_FL); + } + ClearFlag(Mcb->FileAttr, FILE_ATTRIBUTE_REPARSE_POINT); + ClearFlag(Mcb->Inode.i_flags, S_IFLNK); + Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); + if (NT_SUCCESS(Status)) { + Status = Ext2SetFileType(IrpContext, Vcb, ParentDcb, Mcb); + } + + } __finally { + + if (MainResourceAcquired) { + ExReleaseResourceLite(&Fcb->MainResource); + } + + if (NT_SUCCESS(Status)) { + Ext2NotifyReportChange( + IrpContext, + Vcb, + Mcb, + FILE_NOTIFY_CHANGE_ATTRIBUTES, + FILE_ACTION_MODIFIED ); + + } + + if (bNewParentDcb) { + ASSERT(ParentDcb != NULL); + if (Ext2DerefXcb(&ParentDcb->ReferenceCount) == 0) { + Ext2FreeFcb(ParentDcb); + ParentDcb = NULL; + } else { + DEBUG(DL_RES, ( "Ext2DeleteReparsePoint: ParentDcb is resued.\n")); + } + } + + if (!AbnormalTermination()) { + if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { + Status = Ext2QueueRequest(IrpContext); + } else { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + + if (bFcbAllocated) { + if (Ext2DerefXcb(&Fcb->ReferenceCount) == 0) { + Ext2FreeFcb(Fcb); + } + } + } + + return Status; +} NTSTATUS Ext2UserFsRequest (IN PEXT2_IRP_CONTEXT IrpContext) @@ -1302,7 +1891,6 @@ Ext2UserFsRequest (IN PEXT2_IRP_CONTEXT IrpContext) (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); Irp = IrpContext->Irp; - IoStackLocation = IoGetCurrentIrpStackLocation(Irp); #ifndef _GNU_NTIFS_ @@ -1315,6 +1903,18 @@ Ext2UserFsRequest (IN PEXT2_IRP_CONTEXT IrpContext) switch (FsControlCode) { + case FSCTL_GET_REPARSE_POINT: + Status = Ext2GetReparsePoint(IrpContext); + break; + + case FSCTL_SET_REPARSE_POINT: + Status = Ext2SetReparsePoint(IrpContext); + break; + + case FSCTL_DELETE_REPARSE_POINT: + Status = Ext2DeleteReparsePoint(IrpContext); + break; + case FSCTL_LOCK_VOLUME: Status = Ext2LockVolume(IrpContext); break; diff --git a/Ext3Fsd/include/ext2fs.h b/Ext3Fsd/include/ext2fs.h index 2b41390..b4d4084 100755 --- a/Ext3Fsd/include/ext2fs.h +++ b/Ext3Fsd/include/ext2fs.h @@ -805,7 +805,7 @@ struct _EXT2_MCB { // Link List Info PEXT2_MCB Parent; // Parent - PEXT2_MCB Next; // Brothers + PEXT2_MCB Next; // Siblings union { PEXT2_MCB Child; // Children Mcb nodes @@ -929,7 +929,7 @@ typedef struct _EXT2_CCB { #define CCB_FROM_POOL 0x00000001 #define CCB_VOLUME_DASD_PURGE 0x00000002 #define CCB_LAST_WRITE_UPDATED 0x00000004 - +#define CCB_OPEN_REPARSE_POINT 0x00000008 #define CCB_DELETE_ON_CLOSE 0x00000010 #define CCB_ALLOW_EXTENDED_DASD_IO 0x80000000 @@ -1248,7 +1248,7 @@ Ext2FollowLink ( IN PEXT2_VCB Vcb, IN PEXT2_MCB Parent, IN PEXT2_MCB Mcb, - IN USHORT Linkdep + IN ULONG Linkdep ); NTSTATUS @@ -1267,6 +1267,9 @@ Ext2IsSpecialSystemFile( IN BOOLEAN bDirectory ); +#define EXT2_LOOKUP_FLAG_MASK (0xFF00000) +#define EXT2_LOOKUP_NOT_FOLLOW (0x8000000) + NTSTATUS Ext2LookupFile ( IN PEXT2_IRP_CONTEXT IrpContext, @@ -1274,7 +1277,7 @@ Ext2LookupFile ( IN PUNICODE_STRING FullName, IN PEXT2_MCB Parent, OUT PEXT2_MCB * Ext2Mcb, - IN USHORT Linkdep + IN ULONG Linkdep ); NTSTATUS @@ -1859,6 +1862,14 @@ Ext2AddEntry ( ); NTSTATUS +Ext2SetFileType ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_FCB Dcb, + IN PEXT2_MCB Mcb +); + +NTSTATUS Ext2RemoveEntry ( IN PEXT2_IRP_CONTEXT IrpContext, IN PEXT2_VCB Vcb, @@ -2139,6 +2150,34 @@ Ext2Flush (IN PEXT2_IRP_CONTEXT IrpContext); // Fsctl.c // +NTSTATUS +Ext2ReadSymlink ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN PVOID Buffer, + IN ULONG Size, + OUT PULONG BytesRead + ); + +NTSTATUS +Ext2WriteSymlink ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN PVOID Buffer, + IN ULONG Size, + OUT PULONG BytesWritten +); + +NTSTATUS +Ext2TruncateSymlink( + PEXT2_IRP_CONTEXT IrpContext, + PEXT2_VCB Vcb, + PEXT2_MCB Mcb, + ULONG Size +); + // // MountPoint process workitem // @@ -2375,7 +2414,7 @@ VOID Ext2RemoveFcb(PEXT2_VCB Vcb, PEXT2_FCB Fcb); PEXT2_CCB -Ext2AllocateCcb (PEXT2_MCB SymLink); +Ext2AllocateCcb (ULONG Flags, PEXT2_MCB SymLink); VOID Ext2FreeMcb ( diff --git a/Ext3Fsd/memory.c b/Ext3Fsd/memory.c index 9d047b2..6f7b5b5 100755 --- a/Ext3Fsd/memory.c +++ b/Ext3Fsd/memory.c @@ -138,8 +138,6 @@ Ext2AllocateFcb ( { PEXT2_FCB Fcb; - ASSERT(!IsMcbSymLink(Mcb)); - Fcb = (PEXT2_FCB) ExAllocateFromNPagedLookasideList( &(Ext2Global->Ext2FcbLookasideList)); @@ -216,7 +214,7 @@ Ext2FreeFcb (IN PEXT2_FCB Fcb) #endif if ((Fcb->Mcb->Identifier.Type == EXT2MCB) && - (Fcb->Mcb->Identifier.Size == sizeof(EXT2_MCB))) { + (Fcb->Mcb->Identifier.Size == sizeof(EXT2_MCB))) { ASSERT (Fcb->Mcb->Fcb == Fcb); if (IsMcbSpecialFile(Fcb->Mcb) || IsFileDeleted(Fcb->Mcb)) { @@ -283,7 +281,7 @@ Ext2RemoveFcb(PEXT2_VCB Vcb, PEXT2_FCB Fcb) } PEXT2_CCB -Ext2AllocateCcb (PEXT2_MCB SymLink) +Ext2AllocateCcb (ULONG Flags, PEXT2_MCB SymLink) { PEXT2_CCB Ccb; @@ -299,6 +297,7 @@ Ext2AllocateCcb (PEXT2_MCB SymLink) Ccb->Identifier.Type = EXT2CCB; Ccb->Identifier.Size = sizeof(EXT2_CCB); + Ccb->Flags = Flags; Ccb->SymLink = SymLink; if (SymLink) { diff --git a/Ext3Fsd/read.c b/Ext3Fsd/read.c index ae08722..e727424 100755 --- a/Ext3Fsd/read.c +++ b/Ext3Fsd/read.c @@ -347,11 +347,9 @@ Ext2ReadInode ( /* handle fast symlinks */ - if (S_ISLNK(Mcb->Inode.i_mode) && - Mcb->Inode.i_size < EXT2_LINKLEN_IN_INODE) { + if (S_ISLNK(Mcb->Inode.i_mode) && 0 == Mcb->Inode.i_blocks) { PUCHAR Data = (PUCHAR) (&Mcb->Inode.i_block[0]); - if (!Buffer) { Status = STATUS_INSUFFICIENT_RESOURCES; __leave; @@ -534,6 +532,11 @@ Ext2ReadFile(IN PEXT2_IRP_CONTEXT IrpContext) DEBUG(DL_INF, ("Ext2ReadFile: reading %wZ Off=%I64xh Len=%xh Paging=%xh Nocache=%xh\n", &Fcb->Mcb->ShortName, ByteOffset.QuadPart, Length, PagingIo, Nocache)); + if (IsSpecialFile(Fcb) || IsInodeSymLink(Fcb->Inode) ) { + Status = STATUS_INVALID_DEVICE_REQUEST; + __leave; + } + if ((IsSymLink(Fcb) && IsFileDeleted(Fcb->Mcb->Target)) || IsFileDeleted(Fcb->Mcb)) { Status = STATUS_FILE_DELETED; diff --git a/Ext3Fsd/volinfo.c b/Ext3Fsd/volinfo.c index b0bcb9b..65ed938 100755 --- a/Ext3Fsd/volinfo.c +++ b/Ext3Fsd/volinfo.c @@ -187,7 +187,8 @@ Ext2QueryVolumeInformation (IN PEXT2_IRP_CONTEXT IrpContext) FsAttrInfo = (PFILE_FS_ATTRIBUTE_INFORMATION) Buffer; FsAttrInfo->FileSystemAttributes = FILE_SUPPORTS_HARD_LINKS | - FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES; + FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | + FILE_SUPPORTS_REPARSE_POINTS; if (IsVcbReadOnly(Vcb)) { FsAttrInfo->FileSystemAttributes |= FILE_READ_ONLY_VOLUME; } diff --git a/Ext3Fsd/write.c b/Ext3Fsd/write.c index 721b911..5afaef5 100755 --- a/Ext3Fsd/write.c +++ b/Ext3Fsd/write.c @@ -651,7 +651,7 @@ Ext2WriteInode ( IN ULONG Size, IN BOOLEAN bDirectIo, OUT PULONG BytesWritten -) + ) { PEXT2_EXTENT Chain = NULL; NTSTATUS Status = STATUS_UNSUCCESSFUL; @@ -668,7 +668,7 @@ Ext2WriteInode ( Mcb, Offset, Size, - IsMcbDirectory(Mcb) ? FALSE : TRUE, + S_ISDIR(Mcb->Inode.i_mode) ? FALSE : TRUE, &Chain ); @@ -735,6 +735,7 @@ Ext2WriteInode ( return Status; } + NTSTATUS Ext2WriteFile(IN PEXT2_IRP_CONTEXT IrpContext) { @@ -806,7 +807,7 @@ Ext2WriteFile(IN PEXT2_IRP_CONTEXT IrpContext) DEBUG(DL_INF, ("Ext2WriteFile: %wZ Offset=%I64xh Length=%xh Paging=%xh Nocache=%xh\n", &Fcb->Mcb->ShortName, ByteOffset.QuadPart, Length, PagingIo, Nocache)); - if (IsSpecialFile(Fcb)) { + if (IsSpecialFile(Fcb) || IsInodeSymLink(Fcb->Inode) ) { Status = STATUS_INVALID_DEVICE_REQUEST; __leave; } |