From c04a4136962c25a72bbca3f0d1e5c299486db4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bo=20Brant=C3=A9n?= Date: Sat, 25 Jan 2020 01:07:05 +0100 Subject: [PATCH] suport for metadata checksums --- Ext4Fsd/Ext4Fsd.vcxproj | 1 + Ext4Fsd/Ext4Fsd.vcxproj.filters | 3 + Ext4Fsd/ext3/generic.c | 6138 ++++++++++++------------ Ext4Fsd/ext3/htree.c | 4 +- Ext4Fsd/ext3/recover.c | 1 - Ext4Fsd/ext4/SOURCES | 4 +- Ext4Fsd/ext4/ext4_csum.c | 757 +++ Ext4Fsd/ext4/ext4_jbd2.c | 143 +- Ext4Fsd/fsctl.c | 5758 +++++++++++------------ Ext4Fsd/include/ext2fs.h | 7 +- Ext4Fsd/include/linux/ext3_jbd.h | 1 - Ext4Fsd/include/linux/ext4.h | 3516 +++++++++++++- Ext4Fsd/include/linux/ext4_xattr.h | 2 +- Ext4Fsd/memory.c | 7048 ++++++++++++++-------------- 14 files changed, 13681 insertions(+), 9702 deletions(-) create mode 100644 Ext4Fsd/ext4/ext4_csum.c diff --git a/Ext4Fsd/Ext4Fsd.vcxproj b/Ext4Fsd/Ext4Fsd.vcxproj index 10d9344..f7b1a48 100644 --- a/Ext4Fsd/Ext4Fsd.vcxproj +++ b/Ext4Fsd/Ext4Fsd.vcxproj @@ -208,6 +208,7 @@ + diff --git a/Ext4Fsd/Ext4Fsd.vcxproj.filters b/Ext4Fsd/Ext4Fsd.vcxproj.filters index f110844..bbee764 100644 --- a/Ext4Fsd/Ext4Fsd.vcxproj.filters +++ b/Ext4Fsd/Ext4Fsd.vcxproj.filters @@ -274,6 +274,9 @@ Source Files\nls + + Source Files\ext4 + diff --git a/Ext4Fsd/ext3/generic.c b/Ext4Fsd/ext3/generic.c index b1bbe71..1ca0d95 100644 --- a/Ext4Fsd/ext3/generic.c +++ b/Ext4Fsd/ext3/generic.c @@ -1,3102 +1,3036 @@ -/* - * COPYRIGHT: See COPYRIGHT.TXT - * PROJECT: Ext2 File System Driver for WinNT/2K/XP - * FILE: generic.c - * PROGRAMMER: Matt Wu - * HOMEPAGE: http://www.ext2fsd.com - * UPDATE HISTORY: - */ - -/* INCLUDES *****************************************************************/ - -#include "ext2fs.h" -#include "linux\ext4.h" - -/* GLOBALS ***************************************************************/ - -extern PEXT2_GLOBAL Ext2Global; - -/* DEFINITIONS *************************************************************/ - - -/* FUNCTIONS ***************************************************************/ - -NTSTATUS -Ext2LoadSuper(IN PEXT2_VCB Vcb, - IN BOOLEAN bVerify, - OUT PEXT2_SUPER_BLOCK * Sb) -{ - NTSTATUS Status; - PEXT2_SUPER_BLOCK Ext2Sb = NULL; - - Ext2Sb = (PEXT2_SUPER_BLOCK) - Ext2AllocatePool( - PagedPool, - SUPER_BLOCK_SIZE, - EXT2_SB_MAGIC - ); - if (!Ext2Sb) { - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - - Status = Ext2ReadDisk( - Vcb, - (ULONGLONG) SUPER_BLOCK_OFFSET, - SUPER_BLOCK_SIZE, - (PVOID) Ext2Sb, - bVerify ); - - if (!NT_SUCCESS(Status)) { - Ext2FreePool(Ext2Sb, EXT2_SB_MAGIC); - Ext2Sb = NULL; - } - -errorout: - - *Sb = Ext2Sb; - return Status; -} - - -BOOLEAN -Ext2SaveSuper( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb -) -{ - LONGLONG offset; - BOOLEAN rc; - - offset = (LONGLONG) SUPER_BLOCK_OFFSET; - rc = Ext2SaveBuffer( IrpContext, - Vcb, - offset, - SUPER_BLOCK_SIZE, - Vcb->SuperBlock - ); - - if (IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) { - Ext2StartFloppyFlushDpc(Vcb, NULL, NULL); - } - - return rc; -} - - -BOOLEAN -Ext2RefreshSuper ( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb -) -{ - LONGLONG offset; - IO_STATUS_BLOCK iosb; - - offset = (LONGLONG) SUPER_BLOCK_OFFSET; - if (!CcCopyRead( - Vcb->Volume, - (PLARGE_INTEGER)&offset, - SUPER_BLOCK_SIZE, - TRUE, - (PVOID)Vcb->SuperBlock, - &iosb )) { - return FALSE; - } - - if (!NT_SUCCESS(iosb.Status)) { - return FALSE; - } - - /* reload root inode */ - if (Vcb->McbTree) { - - if (!Ext2LoadInode(Vcb, &Vcb->McbTree->Inode)) - return FALSE; - - /* initializeroot node */ - Vcb->McbTree->CreationTime = Ext2NtTime(Vcb->McbTree->Inode.i_ctime); - Vcb->McbTree->LastAccessTime = Ext2NtTime(Vcb->McbTree->Inode.i_atime); - Vcb->McbTree->LastWriteTime = Ext2NtTime(Vcb->McbTree->Inode.i_mtime); - Vcb->McbTree->ChangeTime = Ext2NtTime(Vcb->McbTree->Inode.i_mtime); - } - - return TRUE; -} - -VOID -Ext2DropGroupBH(IN PEXT2_VCB Vcb) -{ - struct ext3_sb_info *sbi = &Vcb->sbi; - unsigned long i; - - if (NULL == Vcb->sbi.s_gd) { - return; - } - - for (i = 0; i < Vcb->sbi.s_gdb_count; i++) { - if (Vcb->sbi.s_gd[i].bh) { - fini_bh(&sbi->s_gd[i].bh); - Vcb->sbi.s_gd[i].bh = NULL; - } - } -} - -VOID -Ext2PutGroup(IN PEXT2_VCB Vcb) -{ - struct ext3_sb_info *sbi = &Vcb->sbi; - unsigned long i; - - - if (NULL == Vcb->sbi.s_gd) { - return; - } - - Ext2DropGroupBH(Vcb); - - kfree(Vcb->sbi.s_gd); - Vcb->sbi.s_gd = NULL; - - ClearFlag(Vcb->Flags, VCB_GD_LOADED); -} - - -BOOLEAN -Ext2LoadGroupBH(IN PEXT2_VCB Vcb) -{ - struct super_block *sb = &Vcb->sb; - struct ext3_sb_info *sbi = &Vcb->sbi; - unsigned long i; - BOOLEAN rc = FALSE; - - __try { - - ExAcquireResourceExclusiveLite(&Vcb->sbi.s_gd_lock, TRUE); - ASSERT (NULL != sbi->s_gd); - - for (i = 0; i < sbi->s_gdb_count; i++) { - ASSERT (sbi->s_gd[i].block); - if (sbi->s_gd[i].bh) - continue; - sbi->s_gd[i].bh = sb_getblk(sb, sbi->s_gd[i].block); - if (!sbi->s_gd[i].bh) { - DEBUG(DL_ERR, ("Ext2LoadGroupBH: can't read group descriptor %d\n", i)); - DbgBreak(); - __leave; - } - sbi->s_gd[i].gd = (struct ext4_group_desc *)sbi->s_gd[i].bh->b_data; - } - - rc = TRUE; - - } __finally { - - ExReleaseResourceLite(&Vcb->sbi.s_gd_lock); - } - - return rc; -} - - -BOOLEAN -Ext2LoadGroup(IN PEXT2_VCB Vcb) -{ - struct super_block *sb = &Vcb->sb; - struct ext3_sb_info *sbi = &Vcb->sbi; - ext3_fsblk_t sb_block = 1; - unsigned long i; - BOOLEAN rc = FALSE; - - __try { - - ExAcquireResourceExclusiveLite(&Vcb->sbi.s_gd_lock, TRUE); - - if (NULL == sbi->s_gd) { - sbi->s_gd = kzalloc(sbi->s_gdb_count * sizeof(struct ext3_gd), - GFP_KERNEL); - } - if (sbi->s_gd == NULL) { - DEBUG(DL_ERR, ("Ext2LoadGroup: not enough memory.\n")); - __leave; - } - - if (BLOCK_SIZE != EXT3_MIN_BLOCK_SIZE) { - sb_block = EXT4_MIN_BLOCK_SIZE / BLOCK_SIZE; - } - - for (i = 0; i < sbi->s_gdb_count; i++) { - sbi->s_gd[i].block = descriptor_loc(sb, sb_block, i); - if (!sbi->s_gd[i].block) { - DEBUG(DL_ERR, ("Ext2LoadGroup: can't locate group descriptor %d\n", i)); - __leave; - } - } - - if (!Ext2LoadGroupBH(Vcb)) { - DEBUG(DL_ERR, ("Ext2LoadGroup: Failed to load group descriptions !\n")); - __leave; - } - - if (!ext4_check_descriptors(sb)) { - DbgBreak(); - DEBUG(DL_ERR, ("Ext2LoadGroup: group descriptors corrupted !\n")); - __leave; - } - - SetFlag(Vcb->Flags, VCB_GD_LOADED); - rc = TRUE; - - } __finally { - - if (!rc) - Ext2PutGroup(Vcb); - - ExReleaseResourceLite(&Vcb->sbi.s_gd_lock); - } - - return rc; -} - -VOID -Ext2DropBH(IN PEXT2_VCB Vcb) -{ - struct ext3_sb_info *sbi = &Vcb->sbi; - - /* do nothing if Vcb is not initialized yet */ - if (!IsFlagOn(Vcb->Flags, VCB_INITIALIZED)) - return; - - __try { - - /* acquire bd lock to avoid bh creation */ - ExAcquireResourceExclusiveLite(&Vcb->bd.bd_bh_lock, TRUE); - - SetFlag(Vcb->Flags, VCB_BEING_DROPPED); - Ext2DropGroupBH(Vcb); - - while (!IsListEmpty(&Vcb->bd.bd_bh_free)) { - struct buffer_head *bh; - PLIST_ENTRY l; - l = RemoveHeadList(&Vcb->bd.bd_bh_free); - bh = CONTAINING_RECORD(l, struct buffer_head, b_link); - InitializeListHead(&bh->b_link); - if (0 == atomic_read(&bh->b_count)) { - buffer_head_remove(&Vcb->bd, bh); - free_buffer_head(bh); - } - } - - } __finally { - ExReleaseResourceLite(&Vcb->bd.bd_bh_lock); - } - - ClearFlag(Vcb->Flags, VCB_BEING_DROPPED); -} - - -VOID -Ext2FlushRange(IN PEXT2_VCB Vcb, LARGE_INTEGER s, LARGE_INTEGER e) -{ - ULONG len; - - if (e.QuadPart <= s.QuadPart) - return; - - /* loop per 2G */ - while (s.QuadPart < e.QuadPart) { - if (e.QuadPart > s.QuadPart + 1024 * 1024 * 1024) { - len = 1024 * 1024 * 1024; - } else { - len = (ULONG) (e.QuadPart - s.QuadPart); - } - CcFlushCache(&Vcb->SectionObject, &s, len, NULL); - s.QuadPart += len; - } -} - -NTSTATUS -Ext2FlushVcb(IN PEXT2_VCB Vcb) -{ - LARGE_INTEGER s = {0}, o; - struct ext3_sb_info *sbi = &Vcb->sbi; - struct rb_node *node; - struct buffer_head *bh; - - if (!IsFlagOn(Vcb->Flags, VCB_GD_LOADED)) { - CcFlushCache(&Vcb->SectionObject, NULL, 0, NULL); - goto errorout; - } - - ASSERT(ExIsResourceAcquiredExclusiveLite(&Vcb->MainResource)); - - __try { - - /* acqurie gd block */ - ExAcquireResourceExclusiveLite(&Vcb->sbi.s_gd_lock, TRUE); - - /* acquire bd lock to avoid bh creation */ - ExAcquireResourceExclusiveLite(&Vcb->bd.bd_bh_lock, TRUE); - - /* drop unused bh */ - Ext2DropBH(Vcb); - - /* flush volume with all outstanding bh skipped */ - - node = rb_first(&Vcb->bd.bd_bh_root); - while (node) { - - bh = container_of(node, struct buffer_head, b_rb_node); - node = rb_next(node); - - o.QuadPart = bh->b_blocknr << BLOCK_BITS; - ASSERT(o.QuadPart >= s.QuadPart); - - if (o.QuadPart == s.QuadPart) { - s.QuadPart = s.QuadPart + bh->b_size; - continue; - } - - if (o.QuadPart > s.QuadPart) { - Ext2FlushRange(Vcb, s, o); - s.QuadPart = (bh->b_blocknr << BLOCK_BITS) + bh->b_size; - continue; - } - } - - o = Vcb->PartitionInformation.PartitionLength; - Ext2FlushRange(Vcb, s, o); - - } __finally { - - ExReleaseResourceLite(&Vcb->bd.bd_bh_lock); - ExReleaseResourceLite(&Vcb->sbi.s_gd_lock); - } - -errorout: - return STATUS_SUCCESS; -} - - -BOOLEAN -Ext2SaveGroup( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN ULONG Group -) -{ - struct ext4_group_desc *gd; - struct buffer_head *gb = NULL; - unsigned long i; - - gd = ext4_get_group_desc(&Vcb->sb, Group, &gb); - if (!gd) - return 0; - - gd->bg_checksum = ext4_group_desc_csum(&Vcb->sbi, Group, gd); - mark_buffer_dirty(gb); - fini_bh(&gb); - - return TRUE; -} - - -BOOLEAN -Ext2RefreshGroup( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb -) -{ - return TRUE; -} - -BOOLEAN -Ext2GetInodeLba ( - IN PEXT2_VCB Vcb, - IN ULONG inode, - OUT PLONGLONG offset -) -{ - PEXT2_GROUP_DESC gd; - struct buffer_head *bh = NULL; - ext4_fsblk_t loc; - int group; - - if (inode < 1 || inode > INODES_COUNT) { - DEBUG(DL_ERR, ( "Ext2GetInodeLba: Inode value %xh is invalid.\n",inode)); - *offset = 0; - return FALSE; - } - - group = (inode - 1) / INODES_PER_GROUP ; - gd = ext4_get_group_desc(&Vcb->sb, group, &bh); - if (!bh) { - *offset = 0; - DbgBreak(); - return FALSE; - } - loc = (LONGLONG)ext4_inode_table(&Vcb->sb, gd); - loc = loc << BLOCK_BITS; - loc = loc + ((inode - 1) % INODES_PER_GROUP) * Vcb->InodeSize; - - *offset = loc; - __brelse(bh); - - return TRUE; -} - -void Ext2DecodeInode(struct inode *dst, struct ext3_inode *src) -{ - dst->i_mode = src->i_mode; - dst->i_flags = src->i_flags; - dst->i_uid = src->i_uid; - dst->i_gid = src->i_gid; - dst->i_nlink = src->i_links_count; - dst->i_generation = src->i_generation; - dst->i_size = src->i_size; - if (S_ISREG(src->i_mode)) { - dst->i_size |= (loff_t)src->i_size_high << 32; - } - dst->i_file_acl = src->i_file_acl_lo; - dst->i_file_acl |= (ext4_fsblk_t)src->osd2.linux2.l_i_file_acl_high << 32; - dst->i_atime = src->i_atime; - dst->i_ctime = src->i_ctime; - dst->i_mtime = src->i_mtime; - dst->i_dtime = src->i_dtime; - dst->i_blocks = ext3_inode_blocks(src, dst); - memcpy(&dst->i_block[0], &src->i_block[0], sizeof(__u32) * 15); - if (EXT3_HAS_RO_COMPAT_FEATURE(dst->i_sb, - EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)) - dst->i_extra_isize = src->i_extra_isize; - else - dst->i_extra_isize = 0; -} - -void Ext2EncodeInode(struct ext3_inode *dst, struct inode *src) -{ - dst->i_mode = src->i_mode; - dst->i_flags = src->i_flags; - dst->i_uid = src->i_uid; - dst->i_gid = src->i_gid; - dst->i_links_count = src->i_nlink; - dst->i_generation = src->i_generation; - dst->i_size = (__u32)src->i_size; - if (S_ISREG(src->i_mode)) { - dst->i_size_high = (__u32)(src->i_size >> 32); - } - dst->i_file_acl_lo = (__u32)src->i_file_acl; - dst->osd2.linux2.l_i_file_acl_high |= (__u16)(src->i_file_acl >> 32); - dst->i_atime = src->i_atime; - dst->i_ctime = src->i_ctime; - dst->i_mtime = src->i_mtime; - dst->i_dtime = src->i_dtime; - dst->i_extra_isize = src->i_extra_isize; - ASSERT(src->i_sb); - ext3_inode_blocks_set(dst, src); - memcpy(&dst->i_block[0], &src->i_block[0], sizeof(__u32) * 15); - if (EXT3_HAS_RO_COMPAT_FEATURE(src->i_sb, - EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)) - dst->i_extra_isize = src->i_extra_isize; -} - - -BOOLEAN -Ext2LoadInode (IN PEXT2_VCB Vcb, - IN struct inode *Inode) -{ - struct ext3_inode ext3i = {0}; - LONGLONG offset; - - if (!Ext2GetInodeLba(Vcb, Inode->i_ino, &offset)) { - DEBUG(DL_ERR, ("Ext2LoadInode: failed inode %u.\n", Inode->i_ino)); - return FALSE; - } - - if (!Ext2LoadBuffer(NULL, Vcb, offset, sizeof(ext3i), &ext3i)) { - return FALSE; - } - - Ext2DecodeInode(Inode, &ext3i); - - return TRUE; -} - - -BOOLEAN -Ext2ClearInode ( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN ULONG Inode) -{ - LONGLONG Offset = 0; - BOOLEAN rc; - - rc = Ext2GetInodeLba(Vcb, Inode, &Offset); - if (!rc) { - DEBUG(DL_ERR, ( "Ext2SaveInode: failed inode %u.\n", Inode)); - goto errorout; - } - - rc = Ext2ZeroBuffer(IrpContext, Vcb, Offset, Vcb->InodeSize); - -errorout: - - return rc; -} - -BOOLEAN -Ext2SaveInode ( IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN struct inode *Inode) -{ - struct ext3_inode ext3i = {0}; - - LONGLONG Offset = 0; - ULONG InodeSize = sizeof(ext3i); - BOOLEAN rc = 0; - - DEBUG(DL_INF, ( "Ext2SaveInode: Saving Inode %xh: Mode=%xh Size=%xh\n", - Inode->i_ino, Inode->i_mode, Inode->i_size)); - rc = Ext2GetInodeLba(Vcb, Inode->i_ino, &Offset); - if (!rc) { - DEBUG(DL_ERR, ( "Ext2SaveInode: failed inode %u.\n", Inode->i_ino)); - goto errorout; - } - - rc = Ext2LoadBuffer(NULL, Vcb, Offset, InodeSize, &ext3i); - if (!rc) { - DEBUG(DL_ERR, ( "Ext2SaveInode: failed reading inode %u.\n", Inode->i_ino)); - goto errorout;; - } - - Ext2EncodeInode(&ext3i, Inode); - if (InodeSize > Vcb->InodeSize) - InodeSize = Vcb->InodeSize; - rc = Ext2SaveBuffer(IrpContext, Vcb, Offset, InodeSize, &ext3i); - - if (rc && IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) { - Ext2StartFloppyFlushDpc(Vcb, NULL, NULL); - } - -errorout: - return rc; -} - -BOOLEAN -Ext2LoadInodeXattr(IN PEXT2_VCB Vcb, - IN struct inode *Inode, - IN PEXT2_INODE InodeXattr) -{ - IO_STATUS_BLOCK IoStatus; - LONGLONG Offset; - - if (!Ext2GetInodeLba(Vcb, Inode->i_ino, &Offset)) { - DEBUG(DL_ERR, ("Ext2LoadRawInode: error get inode(%xh)'s addr.\n", Inode->i_ino)); - return FALSE; - } - - if (!CcCopyRead( - Vcb->Volume, - (PLARGE_INTEGER)&Offset, - Vcb->InodeSize, - PIN_WAIT, - (PVOID)InodeXattr, - &IoStatus)) { - return FALSE; - } - - if (!NT_SUCCESS(IoStatus.Status)) { - return FALSE; - } - - Ext2EncodeInode(InodeXattr, Inode); - return TRUE; -} - -BOOLEAN -Ext2SaveInodeXattr(IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN struct inode *Inode, - IN PEXT2_INODE InodeXattr) -{ - IO_STATUS_BLOCK IoStatus; - LONGLONG Offset = 0; - ULONG InodeSize = Vcb->InodeSize; - BOOLEAN rc = 0; - - /* There is no way to put EA information in such a small inode */ - if (InodeSize == EXT2_GOOD_OLD_INODE_SIZE) - return FALSE; - - DEBUG(DL_INF, ("Ext2SaveInodeXattr: Saving Inode %xh: Mode=%xh Size=%xh\n", - Inode->i_ino, Inode->i_mode, Inode->i_size)); - rc = Ext2GetInodeLba(Vcb, Inode->i_ino, &Offset); - if (!rc) { - DEBUG(DL_ERR, ("Ext2SaveInodeXattr: error get inode(%xh)'s addr.\n", Inode->i_ino)); - goto errorout; - } - - rc = Ext2SaveBuffer(IrpContext, - Vcb, - Offset + EXT2_GOOD_OLD_INODE_SIZE + Inode->i_extra_isize, - InodeSize - EXT2_GOOD_OLD_INODE_SIZE - Inode->i_extra_isize, - (char *)InodeXattr + EXT2_GOOD_OLD_INODE_SIZE + Inode->i_extra_isize); - - if (rc && IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) { - Ext2StartFloppyFlushDpc(Vcb, NULL, NULL); - } - -errorout: - return rc; -} - - -BOOLEAN -Ext2LoadBlock (IN PEXT2_VCB Vcb, - IN ULONG Index, - IN PVOID Buffer ) -{ - struct buffer_head *bh = NULL; - BOOLEAN rc = 0; - - __try { - - bh = sb_getblk(&Vcb->sb, (sector_t)Index); - - if (!bh) { - DEBUG(DL_ERR, ("Ext2Loadblock: can't load block %u\n", Index)); - DbgBreak(); - __leave; - } - - if (!buffer_uptodate(bh)) { - int err = bh_submit_read(bh); - if (err < 0) { - DEBUG(DL_ERR, ("Ext2LoadBlock: reading failed %d\n", err)); - __leave; - } - } - - RtlCopyMemory(Buffer, bh->b_data, BLOCK_SIZE); - rc = TRUE; - - } __finally { - - if (bh) - fini_bh(&bh); - } - - return rc; -} - - -BOOLEAN -Ext2SaveBlock ( IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN ULONG Index, - IN PVOID Buf ) -{ - struct buffer_head *bh = NULL; - BOOLEAN rc = 0; - - __try { - - bh = sb_getblk_zero(&Vcb->sb, (sector_t)Index); - - if (!bh) { - DEBUG(DL_ERR, ("Ext2Saveblock: can't load block %u\n", Index)); - DbgBreak(); - __leave; - } - - if (!buffer_uptodate(bh)) { - } - - RtlCopyMemory(bh->b_data, Buf, BLOCK_SIZE); - mark_buffer_dirty(bh); - rc = TRUE; - - } __finally { - - if (bh) - fini_bh(&bh); - } - - return rc; -} - -BOOLEAN -Ext2LoadBuffer( IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN LONGLONG offset, - IN ULONG size, - IN PVOID buf ) -{ - struct buffer_head *bh = NULL; - BOOLEAN rc; - - __try { - - while (size) { - - sector_t block; - ULONG len = 0, delta = 0; - - block = (sector_t) (offset >> BLOCK_BITS); - delta = (ULONG)offset & (BLOCK_SIZE - 1); - len = BLOCK_SIZE - delta; - if (size < len) - len = size; - - bh = sb_getblk(&Vcb->sb, block); - if (!bh) { - DEBUG(DL_ERR, ("Ext2SaveBuffer: can't load block %I64u\n", block)); - DbgBreak(); - __leave; - } - - if (!buffer_uptodate(bh)) { - int err = bh_submit_read(bh); - if (err < 0) { - DEBUG(DL_ERR, ("Ext2SaveBuffer: bh_submit_read failed: %d\n", err)); - __leave; - } - } - - __try { - RtlCopyMemory(buf, bh->b_data + delta, len); - } __finally { - fini_bh(&bh); - } - - buf = (PUCHAR)buf + len; - offset = offset + len; - size = size - len; - } - - rc = TRUE; - - } __finally { - - if (bh) - fini_bh(&bh); - - } - - return rc; -} - - -BOOLEAN -Ext2ZeroBuffer( IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN LONGLONG offset, - IN ULONG size - ) -{ - struct buffer_head *bh = NULL; - BOOLEAN rc = 0; - - __try { - - while (size) { - - sector_t block; - ULONG len = 0, delta = 0; - - block = (sector_t) (offset >> BLOCK_BITS); - delta = (ULONG)offset & (BLOCK_SIZE - 1); - len = BLOCK_SIZE - delta; - if (size < len) - len = size; - - if (delta == 0 && len >= BLOCK_SIZE) { - bh = sb_getblk_zero(&Vcb->sb, block); - } else { - bh = sb_getblk(&Vcb->sb, block); - } - - if (!bh) { - DEBUG(DL_ERR, ("Ext2SaveBuffer: can't load block %I64u\n", block)); - DbgBreak(); - __leave; - } - - if (!buffer_uptodate(bh)) { - int err = bh_submit_read(bh); - if (err < 0) { - DEBUG(DL_ERR, ("Ext2SaveBuffer: bh_submit_read failed: %d\n", err)); - __leave; - } - } - - __try { - if (delta == 0 && len >= BLOCK_SIZE) { - /* bh (cache) was already cleaned as zero */ - } else { - RtlZeroMemory(bh->b_data + delta, len); - } - mark_buffer_dirty(bh); - } __finally { - fini_bh(&bh); - } - - offset = offset + len; - size = size - len; - } - - rc = TRUE; - - } __finally { - - if (bh) - fini_bh(&bh); - - } - - return rc; -} - - -BOOLEAN -Ext2SaveBuffer( IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN LONGLONG offset, - IN ULONG size, - IN PVOID buf ) -{ - struct buffer_head *bh = NULL; - BOOLEAN rc = 0; - - __try { - - while (size) { - - sector_t block; - ULONG len = 0, delta = 0; - - block = (sector_t) (offset >> BLOCK_BITS); - delta = (ULONG)offset & (BLOCK_SIZE - 1); - len = BLOCK_SIZE - delta; - if (size < len) - len = size; - - if (delta == 0 && len >= BLOCK_SIZE) { - bh = sb_getblk_zero(&Vcb->sb, block); - } else { - bh = sb_getblk(&Vcb->sb, block); - } - - if (!bh) { - DEBUG(DL_ERR, ("Ext2SaveBuffer: can't load block %I64u\n", block)); - DbgBreak(); - __leave; - } - - if (!buffer_uptodate(bh)) { - int err = bh_submit_read(bh); - if (err < 0) { - DEBUG(DL_ERR, ("Ext2SaveBuffer: bh_submit_read failed: %d\n", err)); - __leave; - } - } - - __try { - RtlCopyMemory(bh->b_data + delta, buf, len); - mark_buffer_dirty(bh); - } __finally { - fini_bh(&bh); - } - - buf = (PUCHAR)buf + len; - offset = offset + len; - size = size - len; - } - - rc = TRUE; - - } __finally { - - if (bh) - fini_bh(&bh); - - } - - return rc; -} - - -VOID -Ext2UpdateVcbStat( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb -) -{ - Vcb->SuperBlock->s_free_inodes_count = ext4_count_free_inodes(&Vcb->sb); - ext3_free_blocks_count_set(SUPER_BLOCK, ext4_count_free_blocks(&Vcb->sb)); - Ext2SaveSuper(IrpContext, Vcb); -} - -NTSTATUS -Ext2NewBlock( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN ULONG GroupHint, - IN ULONG BlockHint, - OUT PULONG Block, - IN OUT PULONG Number -) -{ - struct super_block *sb = &Vcb->sb; - PEXT2_GROUP_DESC gd; - struct buffer_head *gb = NULL; - struct buffer_head *bh = NULL; - ext4_fsblk_t bitmap_blk; - - RTL_BITMAP BlockBitmap; - - ULONG Group = 0; - ULONG Index = 0xFFFFFFFF; - ULONG dwHint = 0; - ULONG Count = 0; - ULONG Length = 0; - - NTSTATUS Status = STATUS_DISK_FULL; - - *Block = 0; - - ExAcquireResourceExclusiveLite(&Vcb->MetaBlock, TRUE); - - /* validate the hint group and hint block */ - if (GroupHint >= Vcb->sbi.s_groups_count) { - DbgBreak(); - GroupHint = Vcb->sbi.s_groups_count - 1; - } - - if (BlockHint != 0) { - GroupHint = (BlockHint - EXT2_FIRST_DATA_BLOCK) / BLOCKS_PER_GROUP; - dwHint = (BlockHint - EXT2_FIRST_DATA_BLOCK) % BLOCKS_PER_GROUP; - } - - Group = GroupHint; - -Again: - - if (bh) - fini_bh(&bh); - - if (gb) - fini_bh(&gb); - - gd = ext4_get_group_desc(sb, Group, &gb); - if (!gd) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - - bitmap_blk = ext4_block_bitmap(sb, gd); - - if (gd->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { - bh = sb_getblk_zero(sb, bitmap_blk); - if (!bh) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - gd->bg_checksum = ext4_group_desc_csum(EXT3_SB(sb), Group, gd); - ext4_init_block_bitmap(sb, bh, Group, gd); - set_buffer_uptodate(bh); - gd->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT); - Ext2SaveGroup(IrpContext, Vcb, Group); - } else { - bh = sb_getblk(sb, bitmap_blk); - if (!bh) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - } - - if (!buffer_uptodate(bh)) { - int err = bh_submit_read(bh); - if (err < 0) { - DbgPrint("bh_submit_read error! err: %d\n", err); - Status = Ext2WinntError(err); - goto errorout; - } - } - - if (ext4_free_blks_count(sb, gd)) { - - if (Group == Vcb->sbi.s_groups_count - 1) { - - Length = (ULONG)(TOTAL_BLOCKS % BLOCKS_PER_GROUP); - - /* s_blocks_count is integer multiple of s_blocks_per_group */ - if (Length == 0) { - Length = BLOCKS_PER_GROUP; - } - } else { - Length = BLOCKS_PER_GROUP; - } - - /* initialize bitmap buffer */ - RtlInitializeBitMap(&BlockBitmap, (PULONG)bh->b_data, Length); - - /* try to find a clear bit range */ - Index = RtlFindClearBits(&BlockBitmap, *Number, dwHint); - - /* We could not get new block in the prefered group */ - if (Index == 0xFFFFFFFF) { - - /* search clear bits from the hint block */ - Count = RtlFindNextForwardRunClear(&BlockBitmap, dwHint, &Index); - if (dwHint != 0 && Count == 0) { - /* search clear bits from the very beginning */ - Count = RtlFindNextForwardRunClear(&BlockBitmap, 0, &Index); - } - - if (Count == 0) { - - RtlZeroMemory(&BlockBitmap, sizeof(RTL_BITMAP)); - - /* no blocks found: set bg_free_blocks_count to 0 */ - ext4_free_blks_set(sb, gd, 0); - Ext2SaveGroup(IrpContext, Vcb, Group); - - /* will try next group */ - goto Again; - - } else { - - /* we got free blocks */ - if (Count <= *Number) { - *Number = Count; - } - } - } - - } else { - - /* try next group */ - dwHint = 0; - Group = (Group + 1) % Vcb->sbi.s_groups_count; - if (Group != GroupHint) { - goto Again; - } - - Index = 0xFFFFFFFF; - } - - if (Index < Length) { - - /* mark block bits as allocated */ - RtlSetBits(&BlockBitmap, Index, *Number); - - /* set block bitmap dirty in cache */ - mark_buffer_dirty(bh); - - /* update group description */ - ext4_free_blks_set(sb, gd, RtlNumberOfClearBits(&BlockBitmap)); - Ext2SaveGroup(IrpContext, Vcb, Group); - - /* update Vcb free blocks */ - Ext2UpdateVcbStat(IrpContext, Vcb); - - /* validate the new allocated block number */ - *Block = Index + EXT2_FIRST_DATA_BLOCK + Group * BLOCKS_PER_GROUP; - if (*Block >= TOTAL_BLOCKS || *Block + *Number > TOTAL_BLOCKS) { - DbgBreak(); - dwHint = 0; - goto Again; - } - - if (ext4_block_bitmap(sb, gd) == *Block || - ext4_inode_bitmap(sb, gd) == *Block || - ext4_inode_table(sb, gd) == *Block ) { - DbgBreak(); - dwHint = 0; - goto Again; - } - - /* Always remove dirty MCB to prevent Volume's lazy writing. - Metadata blocks will be re-added during modifications.*/ - if (Ext2RemoveBlockExtent(Vcb, NULL, *Block, *Number)) { - } else { - DbgBreak(); - Ext2RemoveBlockExtent(Vcb, NULL, *Block, *Number); - } - - DEBUG(DL_INF, ("Ext2NewBlock: Block %xh - %x allocated.\n", - *Block, *Block + *Number)); - Status = STATUS_SUCCESS; - } - -errorout: - - ExReleaseResourceLite(&Vcb->MetaBlock); - - if (bh) - fini_bh(&bh); - - if (gb) - fini_bh(&gb); - - return Status; -} - -NTSTATUS -Ext2FreeBlock( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN ULONG Block, - IN ULONG Number -) -{ - struct super_block *sb = &Vcb->sb; - PEXT2_GROUP_DESC gd; - struct buffer_head *gb = NULL; - ext4_fsblk_t bitmap_blk; - - RTL_BITMAP BlockBitmap; - LARGE_INTEGER Offset; - - PBCB BitmapBcb; - PVOID BitmapCache; - - ULONG Group; - ULONG Index; - ULONG Length; - ULONG Count; - - NTSTATUS Status = STATUS_UNSUCCESSFUL; - - ExAcquireResourceExclusiveLite(&Vcb->MetaBlock, TRUE); - - DEBUG(DL_INF, ("Ext2FreeBlock: Block %xh - %x to be freed.\n", - Block, Block + Number)); - - Group = (Block - EXT2_FIRST_DATA_BLOCK) / BLOCKS_PER_GROUP; - Index = (Block - EXT2_FIRST_DATA_BLOCK) % BLOCKS_PER_GROUP; - -Again: - - if (gb) - fini_bh(&gb); - - if ( Block < EXT2_FIRST_DATA_BLOCK || - Block >= TOTAL_BLOCKS || - Group >= Vcb->sbi.s_groups_count) { - - DbgBreak(); - Status = STATUS_SUCCESS; - - } else { - - gd = ext4_get_group_desc(sb, Group, &gb); - if (!gd) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - bitmap_blk = ext4_block_bitmap(sb, gd); - - /* check the block is valid or not */ - if (bitmap_blk >= TOTAL_BLOCKS) { - DbgBreak(); - Status = STATUS_DISK_CORRUPT_ERROR; - goto errorout; - } - - /* get bitmap block offset and length */ - Offset.QuadPart = bitmap_blk; - Offset.QuadPart = Offset.QuadPart << BLOCK_BITS; - - if (Group == Vcb->sbi.s_groups_count - 1) { - - Length = (ULONG)(TOTAL_BLOCKS % BLOCKS_PER_GROUP); - - /* s_blocks_count is integer multiple of s_blocks_per_group */ - if (Length == 0) { - Length = BLOCKS_PER_GROUP; - } - - } else { - Length = BLOCKS_PER_GROUP; - } - - /* read and initialize bitmap */ - if (!CcPinRead( Vcb->Volume, - &Offset, - Vcb->BlockSize, - PIN_WAIT, - &BitmapBcb, - &BitmapCache ) ) { - - DEBUG(DL_ERR, ("Ext2FreeBlock: failed to PinLock bitmap block %xh.\n", - bitmap_blk)); - Status = STATUS_CANT_WAIT; - DbgBreak(); - goto errorout; - } - - /* clear usused bits */ - RtlInitializeBitMap(&BlockBitmap, BitmapCache, Length); - Count = min(Length - Index, Number); - RtlClearBits(&BlockBitmap, Index, Count); - - /* update group description table */ - ext4_free_blks_set(sb, gd, RtlNumberOfClearBits(&BlockBitmap)); - - /* indict the cache range is dirty */ - CcSetDirtyPinnedData(BitmapBcb, NULL ); - Ext2AddVcbExtent(Vcb, Offset.QuadPart, (LONGLONG)Vcb->BlockSize); - CcUnpinData(BitmapBcb); - BitmapBcb = NULL; - BitmapCache = NULL; - Ext2SaveGroup(IrpContext, Vcb, Group); - - /* remove dirty MCB to prevent Volume's lazy writing. */ - if (Ext2RemoveBlockExtent(Vcb, NULL, Block, Count)) { - } else { - DbgBreak(); - Ext2RemoveBlockExtent(Vcb, NULL, Block, Count); - } - - /* save super block (used/unused blocks statics) */ - Ext2UpdateVcbStat(IrpContext, Vcb); - - /* try next group to clear all remaining */ - Number -= Count; - if (Number) { - Group += 1; - if (Group < Vcb->sbi.s_groups_count) { - Index = 0; - Block += Count; - goto Again; - } else { - DEBUG(DL_ERR, ("Ext2FreeBlock: block number beyonds max group.\n")); - goto errorout; - } - } - } - - Status = STATUS_SUCCESS; - -errorout: - - if (gb) - fini_bh(&gb); - - ExReleaseResourceLite(&Vcb->MetaBlock); - - return Status; -} - - -NTSTATUS -Ext2NewInode( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN ULONG GroupHint, - IN ULONG Type, - OUT PULONG Inode -) -{ - struct super_block *sb = &Vcb->sb; - PEXT2_GROUP_DESC gd; - struct buffer_head *gb = NULL; - struct buffer_head *bh = NULL; - ext4_fsblk_t bitmap_blk; - - RTL_BITMAP InodeBitmap; - - ULONG Group, i, j; - ULONG Average, Length; - - ULONG dwInode; - - NTSTATUS Status = STATUS_DISK_FULL; - - *Inode = dwInode = 0XFFFFFFFF; - - ExAcquireResourceExclusiveLite(&Vcb->MetaInode, TRUE); - - if (GroupHint >= Vcb->sbi.s_groups_count) - GroupHint = GroupHint % Vcb->sbi.s_groups_count; - -repeat: - - if (bh) - fini_bh(&bh); - - if (gb) - fini_bh(&gb); - - Group = i = 0; - gd = NULL; - - if (Type == EXT2_FT_DIR) { - - Average = Vcb->SuperBlock->s_free_inodes_count / Vcb->sbi.s_groups_count; - - for (j = 0; j < Vcb->sbi.s_groups_count; j++) { - - i = (j + GroupHint) % (Vcb->sbi.s_groups_count); - gd = ext4_get_group_desc(sb, i, &gb); - if (!gd) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - - if ((gd->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) || - (ext4_used_dirs_count(sb, gd) << 8 < - ext4_free_inodes_count(sb, gd)) ) { - Group = i + 1; - break; - } - fini_bh(&gb); - } - - if (!Group) { - - PEXT2_GROUP_DESC desc = NULL; - - gd = NULL; - - /* get the group with the biggest vacancy */ - for (j = 0; j < Vcb->sbi.s_groups_count; j++) { - - struct buffer_head *gt = NULL; - desc = ext4_get_group_desc(sb, j, >); - if (!desc) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - - /* return the group if it's not initialized yet */ - if (desc->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) { - Group = j + 1; - gd = desc; - - if (gb) - fini_bh(&gb); - gb = gt; - gt = NULL; - break; - } - - if (!gd) { - if (ext4_free_inodes_count(sb, desc) > 0) { - Group = j + 1; - gd = desc; - if (gb) - fini_bh(&gb); - gb = gt; - gt = NULL; - } - } else { - if (ext4_free_inodes_count(sb, desc) > - ext4_free_inodes_count(sb, gd)) { - Group = j + 1; - gd = desc; - if (gb) - fini_bh(&gb); - gb = gt; - gt = NULL; - break; - } - } - if (gt) - fini_bh(>); - } - } - - } else { - - /* - * Try to place the inode in its parent directory (GroupHint) - */ - - gd = ext4_get_group_desc(sb, GroupHint, &gb); - if (!gb) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - - if (gd->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT) || - ext4_free_inodes_count(sb, gd)) { - - Group = GroupHint + 1; - - } else { - - /* this group is 100% cocucpied */ - fini_bh(&gb); - - i = GroupHint; - - /* - * Use a quadratic hash to find a group with a free inode - */ - - for (j = 1; j < Vcb->sbi.s_groups_count; j <<= 1) { - - - i = (i + j) % Vcb->sbi.s_groups_count; - gd = ext4_get_group_desc(sb, i, &gb); - if (!gd) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - - if (gd->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT) || - ext4_free_inodes_count(sb, gd)) { - Group = i + 1; - break; - } - - fini_bh(&gb); - } - } - - if (!Group) { - /* - * That failed: try linear search for a free inode - */ - i = GroupHint; - for (j = 2; j < Vcb->sbi.s_groups_count; j++) { - - i = (i + 1) % Vcb->sbi.s_groups_count; - gd = ext4_get_group_desc(sb, i, &gb); - if (!gd) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - - if (gd->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT) || - ext4_free_inodes_count(sb, gd)) { - Group = i + 1; - break; - } - - fini_bh(&gb); - } - } - } - - if (gd == NULL || Group == 0) { - goto errorout; - } - - /* finally we got the group, but is it valid ? */ - if (Group > Vcb->sbi.s_groups_count) { - DbgBreak(); - goto errorout; - } - - /* valid group number starts from 1, not 0 */ - Group -= 1; - - ASSERT(gd); - bitmap_blk = ext4_inode_bitmap(sb, gd); - /* check the block is valid or not */ - if (bitmap_blk == 0 || bitmap_blk >= TOTAL_BLOCKS) { - DbgBreak(); - Status = STATUS_DISK_CORRUPT_ERROR; - goto errorout; - } - - if (gd->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) { - bh = sb_getblk_zero(sb, bitmap_blk); - if (!bh) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - gd->bg_checksum = ext4_group_desc_csum(EXT3_SB(sb), Group, gd); - ext4_init_inode_bitmap(sb, bh, Group, gd); - set_buffer_uptodate(bh); - gd->bg_flags &= cpu_to_le16(~EXT4_BG_INODE_UNINIT); - Ext2SaveGroup(IrpContext, Vcb, Group); - } else { - bh = sb_getblk(sb, bitmap_blk); - if (!bh) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - } - - if (!buffer_uptodate(bh)) { - int err = bh_submit_read(bh); - if (err < 0) { - DbgPrint("bh_submit_read error! err: %d\n", err); - Status = Ext2WinntError(err); - goto errorout; - } - } - - if (Vcb->sbi.s_groups_count == 1) { - Length = INODES_COUNT; - } else { - if (Group + 1 == Vcb->sbi.s_groups_count) { - Length = INODES_COUNT % INODES_PER_GROUP; - if (!Length) { - /* INODES_COUNT is integer multiple of INODES_PER_GROUP */ - Length = INODES_PER_GROUP; - } - } else { - Length = INODES_PER_GROUP; - } - } - - RtlInitializeBitMap(&InodeBitmap, (PULONG)bh->b_data, Length); - dwInode = RtlFindClearBits(&InodeBitmap, 1, 0); - - if (dwInode == 0xFFFFFFFF || dwInode >= Length) { - - RtlZeroMemory(&InodeBitmap, sizeof(RTL_BITMAP)); - if (ext4_free_inodes_count(sb, gd) > 0) { - ext4_free_inodes_set(sb, gd, 0); - Ext2SaveGroup(IrpContext, Vcb, Group); - } - goto repeat; - - } else { - - __u32 count = 0; - - /* update unused inodes count */ - count = ext4_free_inodes_count(sb, gd) - 1; - ext4_free_inodes_set(sb, gd, count); - - RtlSetBits(&InodeBitmap, dwInode, 1); - - /* set block bitmap dirty in cache */ - mark_buffer_dirty(bh); - - /* If we didn't allocate from within the initialized part of the inode - * table then we need to initialize up to this inode. */ - if (EXT3_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { - - __u32 free; - - if (gd->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) { - gd->bg_flags &= cpu_to_le16(~EXT4_BG_INODE_UNINIT); - /* When marking the block group with - * ~EXT4_BG_INODE_UNINIT we don't want to depend - * on the value of bg_itable_unused even though - * mke2fs could have initialized the same for us. - * Instead we calculated the value below - */ - - free = 0; - } else { - free = EXT3_INODES_PER_GROUP(sb) - ext4_itable_unused_count(sb, gd); - } - - /* - * Check the relative inode number against the last used - * relative inode number in this group. if it is greater - * we need to update the bg_itable_unused count - * - */ - if (dwInode + 1 > free) { - ext4_itable_unused_set(sb, gd, - (EXT3_INODES_PER_GROUP(sb) - 1 - dwInode)); - } - - /* We may have to initialize the block bitmap if it isn't already */ - if (gd->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { - - struct buffer_head *block_bitmap_bh = NULL; - - /* recheck and clear flag under lock if we still need to */ - block_bitmap_bh = sb_getblk_zero(sb, ext4_block_bitmap(sb, gd)); - if (block_bitmap_bh) { - gd->bg_checksum = ext4_group_desc_csum(EXT3_SB(sb), Group, gd); - free = ext4_init_block_bitmap(sb, block_bitmap_bh, Group, gd); - set_buffer_uptodate(block_bitmap_bh); - brelse(block_bitmap_bh); - gd->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT); - ext4_free_blks_set(sb, gd, free); - Ext2SaveGroup(IrpContext, Vcb, Group); - } - } - } - - *Inode = dwInode + 1 + Group * INODES_PER_GROUP; - - /* update group_desc / super_block */ - if (Type == EXT2_FT_DIR) { - ext4_used_dirs_set(sb, gd, ext4_used_dirs_count(sb, gd) + 1); - } - Ext2SaveGroup(IrpContext, Vcb, Group); - Ext2UpdateVcbStat(IrpContext, Vcb); - Status = STATUS_SUCCESS; - } - -errorout: - - ExReleaseResourceLite(&Vcb->MetaInode); - - if (bh) - fini_bh(&bh); - - if (gb) - fini_bh(&gb); - - - return Status; -} - -NTSTATUS -Ext2UpdateGroupDirStat( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN ULONG group - ) -{ - struct super_block *sb = &Vcb->sb; - PEXT2_GROUP_DESC gd; - struct buffer_head *gb = NULL; - NTSTATUS status; - - ExAcquireResourceExclusiveLite(&Vcb->MetaInode, TRUE); - - /* get group desc */ - gd = ext4_get_group_desc(sb, group, &gb); - if (!gd) { - status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - - /* update group_desc and super_block */ - ext4_used_dirs_set(sb, gd, ext4_used_dirs_count(sb, gd) - 1); - Ext2SaveGroup(IrpContext, Vcb, group); - Ext2UpdateVcbStat(IrpContext, Vcb); - status = STATUS_SUCCESS; - -errorout: - - ExReleaseResourceLite(&Vcb->MetaInode); - - if (gb) - fini_bh(&gb); - - return status; -} - - -NTSTATUS -Ext2FreeInode( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN ULONG Inode, - IN ULONG Type -) -{ - struct super_block *sb = &Vcb->sb; - PEXT2_GROUP_DESC gd; - struct buffer_head *gb = NULL; - struct buffer_head *bh = NULL; - ext4_fsblk_t bitmap_blk; - - RTL_BITMAP InodeBitmap; - ULONG Group; - ULONG Length; - LARGE_INTEGER Offset; - - ULONG dwIno; - BOOLEAN bModified = FALSE; - - NTSTATUS Status = STATUS_UNSUCCESSFUL; - - ExAcquireResourceExclusiveLite(&Vcb->MetaInode, TRUE); - - Group = (Inode - 1) / INODES_PER_GROUP; - dwIno = (Inode - 1) % INODES_PER_GROUP; - - DEBUG(DL_INF, ( "Ext2FreeInode: Inode: %xh (Group/Off = %xh/%xh)\n", - Inode, Group, dwIno)); - - if (Group >= Vcb->sbi.s_groups_count) { - DbgBreak(); - goto errorout; - } - - gd = ext4_get_group_desc(sb, Group, &gb); - if (!gd) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - - bitmap_blk = ext4_inode_bitmap(sb, gd); - bh = sb_getblk(sb, bitmap_blk); - if (!bh) { - DbgBreak(); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - if (!buffer_uptodate(bh)) { - int err = bh_submit_read(bh); - if (err < 0) { - DbgPrint("bh_submit_read error! err: %d\n", err); - Status = Ext2WinntError(err); - goto errorout; - } - } - - if (Group == Vcb->sbi.s_groups_count - 1) { - - Length = INODES_COUNT % INODES_PER_GROUP; - if (!Length) { - /* s_inodes_count is integer multiple of s_inodes_per_group */ - Length = INODES_PER_GROUP; - } - } else { - Length = INODES_PER_GROUP; - } - - RtlInitializeBitMap(&InodeBitmap, (PULONG)bh->b_data, Length); - - if (RtlCheckBit(&InodeBitmap, dwIno) == 0) { - DbgBreak(); - Status = STATUS_SUCCESS; - } else { - RtlClearBits(&InodeBitmap, dwIno, 1); - bModified = TRUE; - } - - if (bModified) { - /* update group free inodes */ - ext4_free_inodes_set(sb, gd, - RtlNumberOfClearBits(&InodeBitmap)); - - /* set inode block dirty and add to vcb dirty range */ - mark_buffer_dirty(bh); - - /* update group_desc and super_block */ - if (Type == EXT2_FT_DIR) { - ext4_used_dirs_set(sb, gd, - ext4_used_dirs_count(sb, gd) - 1); - } - Ext2SaveGroup(IrpContext, Vcb, Group); - Ext2UpdateVcbStat(IrpContext, Vcb); - Status = STATUS_SUCCESS; - } - -errorout: - - ExReleaseResourceLite(&Vcb->MetaInode); - - if (bh) - fini_bh(&bh); - - if (gb) - fini_bh(&gb); - - return Status; -} - - -NTSTATUS -Ext2AddEntry ( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN PEXT2_FCB Dcb, - IN struct inode *Inode, - IN PUNICODE_STRING FileName, - struct dentry **Dentry -) -{ - struct dentry *de = NULL; - - NTSTATUS status = STATUS_UNSUCCESSFUL; - OEM_STRING oem; - int rc; - - BOOLEAN MainResourceAcquired = FALSE; - - if (!IsDirectory(Dcb)) { - DbgBreak(); - return STATUS_NOT_A_DIRECTORY; - } - - ExAcquireResourceExclusiveLite(&Dcb->MainResource, TRUE); - MainResourceAcquired = TRUE; - - __try { - - Ext2ReferXcb(&Dcb->ReferenceCount); - de = Ext2BuildEntry(Vcb, Dcb->Mcb, FileName); - if (!de) { - status = STATUS_INSUFFICIENT_RESOURCES; - __leave; - } - de->d_inode = Inode; - - rc = ext3_add_entry(IrpContext, de, Inode); - status = Ext2WinntError(rc); - if (NT_SUCCESS(status)) { - - /* increase dir inode's nlink for .. */ - if (S_ISDIR(Inode->i_mode)) { - ext3_inc_count(Dcb->Inode); - ext3_mark_inode_dirty(IrpContext, Dcb->Inode); - } - - /* increase inode nlink reference */ - ext3_inc_count(Inode); - ext3_mark_inode_dirty(IrpContext, Inode); - - if (Dentry) { - *Dentry = de; - de = NULL; - } - } - - } __finally { - - Ext2DerefXcb(&Dcb->ReferenceCount); - - if (MainResourceAcquired) { - ExReleaseResourceLite(&Dcb->MainResource); - } - - if (de) - Ext2FreeEntry(de); - } - - return status; -} - - -NTSTATUS -Ext2SetFileType ( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN PEXT2_FCB Dcb, - IN PEXT2_MCB Mcb, - IN umode_t mode - ) -{ - 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, mode); - mark_buffer_dirty(bh); - - if (S_ISDIR(inode->i_mode) == S_ISDIR(mode)) { - } else if (S_ISDIR(inode->i_mode)) { - ext3_dec_count(dir); - } else if (S_ISDIR(mode)) { - ext3_inc_count(dir); - } - dir->i_ctime = dir->i_mtime = ext3_current_time(dir); - ext3_mark_inode_dirty(IrpContext, dir); - - inode->i_mode = mode; - ext3_mark_inode_dirty(IrpContext, inode); - - 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, - 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; - int rc = -ENOENT; - NTSTATUS Status = STATUS_UNSUCCESSFUL; - BOOLEAN MainResourceAcquired = FALSE; - - 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; - - if (!inode->i_nlink) { - ext3_warning (inode->i_sb, "ext3_unlink", - "Deleting nonexistent file (%lu), %d", - inode->i_ino, inode->i_nlink); - inode->i_nlink = 1; - } - rc = ext3_delete_entry(IrpContext, dir, de, bh); - if (rc) { - Status = Ext2WinntError(rc); - __leave; - } - /* - if (!inode->i_nlink) - ext3_orphan_add(handle, inode); - */ - inode->i_ctime = dir->i_ctime = dir->i_mtime = ext3_current_time(dir); - ext3_dec_count(inode); - ext3_mark_inode_dirty(IrpContext, inode); - - /* decrease dir inode's nlink for .. */ - if (S_ISDIR(inode->i_mode)) { - ext3_update_dx_flag(dir); - ext3_dec_count(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 -Ext2SetParentEntry ( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN PEXT2_FCB Dcb, - IN ULONG OldParent, - IN ULONG NewParent ) -{ - NTSTATUS Status = STATUS_UNSUCCESSFUL; - - PEXT2_DIR_ENTRY2 pSelf = NULL; - PEXT2_DIR_ENTRY2 pParent = NULL; - - ULONG dwBytes = 0; - - BOOLEAN MainResourceAcquired = FALSE; - - ULONG Offset = 0; - - if (!IsDirectory(Dcb)) { - return STATUS_NOT_A_DIRECTORY; - } - - if (OldParent == NewParent) { - return STATUS_SUCCESS; - } - - MainResourceAcquired = - ExAcquireResourceExclusiveLite(&Dcb->MainResource, TRUE); - - __try { - - Ext2ReferXcb(&Dcb->ReferenceCount); - - pSelf = (PEXT2_DIR_ENTRY2) - Ext2AllocatePool( - PagedPool, - EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2), - EXT2_DENTRY_MAGIC - ); - if (!pSelf) { - DEBUG(DL_ERR, ( "Ex2SetParentEntry: failed to allocate pSelf.\n")); - Status = STATUS_INSUFFICIENT_RESOURCES; - __leave; - } - - dwBytes = 0; - - // - // Reading the DCB contents - // - - Status = Ext2ReadInode( - IrpContext, - Vcb, - Dcb->Mcb, - (ULONGLONG)Offset, - (PVOID)pSelf, - EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2), - FALSE, - &dwBytes ); - - if (!NT_SUCCESS(Status)) { - DEBUG(DL_ERR, ( "Ext2SetParentEntry: failed to read directory.\n")); - __leave; - } - - ASSERT(dwBytes == EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2)); - - pParent = (PEXT2_DIR_ENTRY2)((PUCHAR)pSelf + pSelf->rec_len); - - if (pSelf->name_len == 1 && pSelf->name[0] == '.' && - pParent->name_len == 2 && pParent->name[0] == '.' && - pParent->name[1] == '.') { - - if (pParent->inode != OldParent) { - DbgBreak(); - } - pParent->inode = NewParent; - - Status = Ext2WriteInode( - IrpContext, - Vcb, - Dcb->Mcb, - (ULONGLONG)Offset, - pSelf, - dwBytes, - FALSE, - &dwBytes ); - } else { - DbgBreak(); - } - - } __finally { - - - if (Ext2DerefXcb(&Dcb->ReferenceCount) == 0) { - DEBUG(DL_ERR, ( "Ext2SetParentEntry: Dcb reference goes to ZERO.\n")); - } - - if (MainResourceAcquired) { - ExReleaseResourceLite(&Dcb->MainResource); - } - - if (pSelf) { - Ext2FreePool(pSelf, EXT2_DENTRY_MAGIC); - } - } - - return Status; -} - -int ext3_check_dir_entry (const char * function, struct inode * dir, - struct ext3_dir_entry_2 * de, - struct buffer_head * bh, - unsigned long offset) -{ - const char * error_msg = NULL; - const int rlen = ext3_rec_len_from_disk(de->rec_len); - - if (rlen < EXT3_DIR_REC_LEN(1)) - error_msg = "rec_len is smaller than minimal"; - else if (rlen % 4 != 0) - error_msg = "rec_len % 4 != 0"; - else if (rlen < EXT3_DIR_REC_LEN(de->name_len)) - error_msg = "rec_len is too small for name_len"; - else if ((char *) de + rlen > bh->b_data + dir->i_sb->s_blocksize) - error_msg = "directory entry across blocks"; - else if (le32_to_cpu(de->inode) > - le32_to_cpu(EXT3_SB(dir->i_sb)->s_es->s_inodes_count)) - error_msg = "inode out of bounds"; - - if (error_msg != NULL) { - DEBUG(DL_ERR, ("%s: bad entry in directory %u: %s - " - "offset=%u, inode=%u, rec_len=%d, name_len=%d\n", - function, dir->i_ino, error_msg, offset, - (unsigned long) le32_to_cpu(de->inode), - rlen, de->name_len)); - } - return error_msg == NULL ? 1 : 0; -} - - -/* - * p is at least 6 bytes before the end of page - */ -struct ext3_dir_entry_2 * - ext3_next_entry(struct ext3_dir_entry_2 *p) -{ - return (struct ext3_dir_entry_2 *)((char *)p + - ext3_rec_len_from_disk(p->rec_len)); -} - -#define MAX_LFS_FILESIZE 0x7fffffffffffffff - -/* - * Maximal extent format file size. - * Resulting logical blkno at s_maxbytes must fit in our on-disk - * extent format containers, within a sector_t, and within i_blocks - * in the vfs. ext4 inode has 48 bits of i_block in fsblock units, - * so that won't be a limiting factor. - * - * Note, this does *not* consider any metadata overhead for vfs i_blocks. - */ -static loff_t ext4_max_size(int blkbits, int has_huge_files) -{ - loff_t res; - loff_t upper_limit = MAX_LFS_FILESIZE; - - /* small i_blocks in vfs inode? */ - if (!has_huge_files || sizeof(blkcnt_t) < sizeof(u64)) { - /* - * CONFIG_LBD is not enabled implies the inode - * i_block represent total blocks in 512 bytes - * 32 == size of vfs inode i_blocks * 8 - */ - upper_limit = (1LL << 32) - 1; - - /* total blocks in file system block size */ - upper_limit >>= (blkbits - 9); - upper_limit <<= blkbits; - } - - /* 32-bit extent-start container, ee_block */ - res = 1LL << 32; - res <<= blkbits; - res -= 1; - - /* Sanity check against vm- & vfs- imposed limits */ - if (res > upper_limit) - res = upper_limit; - - return res; -} - -/* - * Maximal extent format file size. - * Resulting logical blkno at s_maxbytes must fit in our on-disk - * extent format containers, within a sector_t, and within i_blocks - * in the vfs. ext4 inode has 48 bits of i_block in fsblock units, - * so that won't be a limiting factor. - * - * Note, this does *not* consider any metadata overhead for vfs i_blocks. - */ -loff_t ext3_max_size(int blkbits, int has_huge_files) -{ - loff_t res; - loff_t upper_limit = MAX_LFS_FILESIZE; - - /* small i_blocks in vfs inode? */ - if (!has_huge_files) { - /* - * CONFIG_LBD is not enabled implies the inode - * i_block represent total blocks in 512 bytes - * 32 == size of vfs inode i_blocks * 8 - */ - upper_limit = ((loff_t)1 << 32) - 1; - - /* total blocks in file system block size */ - upper_limit >>= (blkbits - 9); - upper_limit <<= blkbits; - } - - /* 32-bit extent-start container, ee_block */ - res = (loff_t)1 << 32; - res <<= blkbits; - res -= 1; - - /* Sanity check against vm- & vfs- imposed limits */ - if (res > upper_limit) - res = upper_limit; - - return res; -} - -/* - * Maximal bitmap file size. There is a direct, and {,double-,triple-}indirect - * block limit, and also a limit of (2^48 - 1) 512-byte sectors in i_blocks. - * We need to be 1 filesystem block less than the 2^48 sector limit. - */ -loff_t ext3_max_bitmap_size(int bits, int has_huge_files) -{ - loff_t res = EXT3_NDIR_BLOCKS; - int meta_blocks; - loff_t upper_limit; - /* This is calculated to be the largest file size for a - * dense, bitmapped file such that the total number of - * sectors in the file, including data and all indirect blocks, - * does not exceed 2^48 -1 - * __u32 i_blocks_lo and _u16 i_blocks_high representing the - * total number of 512 bytes blocks of the file - */ - - if (!has_huge_files) { - /* - * !has_huge_files or CONFIG_LBD is not enabled - * implies the inode i_block represent total blocks in - * 512 bytes 32 == size of vfs inode i_blocks * 8 - */ - upper_limit = ((loff_t)1 << 32) - 1; - - /* total blocks in file system block size */ - upper_limit >>= (bits - 9); - - } else { - /* - * We use 48 bit ext4_inode i_blocks - * With EXT4_HUGE_FILE_FL set the i_blocks - * represent total number of blocks in - * file system block size - */ - upper_limit = ((loff_t)1 << 48) - 1; - - } - - /* indirect blocks */ - meta_blocks = 1; - /* double indirect blocks */ - meta_blocks += 1 + ((loff_t)1 << (bits-2)); - /* tripple indirect blocks */ - meta_blocks += 1 + ((loff_t)1 << (bits-2)) + ((loff_t)1 << (2*(bits-2))); - - upper_limit -= meta_blocks; - upper_limit <<= bits; - - res += (loff_t)1 << (bits-2); - res += (loff_t)1 << (2*(bits-2)); - res += (loff_t)1 << (3*(bits-2)); - res <<= bits; - if (res > upper_limit) - res = upper_limit; - - if (res > MAX_LFS_FILESIZE) - res = MAX_LFS_FILESIZE; - - return res; -} - -blkcnt_t ext3_inode_blocks(struct ext3_inode *raw_inode, - struct inode *inode) -{ - blkcnt_t i_blocks ; - struct super_block *sb = inode->i_sb; - PEXT2_VCB Vcb = (PEXT2_VCB)sb->s_priv; - - if (EXT3_HAS_RO_COMPAT_FEATURE(sb, - EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) { - /* we are using combined 48 bit field */ - i_blocks = ((u64)le16_to_cpu(raw_inode->i_blocks_high)) << 32 | - le32_to_cpu(raw_inode->i_blocks); - if (inode->i_flags & EXT4_HUGE_FILE_FL) { - /* i_blocks represent file system block size */ - return i_blocks << (BLOCK_BITS - 9); - } else { - return i_blocks; - } - } else { - return le32_to_cpu(raw_inode->i_blocks); - } -} - -int ext3_inode_blocks_set(struct ext3_inode *raw_inode, - struct inode * inode) -{ - u64 i_blocks = inode->i_blocks; - struct super_block *sb = inode->i_sb; - PEXT2_VCB Vcb = (PEXT2_VCB)sb->s_priv; - - if (i_blocks < 0x100000000) { - /* - * i_blocks can be represnted in a 32 bit variable - * as multiple of 512 bytes - */ - raw_inode->i_blocks = cpu_to_le32(i_blocks); - raw_inode->i_blocks_high = 0; - inode->i_flags &= ~EXT4_HUGE_FILE_FL; - return 0; - } - - if (!EXT3_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) { - EXT3_SET_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_HUGE_FILE); - Ext2SaveSuper(NULL, Vcb); - } - - if (i_blocks <= 0xffffffffffff) { - /* - * i_blocks can be represented in a 48 bit variable - * as multiple of 512 bytes - */ - raw_inode->i_blocks = (__u32)cpu_to_le32(i_blocks); - raw_inode->i_blocks_high = (__u16)cpu_to_le16(i_blocks >> 32); - inode->i_flags &= ~EXT4_HUGE_FILE_FL; - } else { - inode->i_flags |= EXT4_HUGE_FILE_FL; - /* i_block is stored in file system block size */ - i_blocks = i_blocks >> (BLOCK_BITS - 9); - raw_inode->i_blocks = (__u32)cpu_to_le32(i_blocks); - raw_inode->i_blocks_high = (__u16)cpu_to_le16(i_blocks >> 32); - } - return 0; -} - -ext4_fsblk_t ext4_block_bitmap(struct super_block *sb, - struct ext4_group_desc *bg) -{ - return le32_to_cpu(bg->bg_block_bitmap) | - (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? - (ext4_fsblk_t)le32_to_cpu(bg->bg_block_bitmap_hi) << 32 : 0); -} - -ext4_fsblk_t ext4_inode_bitmap(struct super_block *sb, - struct ext4_group_desc *bg) -{ - return le32_to_cpu(bg->bg_inode_bitmap) | - (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? - (ext4_fsblk_t)le32_to_cpu(bg->bg_inode_bitmap_hi) << 32 : 0); -} - -ext4_fsblk_t ext4_inode_table(struct super_block *sb, - struct ext4_group_desc *bg) -{ - return le32_to_cpu(bg->bg_inode_table) | - (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? - (ext4_fsblk_t)le32_to_cpu(bg->bg_inode_table_hi) << 32 : 0); -} - -__u32 ext4_free_blks_count(struct super_block *sb, - struct ext4_group_desc *bg) -{ - return le16_to_cpu(bg->bg_free_blocks_count) | - (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? - (__u32)le16_to_cpu(bg->bg_free_blocks_count_hi) << 16 : 0); -} - -__u32 ext4_free_inodes_count(struct super_block *sb, - struct ext4_group_desc *bg) -{ - return le16_to_cpu(bg->bg_free_inodes_count) | - (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? - (__u32)le16_to_cpu(bg->bg_free_inodes_count_hi) << 16 : 0); -} - -__u32 ext4_used_dirs_count(struct super_block *sb, - struct ext4_group_desc *bg) -{ - return le16_to_cpu(bg->bg_used_dirs_count) | - (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? - (__u32)le16_to_cpu(bg->bg_used_dirs_count_hi) << 16 : 0); -} - -__u32 ext4_itable_unused_count(struct super_block *sb, - struct ext4_group_desc *bg) -{ - return le16_to_cpu(bg->bg_itable_unused) | - (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? - (__u32)le16_to_cpu(bg->bg_itable_unused_hi) << 16 : 0); -} - -void ext4_block_bitmap_set(struct super_block *sb, - struct ext4_group_desc *bg, ext4_fsblk_t blk) -{ - bg->bg_block_bitmap = cpu_to_le32((u32)blk); - if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) - bg->bg_block_bitmap_hi = cpu_to_le32(blk >> 32); -} - -void ext4_inode_bitmap_set(struct super_block *sb, - struct ext4_group_desc *bg, ext4_fsblk_t blk) -{ - bg->bg_inode_bitmap = cpu_to_le32((u32)blk); - if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) - bg->bg_inode_bitmap_hi = cpu_to_le32(blk >> 32); -} - -void ext4_inode_table_set(struct super_block *sb, - struct ext4_group_desc *bg, ext4_fsblk_t blk) -{ - bg->bg_inode_table = cpu_to_le32((u32)blk); - if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) - bg->bg_inode_table_hi = cpu_to_le32(blk >> 32); -} - -void ext4_free_blks_set(struct super_block *sb, - struct ext4_group_desc *bg, __u32 count) -{ - bg->bg_free_blocks_count = cpu_to_le16((__u16)count); - if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) - bg->bg_free_blocks_count_hi = cpu_to_le16(count >> 16); -} - -void ext4_free_inodes_set(struct super_block *sb, - struct ext4_group_desc *bg, __u32 count) -{ - bg->bg_free_inodes_count = cpu_to_le16((__u16)count); - if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) - bg->bg_free_inodes_count_hi = cpu_to_le16(count >> 16); -} - -void ext4_used_dirs_set(struct super_block *sb, - struct ext4_group_desc *bg, __u32 count) -{ - bg->bg_used_dirs_count = cpu_to_le16((__u16)count); - if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) - bg->bg_used_dirs_count_hi = cpu_to_le16(count >> 16); -} - -void ext4_itable_unused_set(struct super_block *sb, - struct ext4_group_desc *bg, __u32 count) -{ - bg->bg_itable_unused = cpu_to_le16((__u16)count); - if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) - bg->bg_itable_unused_hi = cpu_to_le16(count >> 16); -} - -/** CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1) */ -__u16 const crc16_table[256] = { - 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, - 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, - 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, - 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, - 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, - 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, - 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, - 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, - 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, - 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, - 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, - 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, - 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, - 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, - 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, - 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, - 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, - 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, - 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, - 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, - 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, - 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, - 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, - 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, - 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, - 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, - 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, - 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, - 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, - 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, - 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, - 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 -}; - -static inline __u16 crc16_byte(__u16 crc, const __u8 data) -{ - return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff]; -} - -__u16 crc16(__u16 crc, __u8 const *buffer, size_t len) -{ - while (len--) - crc = crc16_byte(crc, *buffer++); - return crc; -} - -__le16 ext4_group_desc_csum(struct ext3_sb_info *sbi, __u32 block_group, - struct ext4_group_desc *gdp) -{ - int offset; - __u16 crc = 0; - __le32 le_group = cpu_to_le32(block_group); - - /* old crc16 code */ - if (!(sbi->s_es->s_feature_ro_compat & - cpu_to_le32(EXT4_FEATURE_RO_COMPAT_GDT_CSUM))) - return 0; - - offset = offsetof(struct ext4_group_desc, bg_checksum); - - crc = crc16(~0, sbi->s_es->s_uuid, sizeof(sbi->s_es->s_uuid)); - crc = crc16(crc, (__u8 *)&le_group, sizeof(le_group)); - crc = crc16(crc, (__u8 *)gdp, offset); - offset += sizeof(gdp->bg_checksum); /* skip checksum */ - /* for checksum of struct ext4_group_desc do the rest...*/ - if ((sbi->s_es->s_feature_incompat & - cpu_to_le32(EXT4_FEATURE_INCOMPAT_64BIT)) && - offset < le16_to_cpu(sbi->s_es->s_desc_size)) - crc = crc16(crc, (__u8 *)gdp + offset, - le16_to_cpu(sbi->s_es->s_desc_size) - - offset); - - return cpu_to_le16(crc); -} - -int ext4_group_desc_csum_verify(struct ext3_sb_info *sbi, __u32 block_group, - struct ext4_group_desc *gdp) -{ - if ((sbi->s_es->s_feature_ro_compat & cpu_to_le32(EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) && - (gdp->bg_checksum != ext4_group_desc_csum(sbi, block_group, gdp))) - return 0; - - return 1; -} - - -static inline int test_root(ext3_group_t a, ext3_group_t b) -{ - ext3_group_t num = b; - - while (a > num) - num *= b; - return num == a; -} - -static int ext3_group_sparse(ext3_group_t group) -{ - if (group <= 1) - return 1; - if (!(group & 1)) - return 0; - return (test_root(group, 7) || test_root(group, 5) || - test_root(group, 3)); -} - -/** - * ext4_bg_has_super - number of blocks used by the superblock in group - * @sb: superblock for filesystem - * @group: group number to check - * - * Return the number of blocks used by the superblock (primary or backup) - * in this group. Currently this will be only 0 or 1. - */ -int ext3_bg_has_super(struct super_block *sb, ext3_group_t group) -{ - if (EXT3_HAS_RO_COMPAT_FEATURE(sb, - EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER) && - !ext3_group_sparse(group)) - return 0; - return 1; -} - -static unsigned long ext4_bg_num_gdb_meta(struct super_block *sb, - ext4_group_t group) -{ - unsigned long metagroup = group / EXT4_DESC_PER_BLOCK(sb); - ext4_group_t first = metagroup * EXT4_DESC_PER_BLOCK(sb); - ext4_group_t last = first + EXT4_DESC_PER_BLOCK(sb) - 1; - - if (group == first || group == first + 1 || group == last) - return 1; - return 0; -} - -static unsigned long ext4_bg_num_gdb_nometa(struct super_block *sb, - ext4_group_t group) -{ - return ext3_bg_has_super(sb, group) ? EXT3_SB(sb)->s_gdb_count : 0; -} - -/** - * ext4_bg_num_gdb - number of blocks used by the group table in group - * @sb: superblock for filesystem - * @group: group number to check - * - * Return the number of blocks used by the group descriptor table - * (primary or backup) in this group. In the future there may be a - * different number of descriptor blocks in each group. - */ -unsigned long ext4_bg_num_gdb(struct super_block *sb, ext4_group_t group) -{ - unsigned long first_meta_bg = - le32_to_cpu(EXT3_SB(sb)->s_es->s_first_meta_bg); - unsigned long metagroup = group / EXT4_DESC_PER_BLOCK(sb); - - if (!EXT3_HAS_INCOMPAT_FEATURE(sb,EXT4_FEATURE_INCOMPAT_META_BG) || - metagroup < first_meta_bg) - return ext4_bg_num_gdb_nometa(sb, group); - - return ext4_bg_num_gdb_meta(sb,group); - -} - -ext3_fsblk_t descriptor_loc(struct super_block *sb, - ext3_fsblk_t logical_sb_block, unsigned int nr) -{ - struct ext3_sb_info *sbi = EXT3_SB(sb); - ext3_group_t bg, first_meta_bg; - int has_super = 0; - - first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg); - - if (!EXT3_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG) || - nr < first_meta_bg) - return logical_sb_block + nr + 1; - bg = sbi->s_desc_per_block * nr; - if (ext3_bg_has_super(sb, bg)) - has_super = 1; - return (has_super + ext3_group_first_block_no(sb, bg)); -} - -#define ext4_set_bit(n, p) set_bit((int)(n), (unsigned long *)(p)) - -/* - * The free inodes are managed by bitmaps. A file system contains several - * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap - * block for inodes, N blocks for the inode table and data blocks. - * - * The file system contains group descriptors which are located after the - * super block. Each descriptor contains the number of the bitmap block and - * the free blocks count in the block. - */ - -/* - * To avoid calling the atomic setbit hundreds or thousands of times, we only - * need to use it within a single byte (to ensure we get endianness right). - * We can use memset for the rest of the bitmap as there are no other users. - */ -void mark_bitmap_end(int start_bit, int end_bit, char *bitmap) -{ - int i; - - if (start_bit >= end_bit) - return; - - DEBUG(DL_INF, ("mark end bits +%d through +%d used\n", start_bit, end_bit)); - for (i = start_bit; (unsigned)i < ((start_bit + 7) & ~7UL); i++) - ext4_set_bit(i, bitmap); - if (i < end_bit) - memset(bitmap + (i >> 3), 0xff, (end_bit - i) >> 3); -} - -/* Initializes an uninitialized inode bitmap */ -unsigned ext4_init_inode_bitmap(struct super_block *sb, struct buffer_head *bh, - ext4_group_t block_group, - struct ext4_group_desc *gdp) -{ - struct ext3_sb_info *sbi = EXT3_SB(sb); - - mark_buffer_dirty(bh); - - /* If checksum is bad mark all blocks and inodes use to prevent - * allocation, essentially implementing a per-group read-only flag. */ - if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) { - ext4_error(sb, __FUNCTION__, "Checksum bad for group %u", - block_group); - ext4_free_blks_set(sb, gdp, 0); - ext4_free_inodes_set(sb, gdp, 0); - ext4_itable_unused_set(sb, gdp, 0); - memset(bh->b_data, 0xff, sb->s_blocksize); - return 0; - } - - memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8); - mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), sb->s_blocksize * 8, - bh->b_data); - ext4_itable_unused_set(sb, gdp, EXT4_INODES_PER_GROUP(sb)); - - return EXT4_INODES_PER_GROUP(sb); -} - -/* - * Calculate the block group number and offset, given a block number - */ -void ext4_get_group_no_and_offset(struct super_block *sb, ext4_fsblk_t blocknr, - ext4_group_t *blockgrpp, ext4_grpblk_t *offsetp) -{ - struct ext3_super_block *es = EXT3_SB(sb)->s_es; - ext4_grpblk_t offset; - - blocknr = blocknr - le32_to_cpu(es->s_first_data_block); - offset = do_div(blocknr, EXT4_BLOCKS_PER_GROUP(sb)); - if (offsetp) - *offsetp = offset; - if (blockgrpp) - *blockgrpp = (ext4_grpblk_t)blocknr; - -} - -static int ext4_block_in_group(struct super_block *sb, ext4_fsblk_t block, - ext4_group_t block_group) -{ - ext4_group_t actual_group; - ext4_get_group_no_and_offset(sb, block, &actual_group, NULL); - if (actual_group == block_group) - return 1; - return 0; -} - -static int ext4_group_used_meta_blocks(struct super_block *sb, - ext4_group_t block_group) -{ - ext4_fsblk_t tmp; - struct ext3_sb_info *sbi = EXT3_SB(sb); - /* block bitmap, inode bitmap, and inode table blocks */ - int used_blocks = sbi->s_itb_per_group + 2; - - if (EXT3_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) { - struct ext4_group_desc *gdp; - struct buffer_head *bh = NULL; - - gdp = ext4_get_group_desc(sb, block_group, &bh); - if (!ext4_block_in_group(sb, ext4_block_bitmap(sb, gdp), - block_group)) - used_blocks--; - - if (!ext4_block_in_group(sb, ext4_inode_bitmap(sb, gdp), - block_group)) - used_blocks--; - - tmp = ext4_inode_table(sb, gdp); - for (; tmp < ext4_inode_table(sb, gdp) + - sbi->s_itb_per_group; tmp++) { - if (!ext4_block_in_group(sb, tmp, block_group)) - used_blocks -= 1; - } - if (bh) - fini_bh(&bh); - } - return used_blocks; -} - -/* Initializes an uninitialized block bitmap if given, and returns the - * number of blocks free in the group. */ -unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, - ext4_group_t block_group, struct ext4_group_desc *gdp) -{ - int bit, bit_max; - unsigned free_blocks, group_blocks; - struct ext3_sb_info *sbi = EXT3_SB(sb); - - if (bh) { - mark_buffer_dirty(bh); - /* If checksum is bad mark all blocks used to prevent allocation - * essentially implementing a per-group read-only flag. */ - if (!ext4_group_desc_csum_verify(sbi, block_group, gdp)) { - ext4_error(sb, __FUNCTION__, - "Checksum bad for group %u", block_group); - ext4_free_blks_set(sb, gdp, 0); - ext4_free_inodes_set(sb, gdp, 0); - ext4_itable_unused_set(sb, gdp, 0); - memset(bh->b_data, 0xff, sb->s_blocksize); - return 0; - } - memset(bh->b_data, 0, sb->s_blocksize); - } - - /* Check for superblock and gdt backups in this group */ - bit_max = ext3_bg_has_super(sb, block_group); - - if (!EXT3_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG) || - block_group < le32_to_cpu(sbi->s_es->s_first_meta_bg) * - sbi->s_desc_per_block) { - if (bit_max) { - bit_max += ext4_bg_num_gdb(sb, block_group); - bit_max += - le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks); - } - } else { /* For META_BG_BLOCK_GROUPS */ - bit_max += ext4_bg_num_gdb(sb, block_group); - } - - if (block_group == sbi->s_groups_count - 1) { - /* - * Even though mke2fs always initialize first and last group - * if some other tool enabled the EXT4_BG_BLOCK_UNINIT we need - * to make sure we calculate the right free blocks - */ - group_blocks = (unsigned int)(ext3_blocks_count(sbi->s_es) - - le32_to_cpu(sbi->s_es->s_first_data_block) - - (EXT4_BLOCKS_PER_GROUP(sb) * (sbi->s_groups_count - 1))); - } else { - group_blocks = EXT4_BLOCKS_PER_GROUP(sb); - } - - free_blocks = group_blocks - bit_max; - - if (bh) { - ext4_fsblk_t start, tmp; - int flex_bg = 0; - - for (bit = 0; bit < bit_max; bit++) - ext4_set_bit(bit, bh->b_data); - - start = ext3_group_first_block_no(sb, block_group); - - if (EXT3_HAS_INCOMPAT_FEATURE(sb, - EXT4_FEATURE_INCOMPAT_FLEX_BG)) - flex_bg = 1; - - /* Set bits for block and inode bitmaps, and inode table */ - tmp = ext4_block_bitmap(sb, gdp); - if (!flex_bg || ext4_block_in_group(sb, tmp, block_group)) - ext4_set_bit(tmp - start, bh->b_data); - - tmp = ext4_inode_bitmap(sb, gdp); - if (!flex_bg || ext4_block_in_group(sb, tmp, block_group)) - ext4_set_bit(tmp - start, bh->b_data); - - tmp = ext4_inode_table(sb, gdp); - for (; tmp < ext4_inode_table(sb, gdp) + - sbi->s_itb_per_group; tmp++) { - if (!flex_bg || - ext4_block_in_group(sb, tmp, block_group)) - ext4_set_bit(tmp - start, bh->b_data); - } - /* - * Also if the number of blocks within the group is - * less than the blocksize * 8 ( which is the size - * of bitmap ), set rest of the block bitmap to 1 - */ - mark_bitmap_end(group_blocks, sb->s_blocksize * 8, bh->b_data); - } - return free_blocks - ext4_group_used_meta_blocks(sb, block_group); -} - -/** - * ext4_get_group_desc() -- load group descriptor from disk - * @sb: super block - * @block_group: given block group - * @bh: pointer to the buffer head to store the block - * group descriptor - */ -struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb, - ext4_group_t block_group, struct buffer_head **bh) -{ - struct ext4_group_desc *desc = NULL; - struct ext3_sb_info *sbi = EXT3_SB(sb); - PEXT2_VCB vcb = sb->s_priv; - ext4_group_t group; - ext4_group_t offset; - - if (bh) - *bh = NULL; - - if (block_group >= sbi->s_groups_count) { - ext4_error(sb, "ext4_get_group_desc", - "block_group >= groups_count - " - "block_group = %u, groups_count = %u", - block_group, sbi->s_groups_count); - - return NULL; - } - - __try { - - group = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb); - offset = block_group & (EXT4_DESC_PER_BLOCK(sb) - 1); - - if (!sbi->s_gd) { - if (!Ext2LoadGroup(vcb)) { - __leave; - } - } else if ( !sbi->s_gd[group].block || - !sbi->s_gd[group].bh) { - if (!Ext2LoadGroupBH(vcb)) { - __leave; - } - } - - desc = (struct ext4_group_desc *)((PCHAR)sbi->s_gd[group].gd + - offset * EXT4_DESC_SIZE(sb)); - if (bh) { - atomic_inc(&sbi->s_gd[group].bh->b_count); - *bh = sbi->s_gd[group].bh; - } - } __finally { - /* do cleanup */ - } - - return desc; -} - - -/** - * ext4_count_free_blocks() -- count filesystem free blocks - * @sb: superblock - * - * Adds up the number of free blocks from each block group. - */ -ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb) -{ - ext4_fsblk_t desc_count; - struct ext4_group_desc *gdp; - struct buffer_head *bh = NULL; - ext4_group_t i; - ext4_group_t ngroups = EXT3_SB(sb)->s_groups_count; - - desc_count = 0; - smp_rmb(); - for (i = 0; i < ngroups; i++) { - gdp = ext4_get_group_desc(sb, i, &bh); - if (!bh) - continue; - desc_count += ext4_free_blks_count(sb, gdp); - fini_bh(&bh); - } - - return desc_count; -} - -unsigned long ext4_count_free_inodes(struct super_block *sb) -{ - unsigned long desc_count; - struct ext4_group_desc *gdp; - struct buffer_head *bh = NULL; - ext4_group_t i; - - desc_count = 0; - for (i = 0; i < EXT3_SB(sb)->s_groups_count; i++) { - gdp = ext4_get_group_desc(sb, i, &bh); - if (!bh) - continue; - desc_count += ext4_free_inodes_count(sb, gdp); - fini_bh(&bh); - } - return desc_count; -} - -/* Called at mount-time, super-block is locked */ -unsigned long ext4_count_dirs(struct super_block * sb) -{ - struct ext4_group_desc *gdp; - struct buffer_head *bh = NULL; - unsigned long count = 0; - ext4_group_t i; - - for (i = 0; i < EXT3_SB(sb)->s_groups_count; i++) { - gdp = ext4_get_group_desc(sb, i, &bh); - if (!bh) - continue; - count += ext4_used_dirs_count(sb, gdp); - fini_bh(&bh); - } - return count; -} - -/* Called at mount-time, super-block is locked */ -int ext4_check_descriptors(struct super_block *sb) -{ - PEXT2_VCB Vcb = sb->s_priv; - struct ext3_sb_info *sbi = EXT3_SB(sb); - ext4_fsblk_t first_block = le32_to_cpu(sbi->s_es->s_first_data_block); - ext4_fsblk_t last_block; - ext4_fsblk_t block_bitmap; - ext4_fsblk_t inode_bitmap; - ext4_fsblk_t inode_table; - int flexbg_flag = 0; - ext4_group_t i; - - if (EXT3_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) - flexbg_flag = 1; - - DEBUG(DL_INF, ("Checking group descriptors")); - - for (i = 0; i < sbi->s_groups_count; i++) { - - struct buffer_head *bh = NULL; - struct ext4_group_desc *gdp = ext4_get_group_desc(sb, i, &bh); - - if (!bh) - continue; - - if (i == sbi->s_groups_count - 1 || flexbg_flag) - last_block = ext3_blocks_count(sbi->s_es) - 1; - else - last_block = first_block + - (EXT3_BLOCKS_PER_GROUP(sb) - 1); - - block_bitmap = ext4_block_bitmap(sb, gdp); - if (block_bitmap < first_block || block_bitmap > last_block) { - printk(KERN_ERR "EXT4-fs: ext4_check_descriptors: " - "Block bitmap for group %u not in group " - "(block %llu)!\n", i, block_bitmap); - __brelse(bh); - return 0; - } - inode_bitmap = ext4_inode_bitmap(sb, gdp); - if (inode_bitmap < first_block || inode_bitmap > last_block) { - printk(KERN_ERR "EXT4-fs: ext4_check_descriptors: " - "Inode bitmap for group %u not in group " - "(block %llu)!\n", i, inode_bitmap); - __brelse(bh); - return 0; - } - inode_table = ext4_inode_table(sb, gdp); - if (inode_table < first_block || - inode_table + sbi->s_itb_per_group - 1 > last_block) { - printk(KERN_ERR "EXT4-fs: ext4_check_descriptors: " - "Inode table for group %u not in group " - "(block %llu)!\n", i, inode_table); - __brelse(bh); - return 0; - } - - if (!ext4_group_desc_csum_verify(sbi, i, gdp)) { - printk(KERN_ERR "EXT4-fs: ext4_check_descriptors: " - "Checksum for group %u failed (%u!=%u)\n", - i, le16_to_cpu(ext4_group_desc_csum(sbi, i, - gdp)), - le16_to_cpu(gdp->bg_checksum)); - if (!IsVcbReadOnly(Vcb)) { - __brelse(bh); - return 0; - } - } - - if (!flexbg_flag) - first_block += EXT4_BLOCKS_PER_GROUP(sb); - - __brelse(bh); - } - - ext3_free_blocks_count_set(sbi->s_es, ext4_count_free_blocks(sb)); - sbi->s_es->s_free_inodes_count = cpu_to_le32(ext4_count_free_inodes(sb)); - return 1; -} +/* + * COPYRIGHT: See COPYRIGHT.TXT + * PROJECT: Ext2 File System Driver for WinNT/2K/XP + * FILE: generic.c + * PROGRAMMER: Matt Wu + * HOMEPAGE: http://www.ext2fsd.com + * UPDATE HISTORY: + */ + +/* INCLUDES *****************************************************************/ + +#include "ext2fs.h" +#include "linux\ext4.h" + +/* GLOBALS ***************************************************************/ + +extern PEXT2_GLOBAL Ext2Global; + +/* DEFINITIONS *************************************************************/ + + +/* FUNCTIONS ***************************************************************/ + +NTSTATUS +Ext2LoadSuper(IN PEXT2_VCB Vcb, + IN BOOLEAN bVerify, + OUT PEXT2_SUPER_BLOCK * Sb) +{ + NTSTATUS Status; + PEXT2_SUPER_BLOCK Ext2Sb = NULL; + + Ext2Sb = (PEXT2_SUPER_BLOCK) + Ext2AllocatePool( + PagedPool, + sizeof(EXT2_SUPER_BLOCK), + EXT2_SB_MAGIC + ); + if (!Ext2Sb) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + + Status = Ext2ReadDisk( + Vcb, + (ULONGLONG) SUPER_BLOCK_OFFSET, + sizeof(EXT2_SUPER_BLOCK), + (PVOID) Ext2Sb, + bVerify ); + + if (!NT_SUCCESS(Status)) { + Ext2FreePool(Ext2Sb, EXT2_SB_MAGIC); + Ext2Sb = NULL; + } + +errorout: + + *Sb = Ext2Sb; + return Status; +} + + +BOOLEAN +Ext2SaveSuper( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb +) +{ + LONGLONG offset; + BOOLEAN rc; + + ext4_superblock_csum_set(&Vcb->sb); + offset = (LONGLONG) SUPER_BLOCK_OFFSET; + rc = Ext2SaveBuffer( IrpContext, + Vcb, + offset, + sizeof(EXT2_SUPER_BLOCK), + Vcb->SuperBlock + ); + + if (IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) { + Ext2StartFloppyFlushDpc(Vcb, NULL, NULL); + } + + return rc; +} + + +BOOLEAN +Ext2RefreshSuper ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb +) +{ + LONGLONG offset; + IO_STATUS_BLOCK iosb; + + offset = (LONGLONG) SUPER_BLOCK_OFFSET; + if (!CcCopyRead( + Vcb->Volume, + (PLARGE_INTEGER)&offset, + sizeof(EXT2_SUPER_BLOCK), + TRUE, + (PVOID)Vcb->SuperBlock, + &iosb )) { + return FALSE; + } + + if (!NT_SUCCESS(iosb.Status)) { + return FALSE; + } + + /* reload root inode */ + if (Vcb->McbTree) { + + if (!Ext2LoadInode(Vcb, &Vcb->McbTree->Inode)) + return FALSE; + + /* initializeroot node */ + Vcb->McbTree->CreationTime = Ext2NtTime(Vcb->McbTree->Inode.i_ctime); + Vcb->McbTree->LastAccessTime = Ext2NtTime(Vcb->McbTree->Inode.i_atime); + Vcb->McbTree->LastWriteTime = Ext2NtTime(Vcb->McbTree->Inode.i_mtime); + Vcb->McbTree->ChangeTime = Ext2NtTime(Vcb->McbTree->Inode.i_mtime); + } + + return TRUE; +} + +VOID +Ext2DropGroupBH(IN PEXT2_VCB Vcb) +{ + struct ext4_sb_info *sbi = &Vcb->sbi; + unsigned long i; + + if (NULL == Vcb->sbi.s_gd) { + return; + } + + for (i = 0; i < Vcb->sbi.s_gdb_count; i++) { + if (Vcb->sbi.s_gd[i].bh) { + fini_bh(&sbi->s_gd[i].bh); + Vcb->sbi.s_gd[i].bh = NULL; + } + } +} + +VOID +Ext2PutGroup(IN PEXT2_VCB Vcb) +{ + struct ext4_sb_info *sbi = &Vcb->sbi; + unsigned long i; + + + if (NULL == Vcb->sbi.s_gd) { + return; + } + + Ext2DropGroupBH(Vcb); + + kfree(Vcb->sbi.s_gd); + Vcb->sbi.s_gd = NULL; + + ClearFlag(Vcb->Flags, VCB_GD_LOADED); +} + + +BOOLEAN +Ext2LoadGroupBH(IN PEXT2_VCB Vcb) +{ + struct super_block *sb = &Vcb->sb; + struct ext4_sb_info *sbi = &Vcb->sbi; + unsigned long i; + BOOLEAN rc = FALSE; + + __try { + + ExAcquireResourceExclusiveLite(&Vcb->sbi.s_gd_lock, TRUE); + ASSERT (NULL != sbi->s_gd); + + for (i = 0; i < sbi->s_gdb_count; i++) { + ASSERT (sbi->s_gd[i].block); + if (sbi->s_gd[i].bh) + continue; + sbi->s_gd[i].bh = sb_getblk(sb, sbi->s_gd[i].block); + if (!sbi->s_gd[i].bh) { + DEBUG(DL_ERR, ("Ext2LoadGroupBH: can't read group descriptor %d\n", i)); + DbgBreak(); + __leave; + } + sbi->s_gd[i].gd = (struct ext4_group_desc *)sbi->s_gd[i].bh->b_data; + } + + rc = TRUE; + + } __finally { + + ExReleaseResourceLite(&Vcb->sbi.s_gd_lock); + } + + return rc; +} + + +BOOLEAN +Ext2LoadGroup(IN PEXT2_VCB Vcb) +{ + struct super_block *sb = &Vcb->sb; + struct ext4_sb_info *sbi = &Vcb->sbi; + ext3_fsblk_t sb_block = 1; + unsigned long i; + BOOLEAN rc = FALSE; + + __try { + + ExAcquireResourceExclusiveLite(&Vcb->sbi.s_gd_lock, TRUE); + + if (NULL == sbi->s_gd) { + sbi->s_gd = kzalloc(sbi->s_gdb_count * sizeof(struct ext3_gd), + GFP_KERNEL); + } + if (sbi->s_gd == NULL) { + DEBUG(DL_ERR, ("Ext2LoadGroup: not enough memory.\n")); + __leave; + } + + if (BLOCK_SIZE != EXT3_MIN_BLOCK_SIZE) { + sb_block = EXT4_MIN_BLOCK_SIZE / BLOCK_SIZE; + } + + for (i = 0; i < sbi->s_gdb_count; i++) { + sbi->s_gd[i].block = descriptor_loc(sb, sb_block, i); + if (!sbi->s_gd[i].block) { + DEBUG(DL_ERR, ("Ext2LoadGroup: can't locate group descriptor %d\n", i)); + __leave; + } + } + + if (!Ext2LoadGroupBH(Vcb)) { + DEBUG(DL_ERR, ("Ext2LoadGroup: Failed to load group descriptions !\n")); + __leave; + } + + if (!ext4_check_descriptors(sb)) { + DbgBreak(); + DEBUG(DL_ERR, ("Ext2LoadGroup: group descriptors corrupted !\n")); + __leave; + } + + SetFlag(Vcb->Flags, VCB_GD_LOADED); + rc = TRUE; + + } __finally { + + if (!rc) + Ext2PutGroup(Vcb); + + ExReleaseResourceLite(&Vcb->sbi.s_gd_lock); + } + + return rc; +} + +VOID +Ext2DropBH(IN PEXT2_VCB Vcb) +{ + struct ext4_sb_info *sbi = &Vcb->sbi; + + /* do nothing if Vcb is not initialized yet */ + if (!IsFlagOn(Vcb->Flags, VCB_INITIALIZED)) + return; + + __try { + + /* acquire bd lock to avoid bh creation */ + ExAcquireResourceExclusiveLite(&Vcb->bd.bd_bh_lock, TRUE); + + SetFlag(Vcb->Flags, VCB_BEING_DROPPED); + Ext2DropGroupBH(Vcb); + + while (!IsListEmpty(&Vcb->bd.bd_bh_free)) { + struct buffer_head *bh; + PLIST_ENTRY l; + l = RemoveHeadList(&Vcb->bd.bd_bh_free); + bh = CONTAINING_RECORD(l, struct buffer_head, b_link); + InitializeListHead(&bh->b_link); + if (0 == atomic_read(&bh->b_count)) { + buffer_head_remove(&Vcb->bd, bh); + free_buffer_head(bh); + } + } + + } __finally { + ExReleaseResourceLite(&Vcb->bd.bd_bh_lock); + } + + ClearFlag(Vcb->Flags, VCB_BEING_DROPPED); +} + + +VOID +Ext2FlushRange(IN PEXT2_VCB Vcb, LARGE_INTEGER s, LARGE_INTEGER e) +{ + ULONG len; + + if (e.QuadPart <= s.QuadPart) + return; + + /* loop per 2G */ + while (s.QuadPart < e.QuadPart) { + if (e.QuadPart > s.QuadPart + 1024 * 1024 * 1024) { + len = 1024 * 1024 * 1024; + } else { + len = (ULONG) (e.QuadPart - s.QuadPart); + } + CcFlushCache(&Vcb->SectionObject, &s, len, NULL); + s.QuadPart += len; + } +} + +NTSTATUS +Ext2FlushVcb(IN PEXT2_VCB Vcb) +{ + LARGE_INTEGER s = {0}, o; + struct ext4_sb_info *sbi = &Vcb->sbi; + struct rb_node *node; + struct buffer_head *bh; + + if (!IsFlagOn(Vcb->Flags, VCB_GD_LOADED)) { + CcFlushCache(&Vcb->SectionObject, NULL, 0, NULL); + goto errorout; + } + + ASSERT(ExIsResourceAcquiredExclusiveLite(&Vcb->MainResource)); + + __try { + + /* acqurie gd block */ + ExAcquireResourceExclusiveLite(&Vcb->sbi.s_gd_lock, TRUE); + + /* acquire bd lock to avoid bh creation */ + ExAcquireResourceExclusiveLite(&Vcb->bd.bd_bh_lock, TRUE); + + /* drop unused bh */ + Ext2DropBH(Vcb); + + /* flush volume with all outstanding bh skipped */ + + node = rb_first(&Vcb->bd.bd_bh_root); + while (node) { + + bh = container_of(node, struct buffer_head, b_rb_node); + node = rb_next(node); + + o.QuadPart = bh->b_blocknr << BLOCK_BITS; + ASSERT(o.QuadPart >= s.QuadPart); + + if (o.QuadPart == s.QuadPart) { + s.QuadPart = s.QuadPart + bh->b_size; + continue; + } + + if (o.QuadPart > s.QuadPart) { + Ext2FlushRange(Vcb, s, o); + s.QuadPart = (bh->b_blocknr << BLOCK_BITS) + bh->b_size; + continue; + } + } + + o = Vcb->PartitionInformation.PartitionLength; + Ext2FlushRange(Vcb, s, o); + + } __finally { + + ExReleaseResourceLite(&Vcb->bd.bd_bh_lock); + ExReleaseResourceLite(&Vcb->sbi.s_gd_lock); + } + +errorout: + return STATUS_SUCCESS; +} + + +BOOLEAN +Ext2SaveGroup( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN ULONG Group +) +{ + struct ext4_group_desc *gd; + struct buffer_head *gb = NULL; + unsigned long i; + + gd = ext4_get_group_desc(&Vcb->sb, Group, &gb); + if (!gd) + return 0; + + ext4_group_desc_csum_set(&Vcb->sb, Group, gd); + mark_buffer_dirty(gb); + fini_bh(&gb); + + return TRUE; +} + + +BOOLEAN +Ext2RefreshGroup( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb +) +{ + return TRUE; +} + +BOOLEAN +Ext2GetInodeLba ( + IN PEXT2_VCB Vcb, + IN ULONG inode, + OUT PLONGLONG offset +) +{ + PEXT2_GROUP_DESC gd; + struct buffer_head *bh = NULL; + ext4_fsblk_t loc; + int group; + + if (inode < 1 || inode > INODES_COUNT) { + DEBUG(DL_ERR, ( "Ext2GetInodeLba: Inode value %xh is invalid.\n",inode)); + *offset = 0; + return FALSE; + } + + group = (inode - 1) / INODES_PER_GROUP ; + gd = ext4_get_group_desc(&Vcb->sb, group, &bh); + if (!bh) { + *offset = 0; + DbgBreak(); + return FALSE; + } + loc = (LONGLONG)ext4_inode_table(&Vcb->sb, gd); + loc = loc << BLOCK_BITS; + loc = loc + ((inode - 1) % INODES_PER_GROUP) * Vcb->InodeSize; + + *offset = loc; + __brelse(bh); + + return TRUE; +} + +void Ext2DecodeInode(struct inode *dst, struct ext4_inode *src) +{ + dst->i_mode = src->i_mode; + dst->i_flags = src->i_flags; + dst->i_uid = src->i_uid; + dst->i_gid = src->i_gid; + dst->i_nlink = src->i_links_count; + dst->i_generation = src->i_generation; + dst->i_size = src->i_size_lo; + if (S_ISREG(src->i_mode)) { + dst->i_size |= (loff_t)src->i_size_high << 32; + } + dst->i_file_acl = src->i_file_acl_lo; + dst->i_file_acl |= (ext4_fsblk_t)src->osd2.linux2.l_i_file_acl_high << 32; + dst->i_atime = src->i_atime; + dst->i_ctime = src->i_ctime; + dst->i_mtime = src->i_mtime; + dst->i_dtime = src->i_dtime; + dst->i_blocks = ext3_inode_blocks(src, dst); + memcpy(&dst->i_block[0], &src->i_block[0], sizeof(__u32) * 15); + if (EXT4_HAS_RO_COMPAT_FEATURE(dst->i_sb, + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)) + dst->i_extra_isize = src->i_extra_isize; + else + dst->i_extra_isize = 0; +} + +void Ext2EncodeInode(struct ext4_inode *dst, struct inode *src) +{ + dst->i_mode = src->i_mode; + dst->i_flags = src->i_flags; + dst->i_uid = src->i_uid; + dst->i_gid = src->i_gid; + dst->i_links_count = src->i_nlink; + dst->i_generation = src->i_generation; + dst->i_size_lo = (__u32)src->i_size; + if (S_ISREG(src->i_mode)) { + dst->i_size_high = (__u32)(src->i_size >> 32); + } + dst->i_file_acl_lo = (__u32)src->i_file_acl; + dst->osd2.linux2.l_i_file_acl_high |= (__u16)(src->i_file_acl >> 32); + dst->i_atime = src->i_atime; + dst->i_ctime = src->i_ctime; + dst->i_mtime = src->i_mtime; + dst->i_dtime = src->i_dtime; + dst->i_extra_isize = src->i_extra_isize; + ASSERT(src->i_sb); + ext3_inode_blocks_set(dst, src); + memcpy(&dst->i_block[0], &src->i_block[0], sizeof(__u32) * 15); + if (EXT4_HAS_RO_COMPAT_FEATURE(src->i_sb, + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)) + dst->i_extra_isize = src->i_extra_isize; +} + + +BOOLEAN +Ext2LoadInode (IN PEXT2_VCB Vcb, + IN struct inode *Inode) +{ + struct ext4_inode ext3i = {0}; + struct ext4_inode_info ei = {0}; + LONGLONG offset; + + if (!Ext2GetInodeLba(Vcb, Inode->i_ino, &offset)) { + DEBUG(DL_ERR, ("Ext2LoadInode: failed inode %u.\n", Inode->i_ino)); + return FALSE; + } + + if (!Ext2LoadBuffer(NULL, Vcb, offset, sizeof(ext3i), &ext3i)) { + return FALSE; + } + + Ext2DecodeInode(Inode, &ext3i); + + if (!ext4_inode_csum_verify(Inode, &ext3i, &ei)) { + //DbgPrint("inod %d checksum invalid\n", Inode->i_ino); + } + + return TRUE; +} + + +BOOLEAN +Ext2ClearInode ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN ULONG Inode) +{ + LONGLONG Offset = 0; + BOOLEAN rc; + + rc = Ext2GetInodeLba(Vcb, Inode, &Offset); + if (!rc) { + DEBUG(DL_ERR, ( "Ext2SaveInode: failed inode %u.\n", Inode)); + goto errorout; + } + + rc = Ext2ZeroBuffer(IrpContext, Vcb, Offset, Vcb->InodeSize); + +errorout: + + return rc; +} + +BOOLEAN +Ext2SaveInode ( IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN struct inode *Inode) +{ + struct ext4_inode ext4i = {0}; + struct ext4_inode_info ei = {0}; + + LONGLONG Offset = 0; + ULONG InodeSize = sizeof(ext4i); + BOOLEAN rc = 0; + + DEBUG(DL_INF, ( "Ext2SaveInode: Saving Inode %xh: Mode=%xh Size=%xh\n", + Inode->i_ino, Inode->i_mode, Inode->i_size)); + rc = Ext2GetInodeLba(Vcb, Inode->i_ino, &Offset); + if (!rc) { + DEBUG(DL_ERR, ( "Ext2SaveInode: failed inode %u.\n", Inode->i_ino)); + goto errorout; + } + + rc = Ext2LoadBuffer(NULL, Vcb, Offset, InodeSize, &ext4i); + if (!rc) { + DEBUG(DL_ERR, ( "Ext2SaveInode: failed reading inode %u.\n", Inode->i_ino)); + goto errorout;; + } + + Ext2EncodeInode(&ext4i, Inode); + + ext4_inode_csum_set(Inode, &ext4i, &ei); + + if (InodeSize > Vcb->InodeSize) + { + DbgPrint("InodeSize > Vcb->InodeSize\n"); + InodeSize = Vcb->InodeSize; + } + rc = Ext2SaveBuffer(IrpContext, Vcb, Offset, InodeSize, &ext4i); + + if (rc && IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) { + Ext2StartFloppyFlushDpc(Vcb, NULL, NULL); + } + +errorout: + return rc; +} + +BOOLEAN +Ext2LoadInodeXattr(IN PEXT2_VCB Vcb, + IN struct inode *Inode, + IN PEXT2_INODE InodeXattr) +{ + IO_STATUS_BLOCK IoStatus; + LONGLONG Offset; + + if (!Ext2GetInodeLba(Vcb, Inode->i_ino, &Offset)) { + DEBUG(DL_ERR, ("Ext2LoadRawInode: error get inode(%xh)'s addr.\n", Inode->i_ino)); + return FALSE; + } + + if (!CcCopyRead( + Vcb->Volume, + (PLARGE_INTEGER)&Offset, + Vcb->InodeSize, + PIN_WAIT, + (PVOID)InodeXattr, + &IoStatus)) { + return FALSE; + } + + if (!NT_SUCCESS(IoStatus.Status)) { + return FALSE; + } + + Ext2EncodeInode(InodeXattr, Inode); + return TRUE; +} + +BOOLEAN +Ext2SaveInodeXattr(IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN struct inode *Inode, + IN PEXT2_INODE InodeXattr) +{ + IO_STATUS_BLOCK IoStatus; + LONGLONG Offset = 0; + ULONG InodeSize = Vcb->InodeSize; + BOOLEAN rc = 0; + + /* There is no way to put EA information in such a small inode */ + if (InodeSize == EXT2_GOOD_OLD_INODE_SIZE) + return FALSE; + + DEBUG(DL_INF, ("Ext2SaveInodeXattr: Saving Inode %xh: Mode=%xh Size=%xh\n", + Inode->i_ino, Inode->i_mode, Inode->i_size)); + rc = Ext2GetInodeLba(Vcb, Inode->i_ino, &Offset); + if (!rc) { + DEBUG(DL_ERR, ("Ext2SaveInodeXattr: error get inode(%xh)'s addr.\n", Inode->i_ino)); + goto errorout; + } + + rc = Ext2SaveBuffer(IrpContext, + Vcb, + Offset + EXT2_GOOD_OLD_INODE_SIZE + Inode->i_extra_isize, + InodeSize - EXT2_GOOD_OLD_INODE_SIZE - Inode->i_extra_isize, + (char *)InodeXattr + EXT2_GOOD_OLD_INODE_SIZE + Inode->i_extra_isize); + + if (rc && IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) { + Ext2StartFloppyFlushDpc(Vcb, NULL, NULL); + } + +errorout: + return rc; +} + + +BOOLEAN +Ext2LoadBlock (IN PEXT2_VCB Vcb, + IN ULONG Index, + IN PVOID Buffer ) +{ + struct buffer_head *bh = NULL; + BOOLEAN rc = 0; + + __try { + + bh = sb_getblk(&Vcb->sb, (sector_t)Index); + + if (!bh) { + DEBUG(DL_ERR, ("Ext2Loadblock: can't load block %u\n", Index)); + DbgBreak(); + __leave; + } + + if (!buffer_uptodate(bh)) { + int err = bh_submit_read(bh); + if (err < 0) { + DEBUG(DL_ERR, ("Ext2LoadBlock: reading failed %d\n", err)); + __leave; + } + } + + RtlCopyMemory(Buffer, bh->b_data, BLOCK_SIZE); + rc = TRUE; + + } __finally { + + if (bh) + fini_bh(&bh); + } + + return rc; +} + + +BOOLEAN +Ext2SaveBlock ( IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN ULONG Index, + IN PVOID Buf ) +{ + struct buffer_head *bh = NULL; + BOOLEAN rc = 0; + + __try { + + bh = sb_getblk_zero(&Vcb->sb, (sector_t)Index); + + if (!bh) { + DEBUG(DL_ERR, ("Ext2Saveblock: can't load block %u\n", Index)); + DbgBreak(); + __leave; + } + + if (!buffer_uptodate(bh)) { + } + + RtlCopyMemory(bh->b_data, Buf, BLOCK_SIZE); + mark_buffer_dirty(bh); + rc = TRUE; + + } __finally { + + if (bh) + fini_bh(&bh); + } + + return rc; +} + +BOOLEAN +Ext2LoadBuffer( IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN LONGLONG offset, + IN ULONG size, + IN PVOID buf ) +{ + struct buffer_head *bh = NULL; + BOOLEAN rc; + + __try { + + while (size) { + + sector_t block; + ULONG len = 0, delta = 0; + + block = (sector_t) (offset >> BLOCK_BITS); + delta = (ULONG)offset & (BLOCK_SIZE - 1); + len = BLOCK_SIZE - delta; + if (size < len) + len = size; + + bh = sb_getblk(&Vcb->sb, block); + if (!bh) { + DEBUG(DL_ERR, ("Ext2SaveBuffer: can't load block %I64u\n", block)); + DbgBreak(); + __leave; + } + + if (!buffer_uptodate(bh)) { + int err = bh_submit_read(bh); + if (err < 0) { + DEBUG(DL_ERR, ("Ext2SaveBuffer: bh_submit_read failed: %d\n", err)); + __leave; + } + } + + __try { + RtlCopyMemory(buf, bh->b_data + delta, len); + } __finally { + fini_bh(&bh); + } + + buf = (PUCHAR)buf + len; + offset = offset + len; + size = size - len; + } + + rc = TRUE; + + } __finally { + + if (bh) + fini_bh(&bh); + + } + + return rc; +} + + +BOOLEAN +Ext2ZeroBuffer( IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN LONGLONG offset, + IN ULONG size + ) +{ + struct buffer_head *bh = NULL; + BOOLEAN rc = 0; + + __try { + + while (size) { + + sector_t block; + ULONG len = 0, delta = 0; + + block = (sector_t) (offset >> BLOCK_BITS); + delta = (ULONG)offset & (BLOCK_SIZE - 1); + len = BLOCK_SIZE - delta; + if (size < len) + len = size; + + if (delta == 0 && len >= BLOCK_SIZE) { + bh = sb_getblk_zero(&Vcb->sb, block); + } else { + bh = sb_getblk(&Vcb->sb, block); + } + + if (!bh) { + DEBUG(DL_ERR, ("Ext2SaveBuffer: can't load block %I64u\n", block)); + DbgBreak(); + __leave; + } + + if (!buffer_uptodate(bh)) { + int err = bh_submit_read(bh); + if (err < 0) { + DEBUG(DL_ERR, ("Ext2SaveBuffer: bh_submit_read failed: %d\n", err)); + __leave; + } + } + + __try { + if (delta == 0 && len >= BLOCK_SIZE) { + /* bh (cache) was already cleaned as zero */ + } else { + RtlZeroMemory(bh->b_data + delta, len); + } + mark_buffer_dirty(bh); + } __finally { + fini_bh(&bh); + } + + offset = offset + len; + size = size - len; + } + + rc = TRUE; + + } __finally { + + if (bh) + fini_bh(&bh); + + } + + return rc; +} + + +BOOLEAN +Ext2SaveBuffer( IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN LONGLONG offset, + IN ULONG size, + IN PVOID buf ) +{ + struct buffer_head *bh = NULL; + BOOLEAN rc = 0; + + __try { + + while (size) { + + sector_t block; + ULONG len = 0, delta = 0; + + block = (sector_t) (offset >> BLOCK_BITS); + delta = (ULONG)offset & (BLOCK_SIZE - 1); + len = BLOCK_SIZE - delta; + if (size < len) + len = size; + + if (delta == 0 && len >= BLOCK_SIZE) { + bh = sb_getblk_zero(&Vcb->sb, block); + } else { + bh = sb_getblk(&Vcb->sb, block); + } + + if (!bh) { + DEBUG(DL_ERR, ("Ext2SaveBuffer: can't load block %I64u\n", block)); + DbgBreak(); + __leave; + } + + if (!buffer_uptodate(bh)) { + int err = bh_submit_read(bh); + if (err < 0) { + DEBUG(DL_ERR, ("Ext2SaveBuffer: bh_submit_read failed: %d\n", err)); + __leave; + } + } + + __try { + RtlCopyMemory(bh->b_data + delta, buf, len); + mark_buffer_dirty(bh); + } __finally { + fini_bh(&bh); + } + + buf = (PUCHAR)buf + len; + offset = offset + len; + size = size - len; + } + + rc = TRUE; + + } __finally { + + if (bh) + fini_bh(&bh); + + } + + return rc; +} + + +VOID +Ext2UpdateVcbStat( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb +) +{ + Vcb->SuperBlock->s_free_inodes_count = ext4_count_free_inodes(&Vcb->sb); + ext3_free_blocks_count_set(SUPER_BLOCK, ext4_count_free_blocks(&Vcb->sb)); + Ext2SaveSuper(IrpContext, Vcb); +} + +NTSTATUS +Ext2NewBlock( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN ULONG GroupHint, + IN ULONG BlockHint, + OUT PULONG Block, + IN OUT PULONG Number +) +{ + struct super_block *sb = &Vcb->sb; + PEXT2_GROUP_DESC gd; + struct buffer_head *gb = NULL; + struct buffer_head *bh = NULL; + ext4_fsblk_t bitmap_blk; + + RTL_BITMAP BlockBitmap; + + ULONG Group = 0; + ULONG Index = 0xFFFFFFFF; + ULONG dwHint = 0; + ULONG Count = 0; + ULONG Length = 0; + + NTSTATUS Status = STATUS_DISK_FULL; + + *Block = 0; + + ExAcquireResourceExclusiveLite(&Vcb->MetaBlock, TRUE); + + /* validate the hint group and hint block */ + if (GroupHint >= Vcb->sbi.s_groups_count) { + DbgBreak(); + GroupHint = Vcb->sbi.s_groups_count - 1; + } + + if (BlockHint != 0) { + GroupHint = (BlockHint - EXT2_FIRST_DATA_BLOCK) / BLOCKS_PER_GROUP; + dwHint = (BlockHint - EXT2_FIRST_DATA_BLOCK) % BLOCKS_PER_GROUP; + } + + Group = GroupHint; + +Again: + + if (bh) + fini_bh(&bh); + + if (gb) + fini_bh(&gb); + + gd = ext4_get_group_desc(sb, Group, &gb); + if (!gd) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + + bitmap_blk = ext4_block_bitmap(sb, gd); + + if (gd->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { + bh = sb_getblk_zero(sb, bitmap_blk); + if (!bh) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + ext4_group_desc_csum_set(sb, Group, gd); + ext4_init_block_bitmap(sb, bh, Group, gd); + set_buffer_uptodate(bh); + gd->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT); + Ext2SaveGroup(IrpContext, Vcb, Group); + } else { + bh = sb_getblk(sb, bitmap_blk); + if (!bh) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + } + + if (!buffer_uptodate(bh)) { + int err = bh_submit_read(bh); + if (err < 0) { + DbgPrint("bh_submit_read error! err: %d\n", err); + Status = Ext2WinntError(err); + goto errorout; + } + } + + if (ext4_free_blks_count(sb, gd)) { + + if (Group == Vcb->sbi.s_groups_count - 1) { + + Length = (ULONG)(TOTAL_BLOCKS % BLOCKS_PER_GROUP); + + /* s_blocks_count is integer multiple of s_blocks_per_group */ + if (Length == 0) { + Length = BLOCKS_PER_GROUP; + } + } else { + Length = BLOCKS_PER_GROUP; + } + + /* initialize bitmap buffer */ + RtlInitializeBitMap(&BlockBitmap, (PULONG)bh->b_data, Length); + + /* try to find a clear bit range */ + Index = RtlFindClearBits(&BlockBitmap, *Number, dwHint); + + /* We could not get new block in the prefered group */ + if (Index == 0xFFFFFFFF) { + + /* search clear bits from the hint block */ + Count = RtlFindNextForwardRunClear(&BlockBitmap, dwHint, &Index); + if (dwHint != 0 && Count == 0) { + /* search clear bits from the very beginning */ + Count = RtlFindNextForwardRunClear(&BlockBitmap, 0, &Index); + } + + if (Count == 0) { + + RtlZeroMemory(&BlockBitmap, sizeof(RTL_BITMAP)); + + /* no blocks found: set bg_free_blocks_count to 0 */ + ext4_free_blks_set(sb, gd, 0); + Ext2SaveGroup(IrpContext, Vcb, Group); + + /* will try next group */ + goto Again; + + } else { + + /* we got free blocks */ + if (Count <= *Number) { + *Number = Count; + } + } + } + + } else { + + /* try next group */ + dwHint = 0; + Group = (Group + 1) % Vcb->sbi.s_groups_count; + if (Group != GroupHint) { + goto Again; + } + + Index = 0xFFFFFFFF; + } + + if (Index < Length) { + + /* mark block bits as allocated */ + RtlSetBits(&BlockBitmap, Index, *Number); + + /* set block bitmap dirty in cache */ + mark_buffer_dirty(bh); + + /* update group description */ + ext4_free_blks_set(sb, gd, RtlNumberOfClearBits(&BlockBitmap)); + ext4_block_bitmap_csum_set(sb, Group, gd, bh); + Ext2SaveGroup(IrpContext, Vcb, Group); + + /* update Vcb free blocks */ + Ext2UpdateVcbStat(IrpContext, Vcb); + + /* validate the new allocated block number */ + *Block = Index + EXT2_FIRST_DATA_BLOCK + Group * BLOCKS_PER_GROUP; + if (*Block >= TOTAL_BLOCKS || *Block + *Number > TOTAL_BLOCKS) { + DbgBreak(); + dwHint = 0; + goto Again; + } + + if (ext4_block_bitmap(sb, gd) == *Block || + ext4_inode_bitmap(sb, gd) == *Block || + ext4_inode_table(sb, gd) == *Block ) { + DbgBreak(); + dwHint = 0; + goto Again; + } + + /* Always remove dirty MCB to prevent Volume's lazy writing. + Metadata blocks will be re-added during modifications.*/ + if (Ext2RemoveBlockExtent(Vcb, NULL, *Block, *Number)) { + } else { + DbgBreak(); + Ext2RemoveBlockExtent(Vcb, NULL, *Block, *Number); + } + + DEBUG(DL_INF, ("Ext2NewBlock: Block %xh - %x allocated.\n", + *Block, *Block + *Number)); + Status = STATUS_SUCCESS; + } + +errorout: + + ExReleaseResourceLite(&Vcb->MetaBlock); + + if (bh) + fini_bh(&bh); + + if (gb) + fini_bh(&gb); + + return Status; +} + +NTSTATUS +Ext2FreeBlock( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN ULONG Block, + IN ULONG Number +) +{ + struct super_block *sb = &Vcb->sb; + PEXT2_GROUP_DESC gd; + struct buffer_head *gb = NULL; + struct buffer_head bh; + ext4_fsblk_t bitmap_blk; + + RTL_BITMAP BlockBitmap; + LARGE_INTEGER Offset; + + PBCB BitmapBcb; + PVOID BitmapCache; + + ULONG Group; + ULONG Index; + ULONG Length; + ULONG Count; + + NTSTATUS Status = STATUS_UNSUCCESSFUL; + + ExAcquireResourceExclusiveLite(&Vcb->MetaBlock, TRUE); + + DEBUG(DL_INF, ("Ext2FreeBlock: Block %xh - %x to be freed.\n", + Block, Block + Number)); + + Group = (Block - EXT2_FIRST_DATA_BLOCK) / BLOCKS_PER_GROUP; + Index = (Block - EXT2_FIRST_DATA_BLOCK) % BLOCKS_PER_GROUP; + +Again: + + if (gb) + fini_bh(&gb); + + if ( Block < EXT2_FIRST_DATA_BLOCK || + Block >= TOTAL_BLOCKS || + Group >= Vcb->sbi.s_groups_count) { + + DbgBreak(); + Status = STATUS_SUCCESS; + + } else { + + gd = ext4_get_group_desc(sb, Group, &gb); + if (!gd) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + bitmap_blk = ext4_block_bitmap(sb, gd); + + /* check the block is valid or not */ + if (bitmap_blk >= TOTAL_BLOCKS) { + DbgBreak(); + Status = STATUS_DISK_CORRUPT_ERROR; + goto errorout; + } + + /* get bitmap block offset and length */ + Offset.QuadPart = bitmap_blk; + Offset.QuadPart = Offset.QuadPart << BLOCK_BITS; + + if (Group == Vcb->sbi.s_groups_count - 1) { + + Length = (ULONG)(TOTAL_BLOCKS % BLOCKS_PER_GROUP); + + /* s_blocks_count is integer multiple of s_blocks_per_group */ + if (Length == 0) { + Length = BLOCKS_PER_GROUP; + } + + } else { + Length = BLOCKS_PER_GROUP; + } + + /* read and initialize bitmap */ + if (!CcPinRead( Vcb->Volume, + &Offset, + Vcb->BlockSize, + PIN_WAIT, + &BitmapBcb, + &BitmapCache ) ) { + + DEBUG(DL_ERR, ("Ext2FreeBlock: failed to PinLock bitmap block %xh.\n", + bitmap_blk)); + Status = STATUS_CANT_WAIT; + DbgBreak(); + goto errorout; + } + + /* clear usused bits */ + RtlInitializeBitMap(&BlockBitmap, BitmapCache, Length); + Count = min(Length - Index, Number); + RtlClearBits(&BlockBitmap, Index, Count); + + /* update group description table */ + ext4_free_blks_set(sb, gd, RtlNumberOfClearBits(&BlockBitmap)); + + bh.b_data = BitmapCache; + ext4_block_bitmap_csum_set(sb, Group, gd, &bh); + + /* indict the cache range is dirty */ + CcSetDirtyPinnedData(BitmapBcb, NULL ); + Ext2AddVcbExtent(Vcb, Offset.QuadPart, (LONGLONG)Vcb->BlockSize); + CcUnpinData(BitmapBcb); + BitmapBcb = NULL; + BitmapCache = NULL; + Ext2SaveGroup(IrpContext, Vcb, Group); + + /* remove dirty MCB to prevent Volume's lazy writing. */ + if (Ext2RemoveBlockExtent(Vcb, NULL, Block, Count)) { + } else { + DbgBreak(); + Ext2RemoveBlockExtent(Vcb, NULL, Block, Count); + } + + /* save super block (used/unused blocks statics) */ + Ext2UpdateVcbStat(IrpContext, Vcb); + + /* try next group to clear all remaining */ + Number -= Count; + if (Number) { + Group += 1; + if (Group < Vcb->sbi.s_groups_count) { + Index = 0; + Block += Count; + goto Again; + } else { + DEBUG(DL_ERR, ("Ext2FreeBlock: block number beyonds max group.\n")); + goto errorout; + } + } + } + + Status = STATUS_SUCCESS; + +errorout: + + if (gb) + fini_bh(&gb); + + ExReleaseResourceLite(&Vcb->MetaBlock); + + return Status; +} + + +NTSTATUS +Ext2NewInode( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN ULONG GroupHint, + IN ULONG Type, + OUT PULONG Inode +) +{ + struct super_block *sb = &Vcb->sb; + PEXT2_GROUP_DESC gd; + struct buffer_head *gb = NULL; + struct buffer_head *bh = NULL; + ext4_fsblk_t bitmap_blk; + + RTL_BITMAP InodeBitmap; + + ULONG Group, i, j; + ULONG Average, Length; + + ULONG dwInode; + + NTSTATUS Status = STATUS_DISK_FULL; + + *Inode = dwInode = 0XFFFFFFFF; + + ExAcquireResourceExclusiveLite(&Vcb->MetaInode, TRUE); + + if (GroupHint >= Vcb->sbi.s_groups_count) + GroupHint = GroupHint % Vcb->sbi.s_groups_count; + +repeat: + + if (bh) + fini_bh(&bh); + + if (gb) + fini_bh(&gb); + + Group = i = 0; + gd = NULL; + + if (Type == EXT2_FT_DIR) { + + Average = Vcb->SuperBlock->s_free_inodes_count / Vcb->sbi.s_groups_count; + + for (j = 0; j < Vcb->sbi.s_groups_count; j++) { + + i = (j + GroupHint) % (Vcb->sbi.s_groups_count); + gd = ext4_get_group_desc(sb, i, &gb); + if (!gd) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + + if ((gd->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) || + (ext4_used_dirs_count(sb, gd) << 8 < + ext4_free_inodes_count(sb, gd)) ) { + Group = i + 1; + break; + } + fini_bh(&gb); + } + + if (!Group) { + + PEXT2_GROUP_DESC desc = NULL; + + gd = NULL; + + /* get the group with the biggest vacancy */ + for (j = 0; j < Vcb->sbi.s_groups_count; j++) { + + struct buffer_head *gt = NULL; + desc = ext4_get_group_desc(sb, j, >); + if (!desc) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + + /* return the group if it's not initialized yet */ + if (desc->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) { + Group = j + 1; + gd = desc; + + if (gb) + fini_bh(&gb); + gb = gt; + gt = NULL; + break; + } + + if (!gd) { + if (ext4_free_inodes_count(sb, desc) > 0) { + Group = j + 1; + gd = desc; + if (gb) + fini_bh(&gb); + gb = gt; + gt = NULL; + } + } else { + if (ext4_free_inodes_count(sb, desc) > + ext4_free_inodes_count(sb, gd)) { + Group = j + 1; + gd = desc; + if (gb) + fini_bh(&gb); + gb = gt; + gt = NULL; + break; + } + } + if (gt) + fini_bh(>); + } + } + + } else { + + /* + * Try to place the inode in its parent directory (GroupHint) + */ + + gd = ext4_get_group_desc(sb, GroupHint, &gb); + if (!gb) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + + if (gd->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT) || + ext4_free_inodes_count(sb, gd)) { + + Group = GroupHint + 1; + + } else { + + /* this group is 100% cocucpied */ + fini_bh(&gb); + + i = GroupHint; + + /* + * Use a quadratic hash to find a group with a free inode + */ + + for (j = 1; j < Vcb->sbi.s_groups_count; j <<= 1) { + + + i = (i + j) % Vcb->sbi.s_groups_count; + gd = ext4_get_group_desc(sb, i, &gb); + if (!gd) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + + if (gd->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT) || + ext4_free_inodes_count(sb, gd)) { + Group = i + 1; + break; + } + + fini_bh(&gb); + } + } + + if (!Group) { + /* + * That failed: try linear search for a free inode + */ + i = GroupHint; + for (j = 2; j < Vcb->sbi.s_groups_count; j++) { + + i = (i + 1) % Vcb->sbi.s_groups_count; + gd = ext4_get_group_desc(sb, i, &gb); + if (!gd) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + + if (gd->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT) || + ext4_free_inodes_count(sb, gd)) { + Group = i + 1; + break; + } + + fini_bh(&gb); + } + } + } + + if (gd == NULL || Group == 0) { + goto errorout; + } + + /* finally we got the group, but is it valid ? */ + if (Group > Vcb->sbi.s_groups_count) { + DbgBreak(); + goto errorout; + } + + /* valid group number starts from 1, not 0 */ + Group -= 1; + + ASSERT(gd); + bitmap_blk = ext4_inode_bitmap(sb, gd); + /* check the block is valid or not */ + if (bitmap_blk == 0 || bitmap_blk >= TOTAL_BLOCKS) { + DbgBreak(); + Status = STATUS_DISK_CORRUPT_ERROR; + goto errorout; + } + + if (gd->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) { + bh = sb_getblk_zero(sb, bitmap_blk); + if (!bh) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + ext4_init_inode_bitmap(sb, bh, Group, gd); + set_buffer_uptodate(bh); + gd->bg_flags &= cpu_to_le16(~EXT4_BG_INODE_UNINIT); + ext4_inode_bitmap_csum_set(sb, Group, gd, bh, EXT4_INODES_PER_GROUP(sb) / 8); + ext4_group_desc_csum_set(sb, Group, gd); + Ext2SaveGroup(IrpContext, Vcb, Group); + } else { + bh = sb_getblk(sb, bitmap_blk); + if (!bh) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + } + + if (!buffer_uptodate(bh)) { + int err = bh_submit_read(bh); + if (err < 0) { + DbgPrint("bh_submit_read error! err: %d\n", err); + Status = Ext2WinntError(err); + goto errorout; + } + } + + if (Vcb->sbi.s_groups_count == 1) { + Length = INODES_COUNT; + } else { + if (Group + 1 == Vcb->sbi.s_groups_count) { + Length = INODES_COUNT % INODES_PER_GROUP; + if (!Length) { + /* INODES_COUNT is integer multiple of INODES_PER_GROUP */ + Length = INODES_PER_GROUP; + } + } else { + Length = INODES_PER_GROUP; + } + } + + RtlInitializeBitMap(&InodeBitmap, (PULONG)bh->b_data, Length); + dwInode = RtlFindClearBits(&InodeBitmap, 1, 0); + + if (dwInode == 0xFFFFFFFF || dwInode >= Length) { + + RtlZeroMemory(&InodeBitmap, sizeof(RTL_BITMAP)); + if (ext4_free_inodes_count(sb, gd) > 0) { + ext4_free_inodes_set(sb, gd, 0); + Ext2SaveGroup(IrpContext, Vcb, Group); + } + goto repeat; + + } else { + + __u32 count = 0; + + /* update unused inodes count */ + count = ext4_free_inodes_count(sb, gd) - 1; + ext4_free_inodes_set(sb, gd, count); + + RtlSetBits(&InodeBitmap, dwInode, 1); + + /* set block bitmap dirty in cache */ + mark_buffer_dirty(bh); + + /* If we didn't allocate from within the initialized part of the inode + * table then we need to initialize up to this inode. */ + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { + + __u32 free; + + if (gd->bg_flags & cpu_to_le16(EXT4_BG_INODE_UNINIT)) { + gd->bg_flags &= cpu_to_le16(~EXT4_BG_INODE_UNINIT); + /* When marking the block group with + * ~EXT4_BG_INODE_UNINIT we don't want to depend + * on the value of bg_itable_unused even though + * mke2fs could have initialized the same for us. + * Instead we calculated the value below + */ + + free = 0; + } else { + free = EXT3_INODES_PER_GROUP(sb) - ext4_itable_unused_count(sb, gd); + } + + /* + * Check the relative inode number against the last used + * relative inode number in this group. if it is greater + * we need to update the bg_itable_unused count + * + */ + if (dwInode + 1 > free) { + ext4_itable_unused_set(sb, gd, + (EXT3_INODES_PER_GROUP(sb) - 1 - dwInode)); + } + + /* We may have to initialize the block bitmap if it isn't already */ + if (gd->bg_flags & cpu_to_le16(EXT4_BG_BLOCK_UNINIT)) { + + struct buffer_head *block_bitmap_bh = NULL; + + /* recheck and clear flag under lock if we still need to */ + block_bitmap_bh = sb_getblk_zero(sb, ext4_block_bitmap(sb, gd)); + if (block_bitmap_bh) { + ext4_block_bitmap_csum_set(sb, Group, gd, + block_bitmap_bh); + ext4_group_desc_csum_set(sb, Group, gd); + free = ext4_init_block_bitmap(sb, block_bitmap_bh, Group, gd); + set_buffer_uptodate(block_bitmap_bh); + brelse(block_bitmap_bh); + gd->bg_flags &= cpu_to_le16(~EXT4_BG_BLOCK_UNINIT); + ext4_free_blks_set(sb, gd, free); + Ext2SaveGroup(IrpContext, Vcb, Group); + } + } + } + + *Inode = dwInode + 1 + Group * INODES_PER_GROUP; + + /* update group_desc / super_block */ + if (Type == EXT2_FT_DIR) { + ext4_used_dirs_set(sb, gd, ext4_used_dirs_count(sb, gd) + 1); + } + ext4_inode_bitmap_csum_set(sb, Group, gd, bh, EXT4_INODES_PER_GROUP(sb) / 8); + ext4_group_desc_csum_set(sb, Group, gd); + Ext2SaveGroup(IrpContext, Vcb, Group); + Ext2UpdateVcbStat(IrpContext, Vcb); + Status = STATUS_SUCCESS; + } + +errorout: + + ExReleaseResourceLite(&Vcb->MetaInode); + + if (bh) + fini_bh(&bh); + + if (gb) + fini_bh(&gb); + + + return Status; +} + +NTSTATUS +Ext2UpdateGroupDirStat( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN ULONG group + ) +{ + struct super_block *sb = &Vcb->sb; + PEXT2_GROUP_DESC gd; + struct buffer_head *gb = NULL; + NTSTATUS status; + + ExAcquireResourceExclusiveLite(&Vcb->MetaInode, TRUE); + + /* get group desc */ + gd = ext4_get_group_desc(sb, group, &gb); + if (!gd) { + status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + + /* update group_desc and super_block */ + ext4_used_dirs_set(sb, gd, ext4_used_dirs_count(sb, gd) - 1); + Ext2SaveGroup(IrpContext, Vcb, group); + Ext2UpdateVcbStat(IrpContext, Vcb); + status = STATUS_SUCCESS; + +errorout: + + ExReleaseResourceLite(&Vcb->MetaInode); + + if (gb) + fini_bh(&gb); + + return status; +} + + +NTSTATUS +Ext2FreeInode( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN ULONG Inode, + IN ULONG Type +) +{ + struct super_block *sb = &Vcb->sb; + PEXT2_GROUP_DESC gd; + struct buffer_head *gb = NULL; + struct buffer_head *bh = NULL; + ext4_fsblk_t bitmap_blk; + + RTL_BITMAP InodeBitmap; + ULONG Group; + ULONG Length; + LARGE_INTEGER Offset; + + ULONG dwIno; + BOOLEAN bModified = FALSE; + + NTSTATUS Status = STATUS_UNSUCCESSFUL; + + ExAcquireResourceExclusiveLite(&Vcb->MetaInode, TRUE); + + Group = (Inode - 1) / INODES_PER_GROUP; + dwIno = (Inode - 1) % INODES_PER_GROUP; + + DEBUG(DL_INF, ( "Ext2FreeInode: Inode: %xh (Group/Off = %xh/%xh)\n", + Inode, Group, dwIno)); + + if (Group >= Vcb->sbi.s_groups_count) { + DbgBreak(); + goto errorout; + } + + gd = ext4_get_group_desc(sb, Group, &gb); + if (!gd) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + + bitmap_blk = ext4_inode_bitmap(sb, gd); + bh = sb_getblk(sb, bitmap_blk); + if (!bh) { + DbgBreak(); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + if (!buffer_uptodate(bh)) { + int err = bh_submit_read(bh); + if (err < 0) { + DbgPrint("bh_submit_read error! err: %d\n", err); + Status = Ext2WinntError(err); + goto errorout; + } + } + + if (Group == Vcb->sbi.s_groups_count - 1) { + + Length = INODES_COUNT % INODES_PER_GROUP; + if (!Length) { + /* s_inodes_count is integer multiple of s_inodes_per_group */ + Length = INODES_PER_GROUP; + } + } else { + Length = INODES_PER_GROUP; + } + + RtlInitializeBitMap(&InodeBitmap, (PULONG)bh->b_data, Length); + + if (RtlCheckBit(&InodeBitmap, dwIno) == 0) { + DbgBreak(); + Status = STATUS_SUCCESS; + } else { + RtlClearBits(&InodeBitmap, dwIno, 1); + bModified = TRUE; + } + + if (bModified) { + /* update group free inodes */ + ext4_free_inodes_set(sb, gd, + RtlNumberOfClearBits(&InodeBitmap)); + + /* set inode block dirty and add to vcb dirty range */ + mark_buffer_dirty(bh); + + /* update group_desc and super_block */ + if (Type == EXT2_FT_DIR) { + ext4_used_dirs_set(sb, gd, + ext4_used_dirs_count(sb, gd) - 1); + } + ext4_inode_bitmap_csum_set(sb, Group, gd, bh, EXT4_INODES_PER_GROUP(sb) / 8); + ext4_group_desc_csum_set(sb, Group, gd); + Ext2SaveGroup(IrpContext, Vcb, Group); + Ext2UpdateVcbStat(IrpContext, Vcb); + Status = STATUS_SUCCESS; + } + +errorout: + + ExReleaseResourceLite(&Vcb->MetaInode); + + if (bh) + fini_bh(&bh); + + if (gb) + fini_bh(&gb); + + return Status; +} + + +NTSTATUS +Ext2AddEntry ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_FCB Dcb, + IN struct inode *Inode, + IN PUNICODE_STRING FileName, + struct dentry **Dentry +) +{ + struct dentry *de = NULL; + + NTSTATUS status = STATUS_UNSUCCESSFUL; + OEM_STRING oem; + int rc; + + BOOLEAN MainResourceAcquired = FALSE; + + if (!IsDirectory(Dcb)) { + DbgBreak(); + return STATUS_NOT_A_DIRECTORY; + } + + ExAcquireResourceExclusiveLite(&Dcb->MainResource, TRUE); + MainResourceAcquired = TRUE; + + __try { + + Ext2ReferXcb(&Dcb->ReferenceCount); + de = Ext2BuildEntry(Vcb, Dcb->Mcb, FileName); + if (!de) { + status = STATUS_INSUFFICIENT_RESOURCES; + __leave; + } + de->d_inode = Inode; + + rc = ext3_add_entry(IrpContext, de, Inode); + status = Ext2WinntError(rc); + if (NT_SUCCESS(status)) { + + /* increase dir inode's nlink for .. */ + if (S_ISDIR(Inode->i_mode)) { + ext3_inc_count(Dcb->Inode); + ext3_mark_inode_dirty(IrpContext, Dcb->Inode); + } + + /* increase inode nlink reference */ + ext3_inc_count(Inode); + ext3_mark_inode_dirty(IrpContext, Inode); + + if (Dentry) { + *Dentry = de; + de = NULL; + } + } + + } __finally { + + Ext2DerefXcb(&Dcb->ReferenceCount); + + if (MainResourceAcquired) { + ExReleaseResourceLite(&Dcb->MainResource); + } + + if (de) + Ext2FreeEntry(de); + } + + return status; +} + + +NTSTATUS +Ext2SetFileType ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_FCB Dcb, + IN PEXT2_MCB Mcb, + IN umode_t mode + ) +{ + 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 (!EXT4_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, mode); + mark_buffer_dirty(bh); + + if (S_ISDIR(inode->i_mode) == S_ISDIR(mode)) { + } else if (S_ISDIR(inode->i_mode)) { + ext3_dec_count(dir); + } else if (S_ISDIR(mode)) { + ext3_inc_count(dir); + } + dir->i_ctime = dir->i_mtime = ext3_current_time(dir); + ext3_mark_inode_dirty(IrpContext, dir); + + inode->i_mode = mode; + ext3_mark_inode_dirty(IrpContext, inode); + + 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, + 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; + int rc = -ENOENT; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + BOOLEAN MainResourceAcquired = FALSE; + + 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; + + if (!inode->i_nlink) { + ext3_warning (inode->i_sb, "ext3_unlink", + "Deleting nonexistent file (%lu), %d", + inode->i_ino, inode->i_nlink); + inode->i_nlink = 1; + } + rc = ext3_delete_entry(IrpContext, dir, de, bh); + if (rc) { + Status = Ext2WinntError(rc); + __leave; + } + /* + if (!inode->i_nlink) + ext3_orphan_add(handle, inode); + */ + inode->i_ctime = dir->i_ctime = dir->i_mtime = ext3_current_time(dir); + ext3_dec_count(inode); + ext3_mark_inode_dirty(IrpContext, inode); + + /* decrease dir inode's nlink for .. */ + if (S_ISDIR(inode->i_mode)) { + ext3_update_dx_flag(dir); + ext3_dec_count(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 +Ext2SetParentEntry ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_FCB Dcb, + IN ULONG OldParent, + IN ULONG NewParent ) +{ + NTSTATUS Status = STATUS_UNSUCCESSFUL; + + PEXT2_DIR_ENTRY2 pSelf = NULL; + PEXT2_DIR_ENTRY2 pParent = NULL; + + ULONG dwBytes = 0; + + BOOLEAN MainResourceAcquired = FALSE; + + ULONG Offset = 0; + + if (!IsDirectory(Dcb)) { + return STATUS_NOT_A_DIRECTORY; + } + + if (OldParent == NewParent) { + return STATUS_SUCCESS; + } + + MainResourceAcquired = + ExAcquireResourceExclusiveLite(&Dcb->MainResource, TRUE); + + __try { + + Ext2ReferXcb(&Dcb->ReferenceCount); + + pSelf = (PEXT2_DIR_ENTRY2) + Ext2AllocatePool( + PagedPool, + EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2), + EXT2_DENTRY_MAGIC + ); + if (!pSelf) { + DEBUG(DL_ERR, ( "Ex2SetParentEntry: failed to allocate pSelf.\n")); + Status = STATUS_INSUFFICIENT_RESOURCES; + __leave; + } + + dwBytes = 0; + + // + // Reading the DCB contents + // + + Status = Ext2ReadInode( + IrpContext, + Vcb, + Dcb->Mcb, + (ULONGLONG)Offset, + (PVOID)pSelf, + EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2), + FALSE, + &dwBytes ); + + if (!NT_SUCCESS(Status)) { + DEBUG(DL_ERR, ( "Ext2SetParentEntry: failed to read directory.\n")); + __leave; + } + + ASSERT(dwBytes == EXT2_DIR_REC_LEN(1) + EXT2_DIR_REC_LEN(2)); + + pParent = (PEXT2_DIR_ENTRY2)((PUCHAR)pSelf + pSelf->rec_len); + + if (pSelf->name_len == 1 && pSelf->name[0] == '.' && + pParent->name_len == 2 && pParent->name[0] == '.' && + pParent->name[1] == '.') { + + if (pParent->inode != OldParent) { + DbgBreak(); + } + pParent->inode = NewParent; + + Status = Ext2WriteInode( + IrpContext, + Vcb, + Dcb->Mcb, + (ULONGLONG)Offset, + pSelf, + dwBytes, + FALSE, + &dwBytes ); + } else { + DbgBreak(); + } + + } __finally { + + + if (Ext2DerefXcb(&Dcb->ReferenceCount) == 0) { + DEBUG(DL_ERR, ( "Ext2SetParentEntry: Dcb reference goes to ZERO.\n")); + } + + if (MainResourceAcquired) { + ExReleaseResourceLite(&Dcb->MainResource); + } + + if (pSelf) { + Ext2FreePool(pSelf, EXT2_DENTRY_MAGIC); + } + } + + return Status; +} + +int ext3_check_dir_entry (const char * function, struct inode * dir, + struct ext3_dir_entry_2 * de, + struct buffer_head * bh, + unsigned long offset) +{ + const char * error_msg = NULL; + const int rlen = ext3_rec_len_from_disk(de->rec_len); + + if (rlen < EXT3_DIR_REC_LEN(1)) + error_msg = "rec_len is smaller than minimal"; + else if (rlen % 4 != 0) + error_msg = "rec_len % 4 != 0"; + else if (rlen < EXT3_DIR_REC_LEN(de->name_len)) + error_msg = "rec_len is too small for name_len"; + else if ((char *) de + rlen > bh->b_data + dir->i_sb->s_blocksize) + error_msg = "directory entry across blocks"; + else if (le32_to_cpu(de->inode) > + le32_to_cpu(EXT4_SB(dir->i_sb)->s_es->s_inodes_count)) + error_msg = "inode out of bounds"; + + if (error_msg != NULL) { + DEBUG(DL_ERR, ("%s: bad entry in directory %u: %s - " + "offset=%u, inode=%u, rec_len=%d, name_len=%d\n", + function, dir->i_ino, error_msg, offset, + (unsigned long) le32_to_cpu(de->inode), + rlen, de->name_len)); + } + return error_msg == NULL ? 1 : 0; +} + + +/* + * p is at least 6 bytes before the end of page + */ +struct ext3_dir_entry_2 * + ext3_next_entry(struct ext3_dir_entry_2 *p) +{ + return (struct ext3_dir_entry_2 *)((char *)p + + ext3_rec_len_from_disk(p->rec_len)); +} + +#define MAX_LFS_FILESIZE 0x7fffffffffffffff + +/* + * Maximal extent format file size. + * Resulting logical blkno at s_maxbytes must fit in our on-disk + * extent format containers, within a sector_t, and within i_blocks + * in the vfs. ext4 inode has 48 bits of i_block in fsblock units, + * so that won't be a limiting factor. + * + * Note, this does *not* consider any metadata overhead for vfs i_blocks. + */ +static loff_t ext4_max_size(int blkbits, int has_huge_files) +{ + loff_t res; + loff_t upper_limit = MAX_LFS_FILESIZE; + + /* small i_blocks in vfs inode? */ + if (!has_huge_files || sizeof(blkcnt_t) < sizeof(u64)) { + /* + * CONFIG_LBD is not enabled implies the inode + * i_block represent total blocks in 512 bytes + * 32 == size of vfs inode i_blocks * 8 + */ + upper_limit = (1LL << 32) - 1; + + /* total blocks in file system block size */ + upper_limit >>= (blkbits - 9); + upper_limit <<= blkbits; + } + + /* 32-bit extent-start container, ee_block */ + res = 1LL << 32; + res <<= blkbits; + res -= 1; + + /* Sanity check against vm- & vfs- imposed limits */ + if (res > upper_limit) + res = upper_limit; + + return res; +} + +/* + * Maximal extent format file size. + * Resulting logical blkno at s_maxbytes must fit in our on-disk + * extent format containers, within a sector_t, and within i_blocks + * in the vfs. ext4 inode has 48 bits of i_block in fsblock units, + * so that won't be a limiting factor. + * + * Note, this does *not* consider any metadata overhead for vfs i_blocks. + */ +loff_t ext3_max_size(int blkbits, int has_huge_files) +{ + loff_t res; + loff_t upper_limit = MAX_LFS_FILESIZE; + + /* small i_blocks in vfs inode? */ + if (!has_huge_files) { + /* + * CONFIG_LBD is not enabled implies the inode + * i_block represent total blocks in 512 bytes + * 32 == size of vfs inode i_blocks * 8 + */ + upper_limit = ((loff_t)1 << 32) - 1; + + /* total blocks in file system block size */ + upper_limit >>= (blkbits - 9); + upper_limit <<= blkbits; + } + + /* 32-bit extent-start container, ee_block */ + res = (loff_t)1 << 32; + res <<= blkbits; + res -= 1; + + /* Sanity check against vm- & vfs- imposed limits */ + if (res > upper_limit) + res = upper_limit; + + return res; +} + +/* + * Maximal bitmap file size. There is a direct, and {,double-,triple-}indirect + * block limit, and also a limit of (2^48 - 1) 512-byte sectors in i_blocks. + * We need to be 1 filesystem block less than the 2^48 sector limit. + */ +loff_t ext3_max_bitmap_size(int bits, int has_huge_files) +{ + loff_t res = EXT3_NDIR_BLOCKS; + int meta_blocks; + loff_t upper_limit; + /* This is calculated to be the largest file size for a + * dense, bitmapped file such that the total number of + * sectors in the file, including data and all indirect blocks, + * does not exceed 2^48 -1 + * __u32 i_blocks_lo and _u16 i_blocks_high representing the + * total number of 512 bytes blocks of the file + */ + + if (!has_huge_files) { + /* + * !has_huge_files or CONFIG_LBD is not enabled + * implies the inode i_block represent total blocks in + * 512 bytes 32 == size of vfs inode i_blocks * 8 + */ + upper_limit = ((loff_t)1 << 32) - 1; + + /* total blocks in file system block size */ + upper_limit >>= (bits - 9); + + } else { + /* + * We use 48 bit ext4_inode i_blocks + * With EXT4_HUGE_FILE_FL set the i_blocks + * represent total number of blocks in + * file system block size + */ + upper_limit = ((loff_t)1 << 48) - 1; + + } + + /* indirect blocks */ + meta_blocks = 1; + /* double indirect blocks */ + meta_blocks += 1 + ((loff_t)1 << (bits-2)); + /* tripple indirect blocks */ + meta_blocks += 1 + ((loff_t)1 << (bits-2)) + ((loff_t)1 << (2*(bits-2))); + + upper_limit -= meta_blocks; + upper_limit <<= bits; + + res += (loff_t)1 << (bits-2); + res += (loff_t)1 << (2*(bits-2)); + res += (loff_t)1 << (3*(bits-2)); + res <<= bits; + if (res > upper_limit) + res = upper_limit; + + if (res > MAX_LFS_FILESIZE) + res = MAX_LFS_FILESIZE; + + return res; +} + +blkcnt_t ext3_inode_blocks(struct ext4_inode *raw_inode, + struct inode *inode) +{ + blkcnt_t i_blocks ; + struct super_block *sb = inode->i_sb; + PEXT2_VCB Vcb = (PEXT2_VCB)sb->s_priv; + + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, + EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) { + /* we are using combined 48 bit field */ + i_blocks = ((u64)le16_to_cpu(raw_inode->i_blocks_high)) << 32 | + le32_to_cpu(raw_inode->i_blocks_lo); + if (inode->i_flags & EXT4_HUGE_FILE_FL) { + /* i_blocks represent file system block size */ + return i_blocks << (BLOCK_BITS - 9); + } else { + return i_blocks; + } + } else { + return le32_to_cpu(raw_inode->i_blocks_lo); + } +} + +int ext3_inode_blocks_set(struct ext4_inode *raw_inode, + struct inode * inode) +{ + u64 i_blocks = inode->i_blocks; + struct super_block *sb = inode->i_sb; + PEXT2_VCB Vcb = (PEXT2_VCB)sb->s_priv; + + if (i_blocks < 0x100000000) { + /* + * i_blocks can be represnted in a 32 bit variable + * as multiple of 512 bytes + */ + raw_inode->i_blocks_lo = cpu_to_le32(i_blocks); + raw_inode->i_blocks_high = 0; + inode->i_flags &= ~EXT4_HUGE_FILE_FL; + return 0; + } + + if (!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) { + EXT3_SET_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_HUGE_FILE); + Ext2SaveSuper(NULL, Vcb); + } + + if (i_blocks <= 0xffffffffffff) { + /* + * i_blocks can be represented in a 48 bit variable + * as multiple of 512 bytes + */ + raw_inode->i_blocks_lo = (__u32)cpu_to_le32(i_blocks); + raw_inode->i_blocks_high = (__u16)cpu_to_le16(i_blocks >> 32); + inode->i_flags &= ~EXT4_HUGE_FILE_FL; + } else { + inode->i_flags |= EXT4_HUGE_FILE_FL; + /* i_block is stored in file system block size */ + i_blocks = i_blocks >> (BLOCK_BITS - 9); + raw_inode->i_blocks_lo = (__u32)cpu_to_le32(i_blocks); + raw_inode->i_blocks_high = (__u16)cpu_to_le16(i_blocks >> 32); + } + return 0; +} + +ext4_fsblk_t ext4_block_bitmap(struct super_block *sb, + struct ext4_group_desc *bg) +{ + return le32_to_cpu(bg->bg_block_bitmap_lo) | + (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? + (ext4_fsblk_t)le32_to_cpu(bg->bg_block_bitmap_hi) << 32 : 0); +} + +ext4_fsblk_t ext4_inode_bitmap(struct super_block *sb, + struct ext4_group_desc *bg) +{ + return le32_to_cpu(bg->bg_inode_bitmap_lo) | + (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? + (ext4_fsblk_t)le32_to_cpu(bg->bg_inode_bitmap_hi) << 32 : 0); +} + +ext4_fsblk_t ext4_inode_table(struct super_block *sb, + struct ext4_group_desc *bg) +{ + return le32_to_cpu(bg->bg_inode_table_lo) | + (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? + (ext4_fsblk_t)le32_to_cpu(bg->bg_inode_table_hi) << 32 : 0); +} + +__u32 ext4_free_blks_count(struct super_block *sb, + struct ext4_group_desc *bg) +{ + return le16_to_cpu(bg->bg_free_blocks_count_lo) | + (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? + (__u32)le16_to_cpu(bg->bg_free_blocks_count_hi) << 16 : 0); +} + +__u32 ext4_free_inodes_count(struct super_block *sb, + struct ext4_group_desc *bg) +{ + return le16_to_cpu(bg->bg_free_inodes_count_lo) | + (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? + (__u32)le16_to_cpu(bg->bg_free_inodes_count_hi) << 16 : 0); +} + +__u32 ext4_used_dirs_count(struct super_block *sb, + struct ext4_group_desc *bg) +{ + return le16_to_cpu(bg->bg_used_dirs_count_lo) | + (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? + (__u32)le16_to_cpu(bg->bg_used_dirs_count_hi) << 16 : 0); +} + +__u32 ext4_itable_unused_count(struct super_block *sb, + struct ext4_group_desc *bg) +{ + return le16_to_cpu(bg->bg_itable_unused_lo) | + (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT ? + (__u32)le16_to_cpu(bg->bg_itable_unused_hi) << 16 : 0); +} + +void ext4_block_bitmap_set(struct super_block *sb, + struct ext4_group_desc *bg, ext4_fsblk_t blk) +{ + bg->bg_block_bitmap_lo = cpu_to_le32((u32)blk); + if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) + bg->bg_block_bitmap_hi = cpu_to_le32(blk >> 32); +} + +void ext4_inode_bitmap_set(struct super_block *sb, + struct ext4_group_desc *bg, ext4_fsblk_t blk) +{ + bg->bg_inode_bitmap_lo = cpu_to_le32((u32)blk); + if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) + bg->bg_inode_bitmap_hi = cpu_to_le32(blk >> 32); +} + +void ext4_inode_table_set(struct super_block *sb, + struct ext4_group_desc *bg, ext4_fsblk_t blk) +{ + bg->bg_inode_table_lo = cpu_to_le32((u32)blk); + if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) + bg->bg_inode_table_hi = cpu_to_le32(blk >> 32); +} + +void ext4_free_blks_set(struct super_block *sb, + struct ext4_group_desc *bg, __u32 count) +{ + bg->bg_free_blocks_count_lo = cpu_to_le16((__u16)count); + if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) + bg->bg_free_blocks_count_hi = cpu_to_le16(count >> 16); +} + +void ext4_free_inodes_set(struct super_block *sb, + struct ext4_group_desc *bg, __u32 count) +{ + bg->bg_free_inodes_count_lo = cpu_to_le16((__u16)count); + if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) + bg->bg_free_inodes_count_hi = cpu_to_le16(count >> 16); +} + +void ext4_used_dirs_set(struct super_block *sb, + struct ext4_group_desc *bg, __u32 count) +{ + bg->bg_used_dirs_count_lo = cpu_to_le16((__u16)count); + if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) + bg->bg_used_dirs_count_hi = cpu_to_le16(count >> 16); +} + +void ext4_itable_unused_set(struct super_block *sb, + struct ext4_group_desc *bg, __u32 count) +{ + bg->bg_itable_unused_lo = cpu_to_le16((__u16)count); + if (EXT4_DESC_SIZE(sb) >= EXT4_MIN_DESC_SIZE_64BIT) + bg->bg_itable_unused_hi = cpu_to_le16(count >> 16); +} + +static inline int test_root(ext3_group_t a, ext3_group_t b) +{ + ext3_group_t num = b; + + while (a > num) + num *= b; + return num == a; +} + +static int ext3_group_sparse(ext3_group_t group) +{ + if (group <= 1) + return 1; + if (!(group & 1)) + return 0; + return (test_root(group, 7) || test_root(group, 5) || + test_root(group, 3)); +} + +/** + * ext4_bg_has_super - number of blocks used by the superblock in group + * @sb: superblock for filesystem + * @group: group number to check + * + * Return the number of blocks used by the superblock (primary or backup) + * in this group. Currently this will be only 0 or 1. + */ +int ext3_bg_has_super(struct super_block *sb, ext3_group_t group) +{ + if (EXT4_HAS_RO_COMPAT_FEATURE(sb, + EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER) && + !ext3_group_sparse(group)) + return 0; + return 1; +} + +static unsigned long ext4_bg_num_gdb_meta(struct super_block *sb, + ext4_group_t group) +{ + unsigned long metagroup = group / EXT4_DESC_PER_BLOCK(sb); + ext4_group_t first = metagroup * EXT4_DESC_PER_BLOCK(sb); + ext4_group_t last = first + EXT4_DESC_PER_BLOCK(sb) - 1; + + if (group == first || group == first + 1 || group == last) + return 1; + return 0; +} + +static unsigned long ext4_bg_num_gdb_nometa(struct super_block *sb, + ext4_group_t group) +{ + return ext3_bg_has_super(sb, group) ? EXT4_SB(sb)->s_gdb_count : 0; +} + +/** + * ext4_bg_num_gdb - number of blocks used by the group table in group + * @sb: superblock for filesystem + * @group: group number to check + * + * Return the number of blocks used by the group descriptor table + * (primary or backup) in this group. In the future there may be a + * different number of descriptor blocks in each group. + */ +unsigned long ext4_bg_num_gdb(struct super_block *sb, ext4_group_t group) +{ + unsigned long first_meta_bg = + le32_to_cpu(EXT4_SB(sb)->s_es->s_first_meta_bg); + unsigned long metagroup = group / EXT4_DESC_PER_BLOCK(sb); + + if (!EXT4_HAS_INCOMPAT_FEATURE(sb,EXT4_FEATURE_INCOMPAT_META_BG) || + metagroup < first_meta_bg) + return ext4_bg_num_gdb_nometa(sb, group); + + return ext4_bg_num_gdb_meta(sb,group); + +} + +ext3_fsblk_t descriptor_loc(struct super_block *sb, + ext3_fsblk_t logical_sb_block, unsigned int nr) +{ + struct ext4_sb_info *sbi = EXT4_SB(sb); + ext3_group_t bg, first_meta_bg; + int has_super = 0; + + first_meta_bg = le32_to_cpu(sbi->s_es->s_first_meta_bg); + + if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG) || + nr < first_meta_bg) + return logical_sb_block + nr + 1; + bg = sbi->s_desc_per_block * nr; + if (ext3_bg_has_super(sb, bg)) + has_super = 1; + return (has_super + ext3_group_first_block_no(sb, bg)); +} + +#define ext4_set_bit(n, p) set_bit((int)(n), (unsigned long *)(p)) + +/* + * The free inodes are managed by bitmaps. A file system contains several + * blocks groups. Each group contains 1 bitmap block for blocks, 1 bitmap + * block for inodes, N blocks for the inode table and data blocks. + * + * The file system contains group descriptors which are located after the + * super block. Each descriptor contains the number of the bitmap block and + * the free blocks count in the block. + */ + +/* + * To avoid calling the atomic setbit hundreds or thousands of times, we only + * need to use it within a single byte (to ensure we get endianness right). + * We can use memset for the rest of the bitmap as there are no other users. + */ +void mark_bitmap_end(int start_bit, int end_bit, char *bitmap) +{ + int i; + + if (start_bit >= end_bit) + return; + + DEBUG(DL_INF, ("mark end bits +%d through +%d used\n", start_bit, end_bit)); + for (i = start_bit; (unsigned)i < ((start_bit + 7) & ~7UL); i++) + ext4_set_bit(i, bitmap); + if (i < end_bit) + memset(bitmap + (i >> 3), 0xff, (end_bit - i) >> 3); +} + +/* Initializes an uninitialized inode bitmap */ +unsigned ext4_init_inode_bitmap(struct super_block *sb, struct buffer_head *bh, + ext4_group_t block_group, + struct ext4_group_desc *gdp) +{ + struct ext4_sb_info *sbi = EXT4_SB(sb); + + mark_buffer_dirty(bh); + + /* If checksum is bad mark all blocks and inodes use to prevent + * allocation, essentially implementing a per-group read-only flag. */ + if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) { + ext4_error(sb, __FUNCTION__, "Checksum bad for group %u", + block_group); + ext4_free_blks_set(sb, gdp, 0); + ext4_free_inodes_set(sb, gdp, 0); + ext4_itable_unused_set(sb, gdp, 0); + memset(bh->b_data, 0xff, sb->s_blocksize); + return 0; + } + + memset(bh->b_data, 0, (EXT4_INODES_PER_GROUP(sb) + 7) / 8); + mark_bitmap_end(EXT4_INODES_PER_GROUP(sb), sb->s_blocksize * 8, + bh->b_data); + ext4_itable_unused_set(sb, gdp, EXT4_INODES_PER_GROUP(sb)); + + return EXT4_INODES_PER_GROUP(sb); +} + +/* + * Calculate the block group number and offset, given a block number + */ +void ext4_get_group_no_and_offset(struct super_block *sb, ext4_fsblk_t blocknr, + ext4_group_t *blockgrpp, ext4_grpblk_t *offsetp) +{ + struct ext4_super_block *es = EXT4_SB(sb)->s_es; + ext4_grpblk_t offset; + + blocknr = blocknr - le32_to_cpu(es->s_first_data_block); + offset = do_div(blocknr, EXT4_BLOCKS_PER_GROUP(sb)); + if (offsetp) + *offsetp = offset; + if (blockgrpp) + *blockgrpp = (ext4_grpblk_t)blocknr; + +} + +static int ext4_block_in_group(struct super_block *sb, ext4_fsblk_t block, + ext4_group_t block_group) +{ + ext4_group_t actual_group; + ext4_get_group_no_and_offset(sb, block, &actual_group, NULL); + if (actual_group == block_group) + return 1; + return 0; +} + +static int ext4_group_used_meta_blocks(struct super_block *sb, + ext4_group_t block_group) +{ + ext4_fsblk_t tmp; + struct ext4_sb_info *sbi = EXT4_SB(sb); + /* block bitmap, inode bitmap, and inode table blocks */ + int used_blocks = sbi->s_itb_per_group + 2; + + if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) { + struct ext4_group_desc *gdp; + struct buffer_head *bh = NULL; + + gdp = ext4_get_group_desc(sb, block_group, &bh); + if (!ext4_block_in_group(sb, ext4_block_bitmap(sb, gdp), + block_group)) + used_blocks--; + + if (!ext4_block_in_group(sb, ext4_inode_bitmap(sb, gdp), + block_group)) + used_blocks--; + + tmp = ext4_inode_table(sb, gdp); + for (; tmp < ext4_inode_table(sb, gdp) + + sbi->s_itb_per_group; tmp++) { + if (!ext4_block_in_group(sb, tmp, block_group)) + used_blocks -= 1; + } + if (bh) + fini_bh(&bh); + } + return used_blocks; +} + +/* Initializes an uninitialized block bitmap if given, and returns the + * number of blocks free in the group. */ +unsigned ext4_init_block_bitmap(struct super_block *sb, struct buffer_head *bh, + ext4_group_t block_group, struct ext4_group_desc *gdp) +{ + int bit, bit_max; + unsigned free_blocks, group_blocks; + struct ext4_sb_info *sbi = EXT4_SB(sb); + + if (bh) { + mark_buffer_dirty(bh); + /* If checksum is bad mark all blocks used to prevent allocation + * essentially implementing a per-group read-only flag. */ + if (!ext4_group_desc_csum_verify(sb, block_group, gdp)) { + ext4_error(sb, __FUNCTION__, + "Checksum bad for group %u", block_group); + ext4_free_blks_set(sb, gdp, 0); + ext4_free_inodes_set(sb, gdp, 0); + ext4_itable_unused_set(sb, gdp, 0); + memset(bh->b_data, 0xff, sb->s_blocksize); + return 0; + } + memset(bh->b_data, 0, sb->s_blocksize); + } + + /* Check for superblock and gdt backups in this group */ + bit_max = ext3_bg_has_super(sb, block_group); + + if (!EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_META_BG) || + block_group < le32_to_cpu(sbi->s_es->s_first_meta_bg) * + sbi->s_desc_per_block) { + if (bit_max) { + bit_max += ext4_bg_num_gdb(sb, block_group); + bit_max += + le16_to_cpu(sbi->s_es->s_reserved_gdt_blocks); + } + } else { /* For META_BG_BLOCK_GROUPS */ + bit_max += ext4_bg_num_gdb(sb, block_group); + } + + if (block_group == sbi->s_groups_count - 1) { + /* + * Even though mke2fs always initialize first and last group + * if some other tool enabled the EXT4_BG_BLOCK_UNINIT we need + * to make sure we calculate the right free blocks + */ + group_blocks = (unsigned int)(ext3_blocks_count(sbi->s_es) - + le32_to_cpu(sbi->s_es->s_first_data_block) - + (EXT4_BLOCKS_PER_GROUP(sb) * (sbi->s_groups_count - 1))); + } else { + group_blocks = EXT4_BLOCKS_PER_GROUP(sb); + } + + free_blocks = group_blocks - bit_max; + + if (bh) { + ext4_fsblk_t start, tmp; + int flex_bg = 0; + + for (bit = 0; bit < bit_max; bit++) + ext4_set_bit(bit, bh->b_data); + + start = ext3_group_first_block_no(sb, block_group); + + if (EXT4_HAS_INCOMPAT_FEATURE(sb, + EXT4_FEATURE_INCOMPAT_FLEX_BG)) + flex_bg = 1; + + /* Set bits for block and inode bitmaps, and inode table */ + tmp = ext4_block_bitmap(sb, gdp); + if (!flex_bg || ext4_block_in_group(sb, tmp, block_group)) + ext4_set_bit(tmp - start, bh->b_data); + + tmp = ext4_inode_bitmap(sb, gdp); + if (!flex_bg || ext4_block_in_group(sb, tmp, block_group)) + ext4_set_bit(tmp - start, bh->b_data); + + tmp = ext4_inode_table(sb, gdp); + for (; tmp < ext4_inode_table(sb, gdp) + + sbi->s_itb_per_group; tmp++) { + if (!flex_bg || + ext4_block_in_group(sb, tmp, block_group)) + ext4_set_bit(tmp - start, bh->b_data); + } + /* + * Also if the number of blocks within the group is + * less than the blocksize * 8 ( which is the size + * of bitmap ), set rest of the block bitmap to 1 + */ + mark_bitmap_end(group_blocks, sb->s_blocksize * 8, bh->b_data); + } + return free_blocks - ext4_group_used_meta_blocks(sb, block_group); +} + +/** + * ext4_get_group_desc() -- load group descriptor from disk + * @sb: super block + * @block_group: given block group + * @bh: pointer to the buffer head to store the block + * group descriptor + */ +struct ext4_group_desc * ext4_get_group_desc(struct super_block *sb, + ext4_group_t block_group, struct buffer_head **bh) +{ + struct ext4_group_desc *desc = NULL; + struct ext4_sb_info *sbi = EXT4_SB(sb); + PEXT2_VCB vcb = sb->s_priv; + ext4_group_t group; + ext4_group_t offset; + + if (bh) + *bh = NULL; + + if (block_group >= sbi->s_groups_count) { + ext4_error(sb, "ext4_get_group_desc", + "block_group >= groups_count - " + "block_group = %u, groups_count = %u", + block_group, sbi->s_groups_count); + + return NULL; + } + + __try { + + group = block_group >> EXT4_DESC_PER_BLOCK_BITS(sb); + offset = block_group & (EXT4_DESC_PER_BLOCK(sb) - 1); + + if (!sbi->s_gd) { + if (!Ext2LoadGroup(vcb)) { + __leave; + } + } else if ( !sbi->s_gd[group].block || + !sbi->s_gd[group].bh) { + if (!Ext2LoadGroupBH(vcb)) { + __leave; + } + } + + desc = (struct ext4_group_desc *)((PCHAR)sbi->s_gd[group].gd + + offset * EXT4_DESC_SIZE(sb)); + if (bh) { + atomic_inc(&sbi->s_gd[group].bh->b_count); + *bh = sbi->s_gd[group].bh; + } + } __finally { + /* do cleanup */ + } + + return desc; +} + + +/** + * ext4_count_free_blocks() -- count filesystem free blocks + * @sb: superblock + * + * Adds up the number of free blocks from each block group. + */ +ext4_fsblk_t ext4_count_free_blocks(struct super_block *sb) +{ + ext4_fsblk_t desc_count; + struct ext4_group_desc *gdp; + struct buffer_head *bh = NULL; + ext4_group_t i; + ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count; + + desc_count = 0; + smp_rmb(); + for (i = 0; i < ngroups; i++) { + gdp = ext4_get_group_desc(sb, i, &bh); + if (!bh) + continue; + desc_count += ext4_free_blks_count(sb, gdp); + fini_bh(&bh); + } + + return desc_count; +} + +unsigned long ext4_count_free_inodes(struct super_block *sb) +{ + unsigned long desc_count; + struct ext4_group_desc *gdp; + struct buffer_head *bh = NULL; + ext4_group_t i; + + desc_count = 0; + for (i = 0; i < EXT4_SB(sb)->s_groups_count; i++) { + gdp = ext4_get_group_desc(sb, i, &bh); + if (!bh) + continue; + desc_count += ext4_free_inodes_count(sb, gdp); + fini_bh(&bh); + } + return desc_count; +} + +/* Called at mount-time, super-block is locked */ +unsigned long ext4_count_dirs(struct super_block * sb) +{ + struct ext4_group_desc *gdp; + struct buffer_head *bh = NULL; + unsigned long count = 0; + ext4_group_t i; + + for (i = 0; i < EXT4_SB(sb)->s_groups_count; i++) { + gdp = ext4_get_group_desc(sb, i, &bh); + if (!bh) + continue; + count += ext4_used_dirs_count(sb, gdp); + fini_bh(&bh); + } + return count; +} + +/* Called at mount-time, super-block is locked */ +int ext4_check_descriptors(struct super_block *sb) +{ + PEXT2_VCB Vcb = sb->s_priv; + struct ext4_sb_info *sbi = EXT4_SB(sb); + ext4_fsblk_t first_block = le32_to_cpu(sbi->s_es->s_first_data_block); + ext4_fsblk_t last_block; + ext4_fsblk_t block_bitmap; + ext4_fsblk_t inode_bitmap; + ext4_fsblk_t inode_table; + int flexbg_flag = 0; + ext4_group_t i; + + if (EXT4_HAS_INCOMPAT_FEATURE(sb, EXT4_FEATURE_INCOMPAT_FLEX_BG)) + flexbg_flag = 1; + + DEBUG(DL_INF, ("Checking group descriptors")); + + for (i = 0; i < sbi->s_groups_count; i++) { + + struct buffer_head *bh = NULL; + struct ext4_group_desc *gdp = ext4_get_group_desc(sb, i, &bh); + + if (!bh) + continue; + + if (i == sbi->s_groups_count - 1 || flexbg_flag) + last_block = ext3_blocks_count(sbi->s_es) - 1; + else + last_block = first_block + + (EXT3_BLOCKS_PER_GROUP(sb) - 1); + + block_bitmap = ext4_block_bitmap(sb, gdp); + if (block_bitmap < first_block || block_bitmap > last_block) { + printk(KERN_ERR "EXT4-fs: ext4_check_descriptors: " + "Block bitmap for group %u not in group " + "(block %llu)!\n", i, block_bitmap); + __brelse(bh); + return 0; + } + inode_bitmap = ext4_inode_bitmap(sb, gdp); + if (inode_bitmap < first_block || inode_bitmap > last_block) { + printk(KERN_ERR "EXT4-fs: ext4_check_descriptors: " + "Inode bitmap for group %u not in group " + "(block %llu)!\n", i, inode_bitmap); + __brelse(bh); + return 0; + } + inode_table = ext4_inode_table(sb, gdp); + if (inode_table < first_block || + inode_table + sbi->s_itb_per_group - 1 > last_block) { + printk(KERN_ERR "EXT4-fs: ext4_check_descriptors: " + "Inode table for group %u not in group " + "(block %llu)!\n", i, inode_table); + __brelse(bh); + return 0; + } + + if (!ext4_group_desc_csum_verify(sb, i, gdp)) { + printk(KERN_ERR "EXT4-fs: ext4_check_descriptors: " + "Checksum for group %u failed.\n", i); + if (!IsVcbReadOnly(Vcb)) { + //__brelse(bh); + //return 0; + } + } + + if (!flexbg_flag) + first_block += EXT4_BLOCKS_PER_GROUP(sb); + + __brelse(bh); + } + + ext3_free_blocks_count_set(sbi->s_es, ext4_count_free_blocks(sb)); + sbi->s_es->s_free_inodes_count = cpu_to_le32(ext4_count_free_inodes(sb)); + return 1; +} diff --git a/Ext4Fsd/ext3/htree.c b/Ext4Fsd/ext3/htree.c index 709f394..8bc397f 100644 --- a/Ext4Fsd/ext3/htree.c +++ b/Ext4Fsd/ext3/htree.c @@ -758,7 +758,7 @@ static int call_filldir(struct file * filp, void * cookie, } return 0; } - +#if 0 struct fake_dirent { __le32 inode; @@ -823,7 +823,7 @@ struct dx_map_entry __u16 offs; __u16 size; }; - +#endif /* * Future: use high four bits of block for coalesce-on-delete flags * Mask them off for now. diff --git a/Ext4Fsd/ext3/recover.c b/Ext4Fsd/ext3/recover.c index ff3e07e..e30a80c 100644 --- a/Ext4Fsd/ext3/recover.c +++ b/Ext4Fsd/ext3/recover.c @@ -11,7 +11,6 @@ #include #include -#include /* GLOBALS ***************************************************************/ diff --git a/Ext4Fsd/ext4/SOURCES b/Ext4Fsd/ext4/SOURCES index 4655235..7214bf8 100644 --- a/Ext4Fsd/ext4/SOURCES +++ b/Ext4Fsd/ext4/SOURCES @@ -25,5 +25,5 @@ DRIVERTYPE=FS INCLUDES=.;..;..\include;$(DRIVER_INC_PATH); # The source code: -SOURCES= ext4_bh.c ext4_extents.c ext4_jbd2.c extents.c \ - ext4_xattr.c +SOURCES= ext4_bh.c ext4_csum.c ext4_extents.c ext4_jbd2.c ext4_xattr.c \ + extents.c diff --git a/Ext4Fsd/ext4/ext4_csum.c b/Ext4Fsd/ext4/ext4_csum.c new file mode 100644 index 0000000..1dd5c80 --- /dev/null +++ b/Ext4Fsd/ext4/ext4_csum.c @@ -0,0 +1,757 @@ +/* + * COPYRIGHT: See COPYRIGHT.TXT + * PROJECT: Ext2-Ext4 File System Driver for WinXP-Win10 + * FILE: ext4_csum.c + * PROGRAMMER: Bo Brantén + * HOMEPAGE: http://www.ext2fsd.com + * UPDATE HISTORY: + */ + +/* INCLUDES *****************************************************************/ + +#include "ext2fs.h" +#include "linux\ext4.h" +#include "linux\ext4_xattr.h" + +/* GLOBALS ***************************************************************/ + +extern PEXT2_GLOBAL Ext2Global; + +/* DEFINITIONS *************************************************************/ + + +/* FUNCTIONS ***************************************************************/ + +/** CRC table for the CRC-16. The poly is 0x8005 (x16 + x15 + x2 + 1) */ +__u16 const crc16_table[256] = { + 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, + 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, + 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, + 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, + 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, + 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, + 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, + 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, + 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, + 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, + 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, + 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, + 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, + 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, + 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, + 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, + 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, + 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, + 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, + 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, + 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, + 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, + 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, + 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, + 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, + 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, + 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, + 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, + 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, + 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, + 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, + 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 +}; + +static inline __u16 crc16_byte(__u16 crc, const __u8 data) +{ + return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff]; +} + +static __u16 crc16(__u16 crc, __u8 const *buffer, size_t len) +{ + while (len--) + crc = crc16_byte(crc, *buffer++); + return crc; +} + +/* + * This is the CRC-32C table + * Generated with: + * width = 32 bits + * poly = 0x1EDC6F41 + * reflect input bytes = true + * reflect output bytes = true + */ + +static const __u32 crc32c_table[256] = { + 0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L, + 0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL, + 0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL, + 0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L, + 0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL, + 0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L, + 0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L, + 0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL, + 0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL, + 0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L, + 0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L, + 0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL, + 0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L, + 0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL, + 0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL, + 0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L, + 0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L, + 0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L, + 0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L, + 0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L, + 0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L, + 0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L, + 0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L, + 0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L, + 0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L, + 0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L, + 0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L, + 0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L, + 0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L, + 0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L, + 0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L, + 0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L, + 0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL, + 0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L, + 0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L, + 0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL, + 0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L, + 0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL, + 0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL, + 0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L, + 0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L, + 0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL, + 0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL, + 0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L, + 0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL, + 0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L, + 0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L, + 0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL, + 0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L, + 0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL, + 0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL, + 0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L, + 0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL, + 0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L, + 0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L, + 0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL, + 0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL, + 0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L, + 0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L, + 0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL, + 0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L, + 0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL, + 0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL, + 0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L +}; + +/* + * Steps through buffer one byte at at time, calculates reflected + * crc using table. + */ + +static __u32 crc32c(__u32 crc, const __u8 *data, unsigned int length) +{ + while (length--) + crc = crc32c_table[(crc ^ *data++) & 0xFFL] ^ (crc >> 8); + + return crc; +} + +__u32 ext4_chksum(struct ext4_sb_info *sbi, __u32 crc, + const void *buffer, unsigned int length) +{ + return crc32c(crc, buffer, length); +} + +/* + * Metadata checksum functions for the superblock. + */ + +static __le32 ext4_superblock_csum(struct super_block *sb, + struct ext4_super_block *es) +{ + struct ext4_sb_info *sbi = EXT4_SB(sb); + int offset = offsetof(struct ext4_super_block, s_checksum); + __u32 csum; + + csum = ext4_chksum(sbi, ~0, (char *)es, offset); + + return cpu_to_le32(csum); +} + +int ext4_superblock_csum_verify(struct super_block *sb, + struct ext4_super_block *es) +{ + if (!ext4_has_feature_metadata_csum(sb)) + return 1; + + return es->s_checksum == ext4_superblock_csum(sb, es); +} + +void ext4_superblock_csum_set(struct super_block *sb) +{ + struct ext4_super_block *es = EXT4_SB(sb)->s_es; + + if (!ext4_has_feature_metadata_csum(sb)) + return; + + es->s_checksum = ext4_superblock_csum(sb, es); +} + +/* + * Metadata checksum functions for the group descriptors. + */ + +static __le16 ext4_group_desc_csum(struct super_block *sb, __u32 block_group, + struct ext4_group_desc *gdp) +{ + unsigned int offset = offsetof(struct ext4_group_desc, bg_checksum); + __u16 crc; + __le32 le_group = cpu_to_le32(block_group); + struct ext4_sb_info *sbi = EXT4_SB(sb); + + if (ext4_has_feature_metadata_csum(sb)) { + /* Use new metadata_csum algorithm */ + __u32 csum32; + __u16 dummy_csum = 0; + + csum32 = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&le_group, + sizeof(le_group)); + csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp, offset); + csum32 = ext4_chksum(sbi, csum32, (__u8 *)&dummy_csum, + sizeof(dummy_csum)); + offset += sizeof(dummy_csum); + if (offset < sbi->s_desc_size) + csum32 = ext4_chksum(sbi, csum32, (__u8 *)gdp + offset, + sbi->s_desc_size - offset); + + crc = csum32 & 0xFFFF; + } else if (ext4_has_feature_gdt_csum(sb)) { + /* old crc16 code */ + crc = crc16(~0, sbi->s_es->s_uuid, sizeof(sbi->s_es->s_uuid)); + crc = crc16(crc, (__u8 *)&le_group, sizeof(le_group)); + crc = crc16(crc, (__u8 *)gdp, offset); + offset += sizeof(gdp->bg_checksum); /* skip checksum */ + /* for checksum of struct ext4_group_desc do the rest...*/ + if (ext4_has_feature_64bit(sb) && + offset < le16_to_cpu(sbi->s_es->s_desc_size)) + crc = crc16(crc, (__u8 *)gdp + offset, + le16_to_cpu(sbi->s_es->s_desc_size) - + offset); + } else { + crc = 0; + } + + return cpu_to_le16(crc); +} + +int ext4_group_desc_csum_verify(struct super_block *sb, __u32 block_group, + struct ext4_group_desc *gdp) +{ + if (!ext4_has_feature_metadata_csum(sb)) + return 1; + + return gdp->bg_checksum == ext4_group_desc_csum(sb, block_group, gdp); +} + +void ext4_group_desc_csum_set(struct super_block *sb, __u32 block_group, + struct ext4_group_desc *gdp) +{ + if (!ext4_has_group_desc_csum(sb)) + return; + + gdp->bg_checksum = ext4_group_desc_csum(sb, block_group, gdp); +} + +/* + * Metadata checksum functions for the inode bitmap. + */ + +int ext4_inode_bitmap_csum_verify(struct super_block *sb, ext4_group_t group, + struct ext4_group_desc *gdp, + struct buffer_head *bh, int sz) +{ + __u32 hi; + __u32 provided, calculated; + struct ext4_sb_info *sbi = EXT4_SB(sb); + + if (!ext4_has_feature_metadata_csum(sb)) + return 1; + + provided = le16_to_cpu(gdp->bg_inode_bitmap_csum_lo); + calculated = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)bh->b_data, sz); + if (sbi->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END) { + hi = le16_to_cpu(gdp->bg_inode_bitmap_csum_hi); + provided |= (hi << 16); + } else + calculated &= 0xFFFF; + + return provided == calculated; +} + +void ext4_inode_bitmap_csum_set(struct super_block *sb, ext4_group_t group, + struct ext4_group_desc *gdp, + struct buffer_head *bh, int sz) +{ + __u32 csum; + struct ext4_sb_info *sbi = EXT4_SB(sb); + + if (!ext4_has_feature_metadata_csum(sb)) + return; + + csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)bh->b_data, sz); + gdp->bg_inode_bitmap_csum_lo = cpu_to_le16(csum & 0xFFFF); + if (sbi->s_desc_size >= EXT4_BG_INODE_BITMAP_CSUM_HI_END) + gdp->bg_inode_bitmap_csum_hi = cpu_to_le16(csum >> 16); +} + +/* + * Metadata checksum functions for the block bitmap. + */ + +int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group, + struct ext4_group_desc *gdp, + struct buffer_head *bh) +{ + __u32 hi; + __u32 provided, calculated; + struct ext4_sb_info *sbi = EXT4_SB(sb); + int sz = EXT4_CLUSTERS_PER_GROUP(sb) / 8; + + if (!ext4_has_feature_metadata_csum(sb)) + return 1; + + provided = le16_to_cpu(gdp->bg_block_bitmap_csum_lo); + calculated = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)bh->b_data, sz); + if (sbi->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_END) { + hi = le16_to_cpu(gdp->bg_block_bitmap_csum_hi); + provided |= (hi << 16); + } else + calculated &= 0xFFFF; + + return provided == calculated; +} + +void ext4_block_bitmap_csum_set(struct super_block *sb, ext4_group_t group, + struct ext4_group_desc *gdp, + struct buffer_head *bh) +{ + int sz = EXT4_CLUSTERS_PER_GROUP(sb) / 8; + __u32 csum; + struct ext4_sb_info *sbi = EXT4_SB(sb); + + if (!ext4_has_feature_metadata_csum(sb)) + return; + + csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)bh->b_data, sz); + gdp->bg_block_bitmap_csum_lo = cpu_to_le16(csum & 0xFFFF); + if (sbi->s_desc_size >= EXT4_BG_BLOCK_BITMAP_CSUM_HI_END) + gdp->bg_block_bitmap_csum_hi = cpu_to_le16(csum >> 16); +} + +/* + * Metadata checksum functions for the inodes. + */ + +static __le32 ext4_inode_csum_seed(struct inode *inode) +{ + /* Precompute checksum seed for inode metadata */ + if (ext4_has_feature_metadata_csum(inode->i_sb)) { + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + __u32 csum; + __le32 inum = cpu_to_le32(inode->i_ino); + __le32 gen = inode->i_generation; + csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&inum, sizeof(inum)); + return ext4_chksum(sbi, csum, (__u8 *)&gen, sizeof(gen)); + } + + return 0; +} + +static __u32 ext4_inode_csum(struct inode *inode, struct ext4_inode *raw, + struct ext4_inode_info *ei) +{ + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + __u32 csum; + __u16 dummy_csum = 0; + int offset = offsetof(struct ext4_inode, i_checksum_lo); + unsigned int csum_size = sizeof(dummy_csum); + + csum = ext4_chksum(sbi, ext4_inode_csum_seed(inode), (__u8 *)raw, offset); + csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, csum_size); + offset += csum_size; + csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset, + EXT4_GOOD_OLD_INODE_SIZE - offset); + + if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE) { + offset = offsetof(struct ext4_inode, i_checksum_hi); + csum = ext4_chksum(sbi, csum, (__u8 *)raw + + EXT4_GOOD_OLD_INODE_SIZE, + offset - EXT4_GOOD_OLD_INODE_SIZE); + if (EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) { + csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, + csum_size); + offset += csum_size; + } + csum = ext4_chksum(sbi, csum, (__u8 *)raw + offset, + EXT4_INODE_SIZE(inode->i_sb) - offset); + } + + return csum; +} + +int ext4_inode_csum_verify(struct inode *inode, struct ext4_inode *raw, + struct ext4_inode_info *ei) +{ + __u32 provided, calculated, isz; + + if (EXT4_SB(inode->i_sb)->s_es->s_creator_os != + cpu_to_le32(EXT4_OS_LINUX) || + !ext4_has_feature_metadata_csum(inode->i_sb)) + return 1; + + provided = le16_to_cpu(raw->i_checksum_lo); + calculated = ext4_inode_csum(inode, raw, ei); + if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE && + EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) + {provided |= ((__u32)le16_to_cpu(raw->i_checksum_hi)) << 16;isz=EXT4_INODE_SIZE(inode->i_sb);} + else + {calculated &= 0xFFFF;isz=EXT4_GOOD_OLD_INODE_SIZE;} + + if (provided != calculated) { + DbgPrint("inod %d checksum invalid: %lx!=%lx, isz=%u\n", inode->i_ino, provided, calculated, isz); + } + + return provided == calculated; +} + +void ext4_inode_csum_set(struct inode *inode, struct ext4_inode *raw, + struct ext4_inode_info *ei) +{ + __u32 csum; + + if (EXT4_SB(inode->i_sb)->s_es->s_creator_os != + cpu_to_le32(EXT4_OS_LINUX) || + !ext4_has_feature_metadata_csum(inode->i_sb)) + return; + + csum = ext4_inode_csum(inode, raw, ei); + raw->i_checksum_lo = cpu_to_le16(csum & 0xFFFF); + if (EXT4_INODE_SIZE(inode->i_sb) > EXT4_GOOD_OLD_INODE_SIZE && + EXT4_FITS_IN_INODE(raw, ei, i_checksum_hi)) + raw->i_checksum_hi = cpu_to_le16(csum >> 16); +} + +/* + * Metadata checksum functions for the extent blocks. + */ + +static __le32 ext4_extent_block_csum(struct inode *inode, + struct ext4_extent_header *eh) +{ + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + __u32 csum; + + csum = ext4_chksum(sbi, ext4_inode_csum_seed(inode), (__u8 *)eh, EXT4_EXTENT_TAIL_OFFSET(eh)); + + return cpu_to_le32(csum); +} + +int ext4_extent_block_csum_verify(struct inode *inode, + struct ext4_extent_header *eh) +{ + struct ext4_extent_tail *et; + + if (!ext4_has_feature_metadata_csum(inode->i_sb)) + return 1; + + et = find_ext4_extent_tail(eh); + return et->et_checksum == ext4_extent_block_csum(inode, eh); +} + +void ext4_extent_block_csum_set(struct inode *inode, + struct ext4_extent_header *eh) +{ + struct ext4_extent_tail *et; + + if (!ext4_has_feature_metadata_csum(inode->i_sb)) + return; + + et = find_ext4_extent_tail(eh); + et->et_checksum = ext4_extent_block_csum(inode, eh); +} + +/* + * Metadata checksum functions for the directory entrys. + */ + +static void initialize_dirent_tail(struct ext4_dir_entry_tail *t, + unsigned int blocksize) +{ + memset(t, 0, sizeof(struct ext4_dir_entry_tail)); + t->det_rec_len = ext4_rec_len_to_disk( + sizeof(struct ext4_dir_entry_tail), blocksize); + t->det_reserved_ft = EXT4_FT_DIR_CSUM; +} + +/* Walk through a dirent block to find a checksum "dirent" at the tail */ +static struct ext4_dir_entry_tail *get_dirent_tail(struct inode *inode, + struct ext4_dir_entry *de) +{ + struct ext4_dir_entry_tail *t; + +#ifdef PARANOID + struct ext4_dir_entry *d, *top; + + d = de; + top = (struct ext4_dir_entry *)(((void *)de) + + (EXT4_BLOCK_SIZE(inode->i_sb) - + sizeof(struct ext4_dir_entry_tail))); + while (d < top && d->rec_len) + d = (struct ext4_dir_entry *)(((void *)d) + + le16_to_cpu(d->rec_len)); + + if (d != top) + return NULL; + + t = (struct ext4_dir_entry_tail *)d; +#else + t = EXT4_DIRENT_TAIL(de, EXT4_BLOCK_SIZE(inode->i_sb)); +#endif + + if (t->det_reserved_zero1 || + le16_to_cpu(t->det_rec_len) != sizeof(struct ext4_dir_entry_tail) || + t->det_reserved_zero2 || + t->det_reserved_ft != EXT4_FT_DIR_CSUM) + return NULL; + + return t; +} + +static __le32 ext4_dirent_csum(struct inode *inode, + struct ext4_dir_entry *dirent, int size) +{ + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + __u32 csum; + + csum = ext4_chksum(sbi, ext4_inode_csum_seed(inode), (__u8 *)dirent, size); + + return cpu_to_le32(csum); +} + +int ext4_dirent_csum_verify(struct inode *inode, struct ext4_dir_entry *dirent) +{ + struct ext4_dir_entry_tail *t; + + if (!ext4_has_feature_metadata_csum(inode->i_sb)) + return 1; + + t = get_dirent_tail(inode, dirent); + if (!t) { + DbgPrint("No space for directory leaf checksum. Please run e2fsck -D.\n"); + return 0; + } + + if (t->det_checksum != ext4_dirent_csum(inode, dirent, + (unsigned int)((unsigned char *)t - (unsigned char *)dirent))) + return 0; + + return 1; +} + +void ext4_dirent_csum_set(struct inode *inode, + struct ext4_dir_entry *dirent) +{ + struct ext4_dir_entry_tail *t; + + if (!ext4_has_feature_metadata_csum(inode->i_sb)) + return; + + t = get_dirent_tail(inode, dirent); + if (!t) { + DbgPrint("No space for directory leaf checksum. Please run e2fsck -D.\n"); + return; + } + + t->det_checksum = ext4_dirent_csum(inode, dirent, + (unsigned int)((unsigned char *)t - (unsigned char *)dirent)); +} + +static struct dx_countlimit *get_dx_countlimit(struct inode *inode, + struct ext4_dir_entry *dirent, + int *offset) +{ + struct ext4_dir_entry *dp; + struct dx_root_info *root; + int count_offset; + + if (le16_to_cpu(dirent->rec_len) == EXT4_BLOCK_SIZE(inode->i_sb)) + count_offset = 8; + else if (le16_to_cpu(dirent->rec_len) == 12) { + dp = (struct ext4_dir_entry *)(((unsigned char *)dirent) + 12); + if (le16_to_cpu(dp->rec_len) != + EXT4_BLOCK_SIZE(inode->i_sb) - 12) + return NULL; + root = (struct dx_root_info *)(((unsigned char *)dp + 12)); + if (root->reserved_zero || + root->info_length != sizeof(struct dx_root_info)) + return NULL; + count_offset = 32; + } else + return NULL; + + if (offset) + *offset = count_offset; + return (struct dx_countlimit *)(((unsigned char *)dirent) + count_offset); +} + +static __le32 ext4_dx_csum(struct inode *inode, struct ext4_dir_entry *dirent, + int count_offset, int count, struct dx_tail *t) +{ + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + __u32 csum; + int size; + __u32 dummy_csum = 0; + int offset = offsetof(struct dx_tail, dt_checksum); + + size = count_offset + (count * sizeof(struct dx_entry)); + csum = ext4_chksum(sbi, ext4_inode_csum_seed(inode), (__u8 *)dirent, size); + csum = ext4_chksum(sbi, csum, (__u8 *)t, offset); + csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, sizeof(dummy_csum)); + + return cpu_to_le32(csum); +} + +int ext4_dx_csum_verify(struct inode *inode, + struct ext4_dir_entry *dirent) +{ + struct dx_countlimit *c; + struct dx_tail *t; + int count_offset, limit, count; + + if (!ext4_has_feature_metadata_csum(inode->i_sb)) + return 1; + + c = get_dx_countlimit(inode, dirent, &count_offset); + if (!c) { + DbgPrint("dir seems corrupt? Run e2fsck -D."); + return 0; + } + limit = le16_to_cpu(c->limit); + count = le16_to_cpu(c->count); + if (count_offset + (limit * sizeof(struct dx_entry)) > + EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) { + DbgPrint("warn_no_space_for_csum in inode\n"); + return 0; + } + t = (struct dx_tail *)(((struct dx_entry *)c) + limit); + + return t->dt_checksum == ext4_dx_csum(inode, dirent, count_offset, count, t); +} + +void ext4_dx_csum_set(struct inode *inode, struct ext4_dir_entry *dirent) +{ + struct dx_countlimit *c; + struct dx_tail *t; + int count_offset, limit, count; + + if (!ext4_has_feature_metadata_csum(inode->i_sb)) + return; + + c = get_dx_countlimit(inode, dirent, &count_offset); + if (!c) { + DbgPrint("dir seems corrupt? Run e2fsck -D."); + return; + } + limit = le16_to_cpu(c->limit); + count = le16_to_cpu(c->count); + if (count_offset + (limit * sizeof(struct dx_entry)) > + EXT4_BLOCK_SIZE(inode->i_sb) - sizeof(struct dx_tail)) { + DbgPrint("warn_no_space_for_csum in inode\n"); + return; + } + t = (struct dx_tail *)(((struct dx_entry *)c) + limit); + + t->dt_checksum = ext4_dx_csum(inode, dirent, count_offset, count, t); +} + +/* + * Metadata checksum functions for the multiple mount point structure. + */ + +static __le32 ext4_mmp_csum(struct super_block *sb, struct mmp_struct *mmp) +{ + struct ext4_sb_info *sbi = EXT4_SB(sb); + int offset = offsetof(struct mmp_struct, mmp_checksum); + __u32 csum; + + csum = ext4_chksum(sbi, sbi->s_csum_seed, (char *)mmp, offset); + + return cpu_to_le32(csum); +} + +int ext4_mmp_csum_verify(struct super_block *sb, struct mmp_struct *mmp) +{ + if (!ext4_has_feature_metadata_csum(sb)) + return 1; + + return mmp->mmp_checksum == ext4_mmp_csum(sb, mmp); +} + +void ext4_mmp_csum_set(struct super_block *sb, struct mmp_struct *mmp) +{ + if (!ext4_has_feature_metadata_csum(sb)) + return; + + mmp->mmp_checksum = ext4_mmp_csum(sb, mmp); +} + +/* + * Metadata checksum functions for the extended attributes. + */ + +static __le32 ext4_xattr_block_csum(struct inode *inode, + sector_t block_nr, + struct ext4_xattr_header *hdr) +{ + struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); + __u32 csum; + __le64 dsk_block_nr = cpu_to_le64(block_nr); + __u32 dummy_csum = 0; + int offset = offsetof(struct ext4_xattr_header, h_checksum); + + csum = ext4_chksum(sbi, sbi->s_csum_seed, (__u8 *)&dsk_block_nr, + sizeof(dsk_block_nr)); + csum = ext4_chksum(sbi, csum, (__u8 *)hdr, offset); + csum = ext4_chksum(sbi, csum, (__u8 *)&dummy_csum, sizeof(dummy_csum)); + offset += sizeof(dummy_csum); + csum = ext4_chksum(sbi, csum, (__u8 *)hdr + offset, + EXT4_BLOCK_SIZE(inode->i_sb) - offset); + + return cpu_to_le32(csum); +} + +int ext4_xattr_block_csum_verify(struct inode *inode, + struct buffer_head *bh) +{ + struct ext4_xattr_header *hdr = EXT4_XATTR_BHDR(bh); + int ret = 1; + + if (ext4_has_feature_metadata_csum(inode->i_sb)) { + lock_buffer(bh); + ret = (hdr->h_checksum == ext4_xattr_block_csum(inode, + bh->b_blocknr, hdr)); + unlock_buffer(bh); + } + return ret; +} + +void ext4_xattr_block_csum_set(struct inode *inode, + struct buffer_head *bh) +{ + if (ext4_has_feature_metadata_csum(inode->i_sb)) + EXT4_XATTR_BHDR(bh)->h_checksum = ext4_xattr_block_csum(inode, + bh->b_blocknr, EXT4_XATTR_BHDR(bh)); +} diff --git a/Ext4Fsd/ext4/ext4_jbd2.c b/Ext4Fsd/ext4/ext4_jbd2.c index 9f1d2a4..5421355 100644 --- a/Ext4Fsd/ext4/ext4_jbd2.c +++ b/Ext4Fsd/ext4/ext4_jbd2.c @@ -1,71 +1,72 @@ -#include "ext2fs.h" -#include "linux\ext4.h" - -static handle_t no_journal; - -handle_t *__ext4_journal_start_sb(void *icb, struct super_block *sb, unsigned int line, - int type, int blocks, int rsv_blocks) -{ - return &no_journal; -} - -int __ext4_journal_stop(const char *where, unsigned int line, void *icb, handle_t *handle) -{ - return 0; -} - -void ext4_journal_abort_handle(const char *caller, unsigned int line, - const char *err_fn, struct buffer_head *bh, - handle_t *handle, int err) -{ -} - -int __ext4_journal_get_write_access(const char *where, unsigned int line, - void *icb, handle_t *handle, struct buffer_head *bh) -{ - int err = 0; - return err; -} - -/* - * The ext4 forget function must perform a revoke if we are freeing data - * which has been journaled. Metadata (eg. indirect blocks) must be - * revoked in all cases. - * - * "bh" may be NULL: a metadata block may have been freed from memory - * but there may still be a record of it in the journal, and that record - * still needs to be revoked. - * - * If the handle isn't valid we're not journaling, but we still need to - * call into ext4_journal_revoke() to put the buffer head. - */ -int __ext4_forget(const char *where, unsigned int line, void *icb, handle_t *handle, - int is_metadata, struct inode *inode, - struct buffer_head *bh, ext4_fsblk_t blocknr) -{ - int err = 0; - return err; -} - -int __ext4_journal_get_create_access(const char *where, unsigned int line, - void *icb, handle_t *handle, struct buffer_head *bh) -{ - int err = 0; - return err; -} - -int __ext4_handle_dirty_metadata(const char *where, unsigned int line, - void *icb, handle_t *handle, struct inode *inode, - struct buffer_head *bh) -{ - int err = 0; - - extents_mark_buffer_dirty(bh); - return err; -} - -int __ext4_handle_dirty_super(const char *where, unsigned int line, - handle_t *handle, struct super_block *sb) -{ - return 0; -} +#include "ext2fs.h" +#include "linux\ext4.h" +#include "linux\jbd.h" + +static handle_t no_journal; + +handle_t *__ext4_journal_start_sb(void *icb, struct super_block *sb, unsigned int line, + int type, int blocks, int rsv_blocks) +{ + return &no_journal; +} + +int __ext4_journal_stop(const char *where, unsigned int line, void *icb, handle_t *handle) +{ + return 0; +} + +void ext4_journal_abort_handle(const char *caller, unsigned int line, + const char *err_fn, struct buffer_head *bh, + handle_t *handle, int err) +{ +} + +int __ext4_journal_get_write_access(const char *where, unsigned int line, + void *icb, handle_t *handle, struct buffer_head *bh) +{ + int err = 0; + return err; +} + +/* + * The ext4 forget function must perform a revoke if we are freeing data + * which has been journaled. Metadata (eg. indirect blocks) must be + * revoked in all cases. + * + * "bh" may be NULL: a metadata block may have been freed from memory + * but there may still be a record of it in the journal, and that record + * still needs to be revoked. + * + * If the handle isn't valid we're not journaling, but we still need to + * call into ext4_journal_revoke() to put the buffer head. + */ +int __ext4_forget(const char *where, unsigned int line, void *icb, handle_t *handle, + int is_metadata, struct inode *inode, + struct buffer_head *bh, ext4_fsblk_t blocknr) +{ + int err = 0; + return err; +} + +int __ext4_journal_get_create_access(const char *where, unsigned int line, + void *icb, handle_t *handle, struct buffer_head *bh) +{ + int err = 0; + return err; +} + +int __ext4_handle_dirty_metadata(const char *where, unsigned int line, + void *icb, handle_t *handle, struct inode *inode, + struct buffer_head *bh) +{ + int err = 0; + + extents_mark_buffer_dirty(bh); + return err; +} + +int __ext4_handle_dirty_super(const char *where, unsigned int line, + handle_t *handle, struct super_block *sb) +{ + return 0; +} diff --git a/Ext4Fsd/fsctl.c b/Ext4Fsd/fsctl.c index 9007eed..5450766 100644 --- a/Ext4Fsd/fsctl.c +++ b/Ext4Fsd/fsctl.c @@ -1,2877 +1,2881 @@ -/* - * COPYRIGHT: See COPYRIGHT.TXT - * PROJECT: Ext2 File System Driver for WinNT/2K/XP - * FILE: fsctl.c - * PROGRAMMER: Matt Wu - * HOMEPAGE: http://www.ext2fsd.com - * UPDATE HISTORY: - */ - -/* INCLUDES *****************************************************************/ - -#include "ext2fs.h" - -/* GLOBALS ***************************************************************/ - -extern PEXT2_GLOBAL Ext2Global; - -/* DEFINITIONS *************************************************************/ - -#ifdef ALLOC_PRAGMA -#pragma alloc_text(PAGE, Ext2IsHandleCountZero) -#pragma alloc_text(PAGE, Ext2LockVcb) -#pragma alloc_text(PAGE, Ext2LockVolume) -#pragma alloc_text(PAGE, Ext2UnlockVcb) -#pragma alloc_text(PAGE, Ext2UnlockVolume) -#pragma alloc_text(PAGE, Ext2AllowExtendedDasdIo) -#pragma alloc_text(PAGE, Ext2GetRetrievalPointerBase) -#pragma alloc_text(PAGE, Ext2QueryExtentMappings) -#pragma alloc_text(PAGE, Ext2QueryRetrievalPointers) -#pragma alloc_text(PAGE, Ext2GetRetrievalPointers) -#pragma alloc_text(PAGE, Ext2UserFsRequest) -#pragma alloc_text(PAGE, Ext2IsMediaWriteProtected) -#pragma alloc_text(PAGE, Ext2MountVolume) -#pragma alloc_text(PAGE, Ext2PurgeVolume) -#pragma alloc_text(PAGE, Ext2PurgeFile) -#pragma alloc_text(PAGE, Ext2DismountVolume) -#pragma alloc_text(PAGE, Ext2IsVolumeMounted) -#pragma alloc_text(PAGE, Ext2VerifyVolume) -#pragma alloc_text(PAGE, Ext2FileSystemControl) -#endif - - -VOID -Ext2SetVpbFlag ( - IN PVPB Vpb, - IN USHORT Flag ) -{ - KIRQL OldIrql; - - IoAcquireVpbSpinLock(&OldIrql); - Vpb->Flags |= Flag; - IoReleaseVpbSpinLock(OldIrql); -} - -VOID -Ext2ClearVpbFlag ( - IN PVPB Vpb, - IN USHORT Flag ) -{ - KIRQL OldIrql; - - IoAcquireVpbSpinLock(&OldIrql); - Vpb->Flags &= ~Flag; - IoReleaseVpbSpinLock(OldIrql); -} - -BOOLEAN -Ext2IsHandleCountZero(IN PEXT2_VCB Vcb) -{ - PEXT2_FCB Fcb; - PLIST_ENTRY List; - - for ( List = Vcb->FcbList.Flink; - List != &Vcb->FcbList; - List = List->Flink ) { - - Fcb = CONTAINING_RECORD(List, EXT2_FCB, Next); - - ASSERT((Fcb->Identifier.Type == EXT2FCB) && - (Fcb->Identifier.Size == sizeof(EXT2_FCB))); - - DEBUG(DL_INF, ( "Ext2IsHandleCountZero: Inode:%xh File:%S OpenHandleCount=%xh\n", - Fcb->Inode->i_ino, Fcb->Mcb->ShortName.Buffer, Fcb->OpenHandleCount)); - - if (Fcb->OpenHandleCount) { - return FALSE; - } - } - - return TRUE; -} - -NTSTATUS -Ext2LockVcb (IN PEXT2_VCB Vcb, - IN PFILE_OBJECT FileObject) -{ - NTSTATUS Status = STATUS_SUCCESS; - - __try { - - if (FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED)) { - DEBUG(DL_INF, ( "Ext2LockVolume: Volume is already locked.\n")); - Status = STATUS_ACCESS_DENIED; - __leave; - } - - if (Vcb->OpenHandleCount > (ULONG)(FileObject ? 1 : 0)) { - DEBUG(DL_INF, ( "Ext2LockVcb: There are still opened files.\n")); - - Status = STATUS_ACCESS_DENIED; - __leave; - } - - if (!Ext2IsHandleCountZero(Vcb)) { - DEBUG(DL_INF, ( "Ext2LockVcb: Thare are still opened files.\n")); - - Status = STATUS_ACCESS_DENIED; - __leave; - } - - SetLongFlag(Vcb->Flags, VCB_VOLUME_LOCKED); - Ext2SetVpbFlag(Vcb->Vpb, VPB_LOCKED); - Vcb->LockFile = FileObject; - - DEBUG(DL_INF, ( "Ext2LockVcb: Volume locked.\n")); - - } __finally { - // Nothing - } - - return Status; -} - - -NTSTATUS -Ext2LockVolume (IN PEXT2_IRP_CONTEXT IrpContext) -{ - PIO_STACK_LOCATION IrpSp; - PDEVICE_OBJECT DeviceObject; - PEXT2_VCB Vcb = NULL; - NTSTATUS Status; - BOOLEAN VcbResourceAcquired = FALSE; - - __try { - - ASSERT(IrpContext != NULL); - - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - DeviceObject = IrpContext->DeviceObject; - - Status = STATUS_UNSUCCESSFUL; - - // - // This request is not allowed on the main device object - // - if (IsExt2FsDevice(DeviceObject)) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - - Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; - - ASSERT(Vcb != NULL); - - ASSERT((Vcb->Identifier.Type == EXT2VCB) && - (Vcb->Identifier.Size == sizeof(EXT2_VCB))); - - ASSERT(IsMounted(Vcb)); - - IrpSp = IoGetCurrentIrpStackLocation(IrpContext->Irp); - -#if (_WIN32_WINNT >= 0x0500) - CcWaitForCurrentLazyWriterActivity(); -#endif - ExAcquireResourceExclusiveLite( - &Vcb->MainResource, - TRUE ); - - VcbResourceAcquired = TRUE; - - /* flush dirty data before locking the volume */ - if (!IsVcbReadOnly(Vcb)) { - Ext2FlushFiles(IrpContext, Vcb, FALSE); - Ext2FlushVolume(IrpContext, Vcb, FALSE); - } - - Status = Ext2LockVcb(Vcb, IrpSp->FileObject); - - } __finally { - - if (VcbResourceAcquired) { - ExReleaseResourceLite(&Vcb->MainResource); - } - - if (!IrpContext->ExceptionInProgress) { - Ext2CompleteIrpContext(IrpContext, Status); - } - } - - return Status; -} - -NTSTATUS -Ext2UnlockVcb ( IN PEXT2_VCB Vcb, - IN PFILE_OBJECT FileObject ) -{ - NTSTATUS Status; - - __try { - - if (FileObject && FileObject->FsContext != Vcb) { - Status = STATUS_NOT_LOCKED; - __leave; - } - - if (!FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED)) { - DEBUG(DL_ERR, ( ": Ext2UnlockVcb: Volume is not locked.\n")); - Status = STATUS_NOT_LOCKED; - __leave; - } - - if (Vcb->LockFile == FileObject) { - ClearFlag(Vcb->Flags, VCB_VOLUME_LOCKED); - Ext2ClearVpbFlag(Vcb->Vpb, VPB_LOCKED); - DEBUG(DL_INF, ( "Ext2UnlockVcb: Volume unlocked.\n")); - Status = STATUS_SUCCESS; - } else { - Status = STATUS_NOT_LOCKED; - } - - } __finally { - // Nothing - } - - return Status; -} - -NTSTATUS -Ext2UnlockVolume ( - IN PEXT2_IRP_CONTEXT IrpContext -) -{ - PIO_STACK_LOCATION IrpSp = NULL; - PDEVICE_OBJECT DeviceObject = NULL; - PEXT2_VCB Vcb = NULL; - NTSTATUS Status; - BOOLEAN VcbResourceAcquired = FALSE; - - __try { - - ASSERT(IrpContext != NULL); - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - DeviceObject = IrpContext->DeviceObject; - IrpSp = IoGetCurrentIrpStackLocation(IrpContext->Irp); - - // - // This request is not allowed on the main device object - // - if (IsExt2FsDevice(DeviceObject)) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - - Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; - ASSERT(Vcb != NULL); - ASSERT((Vcb->Identifier.Type == EXT2VCB) && - (Vcb->Identifier.Size == sizeof(EXT2_VCB))); - - ExAcquireResourceExclusiveLite( - &Vcb->MainResource, - TRUE ); - VcbResourceAcquired = TRUE; - - Status = Ext2UnlockVcb(Vcb, IrpSp->FileObject); - - } __finally { - - if (VcbResourceAcquired) { - ExReleaseResourceLite(&Vcb->MainResource); - } - - if (!IrpContext->ExceptionInProgress) { - Ext2CompleteIrpContext(IrpContext, Status); - } - } - - return Status; -} - - -NTSTATUS -Ext2InvalidateVolumes ( IN PEXT2_IRP_CONTEXT IrpContext ) -{ - NTSTATUS Status; - PIRP Irp; - PIO_STACK_LOCATION IrpSp; - - PVPB NewVpb = NULL; - HANDLE Handle; - PLIST_ENTRY ListEntry; - - ULONG InputLength = 0; - PFILE_OBJECT FileObject; - PDEVICE_OBJECT DeviceObject; - BOOLEAN GlobalResourceAcquired = FALSE; - - LUID Privilege = {SE_TCB_PRIVILEGE, 0}; - - __try { - - Irp = IrpContext->Irp; - IrpSp = IoGetCurrentIrpStackLocation(Irp); - - if (!IsExt2FsDevice(IrpSp->DeviceObject)) { - Status = STATUS_INVALID_DEVICE_REQUEST; - __leave; - } - - if (!SeSinglePrivilegeCheck(Privilege, Irp->RequestorMode)) { - Status = STATUS_PRIVILEGE_NOT_HELD; - __leave; - } - - -#ifndef _GNU_NTIFS_ - InputLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; -#else - InputLength = ((PEXTENDED_IO_STACK_LOCATION)(IrpSp))-> - Parameters.FileSystemControl.InputBufferLength; -#endif - -#if defined(_WIN64) - if (IoIs32bitProcess(Irp)) { - if (InputLength != sizeof(UINT32)) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - Handle = (HANDLE) LongToHandle( (*(PUINT32)Irp->AssociatedIrp.SystemBuffer) ); - } else -#endif - { - if (InputLength != sizeof(HANDLE)) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - Handle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer; - } - - Status = ObReferenceObjectByHandle( Handle, - 0, - *IoFileObjectType, - KernelMode, - &FileObject, - NULL ); - - if (!NT_SUCCESS(Status)) { - __leave; - } else { - DeviceObject = FileObject->DeviceObject; - ObDereferenceObject(FileObject); - } - - ExAcquireResourceExclusiveLite(&Ext2Global->Resource, TRUE); - GlobalResourceAcquired = TRUE; - - ListEntry = Ext2Global->VcbList.Flink; - while (ListEntry != &Ext2Global->VcbList) { - - PEXT2_VCB Vcb = CONTAINING_RECORD(ListEntry, EXT2_VCB, Next); - ListEntry = ListEntry->Flink; - - DEBUG(DL_DBG, ( "Ext2InvalidateVolumes: Vcb=%xh Vcb->Vpb=%xh " - "Blink = %p &Vcb->Next = %p\n", - Vcb, Vcb->Vpb, ListEntry->Blink, &Vcb->Next)); - - if (Vcb->Vpb && (Vcb->Vpb->RealDevice == DeviceObject)) { - - DEBUG(DL_DBG, ( "Ext2InvalidateVolumes: Got Vcb=%xh Vcb->Vpb=%xh " - "Blink = %p &Vcb->Next = %p\n", - Vcb, Vcb->Vpb, ListEntry->Blink, &Vcb->Next)); - /* dismount the volume */ - Ext2CheckDismount(IrpContext, Vcb, FALSE); - } - } - - } __finally { - - if (GlobalResourceAcquired) { - ExReleaseResourceLite(&Ext2Global->Resource); - } - - if (!IrpContext->ExceptionInProgress) { - Ext2CompleteIrpContext(IrpContext, Status); - } - } - - return Status; -} - -NTSTATUS -Ext2AllowExtendedDasdIo(IN PEXT2_IRP_CONTEXT IrpContext) -{ - PIO_STACK_LOCATION IrpSp; - PEXT2_VCB Vcb; - PEXT2_CCB Ccb; - NTSTATUS status; - - IrpSp = IoGetCurrentIrpStackLocation(IrpContext->Irp); - - Vcb = (PEXT2_VCB) IrpSp->FileObject->FsContext; - Ccb = (PEXT2_CCB) IrpSp->FileObject->FsContext2; - - ASSERT(Vcb != NULL); - - ASSERT((Vcb->Identifier.Type == EXT2VCB) && - (Vcb->Identifier.Size == sizeof(EXT2_VCB))); - - ASSERT(IsMounted(Vcb)); - - if (Ccb) { - SetLongFlag(Ccb->Flags, CCB_ALLOW_EXTENDED_DASD_IO); - status = STATUS_SUCCESS; - } else { - status = STATUS_INVALID_PARAMETER; - } - - Ext2CompleteIrpContext(IrpContext, status); - return status; -} - -/* - * Ext2OplockRequest - * - * oplock requests handler routine - * - * Arguments: - * IrpContext: the ext2 irp context - * - * Return Value: - * NTSTATUS: The return status for the operation - * - */ - -NTSTATUS -Ext2OplockRequest ( - IN PEXT2_IRP_CONTEXT IrpContext -) -{ - NTSTATUS Status; - - ULONG FsCtrlCode; - PDEVICE_OBJECT DeviceObject; - PFILE_OBJECT FileObject; - - PIRP Irp = NULL; - PIO_STACK_LOCATION IrpSp; - PEXTENDED_IO_STACK_LOCATION EIrpSp; - - PEXT2_VCB Vcb = NULL; - PEXT2_FCB Fcb = NULL; - PEXT2_CCB Ccb = NULL; - - ULONG OplockCount = 0; - - BOOLEAN VcbResourceAcquired = FALSE; - BOOLEAN FcbResourceAcquired = FALSE; - - ASSERT(IrpContext); - - __try { - - Irp = IrpContext->Irp; - ASSERT(Irp); - - IrpSp = IoGetCurrentIrpStackLocation(Irp); - ASSERT(IrpSp); - EIrpSp = (PEXTENDED_IO_STACK_LOCATION)IrpSp; - - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - DeviceObject = IrpContext->DeviceObject; - - // - // This request is not allowed on the main device object - // - if (IsExt2FsDevice(DeviceObject)) { - Status = STATUS_INVALID_DEVICE_REQUEST; - __leave; - } - - Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; - - ASSERT(Vcb != NULL); - - ASSERT((Vcb->Identifier.Type == EXT2VCB) && - (Vcb->Identifier.Size == sizeof(EXT2_VCB))); - - ASSERT(IsMounted(Vcb)); - - FileObject = IrpContext->FileObject; - - Fcb = (PEXT2_FCB) FileObject->FsContext; - - // - // This request is not allowed on volumes - // - - if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - - ASSERT((Fcb->Identifier.Type == EXT2FCB) && - (Fcb->Identifier.Size == sizeof(EXT2_FCB))); - - if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { - Status = STATUS_FILE_DELETED; - __leave; - } - - Ccb = (PEXT2_CCB) FileObject->FsContext2; - if (Ccb == NULL) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - - - ASSERT((Ccb->Identifier.Type == EXT2CCB) && - (Ccb->Identifier.Size == sizeof(EXT2_CCB))); - - FsCtrlCode = EIrpSp->Parameters.FileSystemControl.FsControlCode; - - switch (FsCtrlCode) { - - case FSCTL_REQUEST_OPLOCK_LEVEL_1: - case FSCTL_REQUEST_OPLOCK_LEVEL_2: - case FSCTL_REQUEST_BATCH_OPLOCK: - - VcbResourceAcquired = - ExAcquireResourceSharedLite( - &Vcb->MainResource, - IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); - - ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); - - FcbResourceAcquired = - ExAcquireResourceExclusiveLite ( - &Fcb->MainResource, - IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); - - if (FsCtrlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) { - OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks(&Fcb->FileLockAnchor); - } else { - OplockCount = Fcb->OpenHandleCount; - } - - break; - - case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: - case FSCTL_OPBATCH_ACK_CLOSE_PENDING : - case FSCTL_OPLOCK_BREAK_NOTIFY: - case FSCTL_OPLOCK_BREAK_ACK_NO_2: - - FcbResourceAcquired = - ExAcquireResourceSharedLite ( - &Fcb->MainResource, - IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); - - break; - - default: - - Ext2BugCheck(EXT2_BUGCHK_FSCTL, FsCtrlCode, 0, 0); - } - - - // - // Call the FsRtl routine to grant/acknowledge oplock. - // - - Status = FsRtlOplockFsctrl( &Fcb->Oplock, - Irp, - OplockCount ); - - // - // Set the flag indicating if Fast I/O is possible - // - - Fcb->Header.IsFastIoPossible = Ext2IsFastIoPossible(Fcb); - IrpContext->Irp = NULL; - - } __finally { - - if (FcbResourceAcquired) { - ExReleaseResourceLite(&Fcb->MainResource); - } - - if (VcbResourceAcquired) { - ExReleaseResourceLite(&Vcb->MainResource); - } - - if (!AbnormalTermination()) { - Ext2CompleteIrpContext(IrpContext, Status); - } - } - - return Status; -} - -NTSTATUS -Ext2IsVolumeDirty ( - IN PEXT2_IRP_CONTEXT IrpContext -) -{ - NTSTATUS status = STATUS_SUCCESS; - PIRP Irp; - PEXTENDED_IO_STACK_LOCATION IrpSp; - PULONG VolumeState; - - __try { - - Irp = IrpContext->Irp; - IrpSp = (PEXTENDED_IO_STACK_LOCATION)IoGetCurrentIrpStackLocation(Irp); - - // - // Get a pointer to the output buffer. Look at the system buffer field in th - // irp first. Then the Irp Mdl. - // - - if (Irp->AssociatedIrp.SystemBuffer != NULL) { - - VolumeState = Irp->AssociatedIrp.SystemBuffer; - - } else if (Irp->MdlAddress != NULL) { - - VolumeState = MmGetSystemAddressForMdl( Irp->MdlAddress ); - - } else { - - status = STATUS_INVALID_USER_BUFFER; - __leave; - } - - if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) { - status = STATUS_INVALID_PARAMETER; - __leave; - } - - *VolumeState = 0; - - } __finally { - - if (!IrpContext->ExceptionInProgress) { - Ext2CompleteIrpContext(IrpContext, status); - } - } - - return status; -} - - -NTSTATUS -Ext2QueryExtentMappings( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN PEXT2_FCB Fcb, - IN PLARGE_INTEGER RequestVbn, - OUT PLARGE_INTEGER * pMappedRuns -) -{ - PLARGE_INTEGER MappedRuns = NULL; - PLARGE_INTEGER PartialRuns = NULL; - - PEXT2_EXTENT Chain = NULL; - PEXT2_EXTENT Extent = NULL; - - LONGLONG Vbn = 0; - ULONG Length = 0; - ULONG i = 0; - - NTSTATUS Status = STATUS_SUCCESS; - - __try { - - /* now building all the request extents */ - while (Vbn < RequestVbn->QuadPart) { - - Length = 0x80000000; /* 2g bytes */ - if (RequestVbn->QuadPart < Vbn + Length) { - Length = (ULONG)(RequestVbn->QuadPart - Vbn); - } - - /* build extents for sub-range */ - Extent = NULL; - Status = Ext2BuildExtents( - IrpContext, - Vcb, - Fcb->Mcb, - Vbn, - Length, - FALSE, - &Extent); - - if (!NT_SUCCESS(Status)) { - __leave; - } - - if (Chain) { - Ext2JointExtents(Chain, Extent); - } else { - Chain = Extent; - } - - /* allocate extent array */ - PartialRuns = Ext2AllocatePool( - NonPagedPool, - (Ext2CountExtents(Chain) + 2) * - (2 * sizeof(LARGE_INTEGER)), - 'RE2E'); - - if (PartialRuns == NULL) { - Status = STATUS_INSUFFICIENT_RESOURCES; - __leave; - } - RtlZeroMemory( PartialRuns, - (Ext2CountExtents(Chain) + 2) * - (2 * sizeof(LARGE_INTEGER))); - - if (MappedRuns) { - RtlMoveMemory(PartialRuns, - MappedRuns, - i * 2 * sizeof(LARGE_INTEGER)); - Ext2FreePool(MappedRuns, 'RE2E'); - } - MappedRuns = PartialRuns; - - /* walk all the Mcb runs in Extent */ - for (; Extent != NULL; Extent = Extent->Next) { - MappedRuns[i*2 + 0].QuadPart = Vbn + Extent->Offset; - MappedRuns[i*2 + 1].QuadPart = Extent->Lba; - i = i+1; - } - - Vbn = Vbn + Length; - } - - *pMappedRuns = MappedRuns; - - } __finally { - - if (!NT_SUCCESS(Status) || Status == STATUS_PENDING) { - if (MappedRuns) { - Ext2FreePool(MappedRuns, 'RE2E'); - } - *pMappedRuns = NULL; - } - - if (Chain) { - Ext2DestroyExtentChain(Chain); - } - } - - return Status; -} - -NTSTATUS -Ext2QueryRetrievalPointers ( - IN PEXT2_IRP_CONTEXT IrpContext -) -{ - PIRP Irp = NULL; - PIO_STACK_LOCATION IrpSp; - PEXTENDED_IO_STACK_LOCATION EIrpSp; - - PDEVICE_OBJECT DeviceObject; - PFILE_OBJECT FileObject; - - PEXT2_VCB Vcb = NULL; - PEXT2_FCB Fcb = NULL; - PEXT2_CCB Ccb = NULL; - - PLARGE_INTEGER RequestVbn; - PLARGE_INTEGER * pMappedRuns; - - ULONG InputSize; - ULONG OutputSize; - - NTSTATUS Status = STATUS_SUCCESS; - - BOOLEAN FcbResourceAcquired = FALSE; - - __try { - - ASSERT(IrpContext); - Irp = IrpContext->Irp; - ASSERT(Irp); - - IrpSp = IoGetCurrentIrpStackLocation(Irp); - EIrpSp = (PEXTENDED_IO_STACK_LOCATION)IrpSp; - ASSERT(IrpSp); - - InputSize = EIrpSp->Parameters.FileSystemControl.InputBufferLength; - OutputSize = EIrpSp->Parameters.FileSystemControl.OutputBufferLength; - - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - DeviceObject = IrpContext->DeviceObject; - - DbgBreak(); - - /* This request is not allowed on the main device object */ - if (IsExt2FsDevice(DeviceObject)) { - Status = STATUS_INVALID_DEVICE_REQUEST; - __leave; - } - - Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; - ASSERT(Vcb != NULL); - ASSERT((Vcb->Identifier.Type == EXT2VCB) && - (Vcb->Identifier.Size == sizeof(EXT2_VCB))); - ASSERT(IsMounted(Vcb)); - - FileObject = IrpContext->FileObject; - Fcb = (PEXT2_FCB) FileObject->FsContext; - - /* check Fcb is valid or not */ - if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - - ASSERT((Fcb->Identifier.Type == EXT2FCB) && - (Fcb->Identifier.Size == sizeof(EXT2_FCB))); - if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { - Status = STATUS_FILE_DELETED; - __leave; - } - - Ccb = (PEXT2_CCB) FileObject->FsContext2; - if (Ccb == NULL) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - - ASSERT((Ccb->Identifier.Type == EXT2CCB) && - (Ccb->Identifier.Size == sizeof(EXT2_CCB))); - - /* Is requstor in kernel and Fcb a paging file ? */ - if (Irp->RequestorMode != KernelMode || - !IsFlagOn(Fcb->Flags, FCB_PAGE_FILE) || - InputSize != sizeof(LARGE_INTEGER) || - OutputSize != sizeof(PVOID)) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - - if (!ExAcquireResourceExclusiveLite ( - &Fcb->MainResource, Ext2CanIWait())) { - Status = STATUS_PENDING; - __leave; - } - FcbResourceAcquired = TRUE; - - RequestVbn = EIrpSp->Parameters.FileSystemControl.Type3InputBuffer; - pMappedRuns = Irp->UserBuffer; - - DbgBreak(); - - /* request size beyonds whole file size */ - if (RequestVbn->QuadPart >= Fcb->Header.AllocationSize.QuadPart) { - Status = STATUS_END_OF_FILE; - __leave; - } - - Status = Ext2QueryExtentMappings( - IrpContext, - Vcb, - Fcb, - RequestVbn, - pMappedRuns - ); - - } __finally { - - if (FcbResourceAcquired) { - ExReleaseResourceLite(&Fcb->MainResource); - } - - if (!AbnormalTermination()) { - if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { - Status = Ext2QueueRequest(IrpContext); - } else { - Ext2CompleteIrpContext(IrpContext, Status); - } - } - } - - return Status; -} - - -NTSTATUS -Ext2GetRetrievalPointers ( - IN PEXT2_IRP_CONTEXT IrpContext -) -{ - PIRP Irp = NULL; - PIO_STACK_LOCATION IrpSp; - PEXTENDED_IO_STACK_LOCATION EIrpSp; - - PDEVICE_OBJECT DeviceObject; - PFILE_OBJECT FileObject; - - PEXT2_VCB Vcb = NULL; - PEXT2_FCB Fcb = NULL; - PEXT2_CCB Ccb = NULL; - - PSTARTING_VCN_INPUT_BUFFER SVIB; - PRETRIEVAL_POINTERS_BUFFER RPSB; - - PEXT2_EXTENT Chain = NULL; - PEXT2_EXTENT Extent = NULL; - - LONGLONG Vbn = 0; - ULONG Length = 0; - ULONG i = 0; - - ULONG UsedSize = 0; - ULONG InputSize; - ULONG OutputSize; - - NTSTATUS Status = STATUS_SUCCESS; - - BOOLEAN FcbResourceAcquired = FALSE; - - __try { - - ASSERT(IrpContext); - Irp = IrpContext->Irp; - ASSERT(Irp); - - IrpSp = IoGetCurrentIrpStackLocation(Irp); - EIrpSp = (PEXTENDED_IO_STACK_LOCATION)IrpSp; - ASSERT(IrpSp); - - InputSize = EIrpSp->Parameters.FileSystemControl.InputBufferLength; - OutputSize = EIrpSp->Parameters.FileSystemControl.OutputBufferLength; - - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - DeviceObject = IrpContext->DeviceObject; - - /* This request is not allowed on the main device object */ - if (IsExt2FsDevice(DeviceObject)) { - Status = STATUS_INVALID_DEVICE_REQUEST; - __leave; - } - - Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; - ASSERT(Vcb != NULL); - ASSERT((Vcb->Identifier.Type == EXT2VCB) && - (Vcb->Identifier.Size == sizeof(EXT2_VCB))); - ASSERT(IsMounted(Vcb)); - - FileObject = IrpContext->FileObject; - Fcb = (PEXT2_FCB) FileObject->FsContext; - - /* check Fcb is valid or not */ - if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - - ASSERT((Fcb->Identifier.Type == EXT2FCB) && - (Fcb->Identifier.Size == sizeof(EXT2_FCB))); - - if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { - Status = STATUS_FILE_DELETED; - __leave; - } - - Ccb = (PEXT2_CCB) FileObject->FsContext2; - if (Ccb == NULL) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - - ASSERT((Ccb->Identifier.Type == EXT2CCB) && - (Ccb->Identifier.Size == sizeof(EXT2_CCB))); - - if (InputSize < sizeof(STARTING_VCN_INPUT_BUFFER) || - OutputSize < sizeof(RETRIEVAL_POINTERS_BUFFER) ) { - Status = STATUS_BUFFER_TOO_SMALL; - __leave; - } - - if (!ExAcquireResourceExclusiveLite ( - &Fcb->MainResource, Ext2CanIWait())) { - Status = STATUS_PENDING; - __leave; - } - FcbResourceAcquired = TRUE; - - SVIB = (PSTARTING_VCN_INPUT_BUFFER) - EIrpSp->Parameters.FileSystemControl.Type3InputBuffer; - RPSB = (PRETRIEVAL_POINTERS_BUFFER) Ext2GetUserBuffer(Irp); - - /* probe user buffer */ - - __try { - if (Irp->RequestorMode != KernelMode) { - ProbeForRead (SVIB, InputSize, sizeof(UCHAR)); - ProbeForWrite(RPSB, OutputSize, sizeof(UCHAR)); - } - } __except(EXCEPTION_EXECUTE_HANDLER) { - Status = STATUS_INVALID_USER_BUFFER; - } - - if (!NT_SUCCESS(Status)) { - __leave; - } - - UsedSize = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[0]); - - /* request size beyonds whole file size ? */ - DEBUG(DL_DBG, ("Ext2GetRetrievalPointers: Startin from Vbn: %I64xh\n", - SVIB->StartingVcn.QuadPart)); - Vbn = (SVIB->StartingVcn.QuadPart << BLOCK_BITS); - if (Vbn >= Fcb->Header.AllocationSize.QuadPart ) { - Status = STATUS_END_OF_FILE; - __leave; - } - - /* now building all the request extents */ - while (Vbn < Fcb->Header.AllocationSize.QuadPart) { - - ASSERT(Chain == NULL); - Length = 0x80000000; /* 2g bytes */ - if (Fcb->Header.AllocationSize.QuadPart < Vbn + Length) { - Length = (ULONG)(Fcb->Header.AllocationSize.QuadPart - Vbn); - } - - /* build extents for sub-range */ - Status = Ext2BuildExtents( - IrpContext, - Vcb, - Fcb->Mcb, - Vbn, - Length, - FALSE, - &Chain); - - if (!NT_SUCCESS(Status)) { - DbgBreak(); - __leave; - } - - /* fill user buffer of RETRIEVAL_POINTERS_BUFFER */ - Extent = Chain; - while (Extent) { - - DEBUG(DL_MAP, ("Ext2GetRetrievalPointers: %wZ %d Vbn = %I64xh Lbn = %I64xh\n", - &Fcb->Mcb->FullName, i, - ((Vbn + Extent->Offset) >> BLOCK_BITS), - Extent->Lba)); - - RPSB->Extents[i].Lcn.QuadPart = (Extent->Lba >> BLOCK_BITS); - RPSB->Extents[i].NextVcn.QuadPart = ((Vbn + Extent->Offset + Extent->Length) >> BLOCK_BITS); - if (i == 0) { - RPSB->StartingVcn.QuadPart = ((Vbn + Extent->Offset) >> BLOCK_BITS); - } else { - ASSERT(RPSB->Extents[i-1].NextVcn.QuadPart == ((Vbn + Extent->Offset) >> BLOCK_BITS)); - } - if (UsedSize + sizeof(RETRIEVAL_POINTERS_BUFFER) > OutputSize) { - Status = STATUS_BUFFER_OVERFLOW; - __leave; - } - UsedSize += sizeof(LARGE_INTEGER) * 2; - Irp->IoStatus.Information = (ULONG_PTR)UsedSize; - RPSB->ExtentCount = ++i; - Extent = Extent->Next; - } - - if (Chain) { - Ext2DestroyExtentChain(Chain); - Chain = NULL; - } - - Vbn = Vbn + Length; - } - -#if 0 - { - NTSTATUS _s; - ULONG _i = 0; - LARGE_INTEGER RequestVbn = Fcb->Header.AllocationSize; - PLARGE_INTEGER MappedRuns = NULL; - - _s = Ext2QueryExtentMappings( - IrpContext, - Vcb, - Fcb, - &RequestVbn, - &MappedRuns - ); - if (!NT_SUCCESS(_s) || NULL == MappedRuns) { - DbgBreak(); - goto exit_to_get_rps; - } - - while (MappedRuns[_i*2 + 0].QuadPart != 0 || - MappedRuns[_i*2 + 1].QuadPart != 0 ) { - DEBUG(DL_MAP, ("Ext2QueryExtentMappings: %wZ %d Vbn = %I64xh Lbn = %I64xh\n", - &Fcb->Mcb->FullName, _i, - MappedRuns[_i*2 + 0].QuadPart, - MappedRuns[_i*2 + 1].QuadPart)); - _i++; - } - -exit_to_get_rps: - - if (MappedRuns) { - Ext2FreePool(MappedRuns, 'RE2E'); - } - } -#endif - - } __finally { - - if (FcbResourceAcquired) { - ExReleaseResourceLite(&Fcb->MainResource); - } - - if (Chain) { - Ext2DestroyExtentChain(Chain); - } - - if (!AbnormalTermination()) { - if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { - Status = Ext2QueueRequest(IrpContext); - } else { - Ext2CompleteIrpContext(IrpContext, Status); - } - } - } - - return Status; -} - -NTSTATUS -Ext2GetRetrievalPointerBase ( - IN PEXT2_IRP_CONTEXT IrpContext -) -{ - PIRP Irp = NULL; - PIO_STACK_LOCATION IrpSp; - PEXTENDED_IO_STACK_LOCATION EIrpSp; - - PDEVICE_OBJECT DeviceObject; - PFILE_OBJECT FileObject; - - PEXT2_VCB Vcb = NULL; - PEXT2_FCB Fcb = NULL; - PEXT2_CCB Ccb = NULL; - - PLARGE_INTEGER FileAreaOffset; - - ULONG OutputSize; - - NTSTATUS Status = STATUS_SUCCESS; - - BOOLEAN FcbResourceAcquired = FALSE; - - __try { - - ASSERT(IrpContext); - Irp = IrpContext->Irp; - ASSERT(Irp); - - IrpSp = IoGetCurrentIrpStackLocation(Irp); - EIrpSp = (PEXTENDED_IO_STACK_LOCATION)IrpSp; - ASSERT(IrpSp); - - OutputSize = EIrpSp->Parameters.FileSystemControl.OutputBufferLength; - - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - DeviceObject = IrpContext->DeviceObject; - - /* This request is not allowed on the main device object */ - if (IsExt2FsDevice(DeviceObject)) { - Status = STATUS_INVALID_DEVICE_REQUEST; - __leave; - } - - Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; - ASSERT(Vcb != NULL); - ASSERT((Vcb->Identifier.Type == EXT2VCB) && - (Vcb->Identifier.Size == sizeof(EXT2_VCB))); - ASSERT(IsMounted(Vcb)); - - FileObject = IrpContext->FileObject; - Fcb = (PEXT2_FCB) FileObject->FsContext; - - /* check Fcb is valid or not */ - if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - - ASSERT((Fcb->Identifier.Type == EXT2FCB) && - (Fcb->Identifier.Size == sizeof(EXT2_FCB))); - - if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { - Status = STATUS_FILE_DELETED; - __leave; - } - - Ccb = (PEXT2_CCB) FileObject->FsContext2; - if (Ccb == NULL) { - Status = STATUS_INVALID_PARAMETER; - __leave; - } - - ASSERT((Ccb->Identifier.Type == EXT2CCB) && - (Ccb->Identifier.Size == sizeof(EXT2_CCB))); - - if (OutputSize < sizeof(LARGE_INTEGER)) { - Status = STATUS_BUFFER_TOO_SMALL; - __leave; - } - - if (!ExAcquireResourceExclusiveLite ( - &Fcb->MainResource, Ext2CanIWait())) { - Status = STATUS_PENDING; - __leave; - } - FcbResourceAcquired = TRUE; - - FileAreaOffset = (PLARGE_INTEGER) Ext2GetUserBuffer(Irp); - - /* probe user buffer */ - - __try { - if (Irp->RequestorMode != KernelMode) { - ProbeForWrite(FileAreaOffset, OutputSize, sizeof(UCHAR)); - } - - } __except(EXCEPTION_EXECUTE_HANDLER) { - - Status = STATUS_INVALID_USER_BUFFER; - } - - if (!NT_SUCCESS(Status)) { - __leave; - } - - DEBUG(DL_DBG, ("Ext2GetRetrievalPointerBase: FileAreaOffset is 0.\n")); - - FileAreaOffset->QuadPart = 0; // sector offset to the first allocatable unit on the filesystem - - Irp->IoStatus.Information = sizeof(LARGE_INTEGER); - - } __finally { - - if (FcbResourceAcquired) { - ExReleaseResourceLite(&Fcb->MainResource); - } - - if (!AbnormalTermination()) { - if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { - Status = Ext2QueueRequest(IrpContext); - } else { - Ext2CompleteIrpContext(IrpContext, Status); - } - } - } - - 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) || - !IsFlagOn(Ccb->Flags, CCB_OPEN_REPARSE_POINT)) { - 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; - - 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; - - BOOLEAN MainResourceAcquired = FALSE; - BOOLEAN FcbLockAcquired = FALSE; - - __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); - - ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); - FcbLockAcquired = TRUE; - - ParentMcb = Mcb->Parent; - ParentDcb = ParentMcb->Fcb; - if (ParentDcb == NULL) { - ParentDcb = Ext2AllocateFcb(Vcb, ParentMcb); - } - if (ParentDcb) { - Ext2ReferXcb(&ParentDcb->ReferenceCount); - } - - if (!Mcb) - __leave; - - if (FcbLockAcquired) { - ExReleaseResourceLite(&Vcb->FcbLock); - FcbLockAcquired = FALSE; - } - - 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] = '/'; - } - } - - /* free all data blocks of the inode (to be set as symlink) */ - { - LARGE_INTEGER zero = {0}; - Status = Ext2TruncateFile(IrpContext, Vcb, Mcb, &zero); - } - - /* decrease dir count of group desc and vcb stat */ - if (S_ISDIR(Mcb->Inode.i_mode)) { - - ULONG group = (Mcb->Inode.i_ino - 1) / INODES_PER_GROUP; - Ext2UpdateGroupDirStat(IrpContext, Vcb, group); - - /* drop extra reference for dir inode */ - ext3_dec_count(&Mcb->Inode); - } - - /* overwrite inode mode as type SYMLINK */ - Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); - SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_REPARSE_POINT); - - Status = Ext2WriteSymlink(IrpContext, Vcb, Mcb, OemNameBuffer, - OemNameLength, &BytesWritten); - if (NT_SUCCESS(Status)) { - Ext2SetFileType(IrpContext, Vcb, ParentDcb, Mcb, - S_IFLNK | S_IRWXUGO); - } - - } __finally { - - if (FcbLockAcquired) { - ExReleaseResourceLite(&Vcb->FcbLock); - FcbLockAcquired = FALSE; - } - - 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 (!AbnormalTermination()) { - if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { - Status = Ext2QueueRequest(IrpContext); - } else { - Ext2CompleteIrpContext(IrpContext, Status); - } - } - - if (ParentDcb) { - Ext2ReleaseFcb(ParentDcb); - } - } - - 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; - - PEXT2_FCB ParentDcb = NULL; /* Dcb of it's current parent */ - PEXT2_MCB ParentMcb = NULL; - - NTSTATUS Status = STATUS_UNSUCCESSFUL; - - BOOLEAN FcbLockAcquired = FALSE; - BOOLEAN MainResourceAcquired = FALSE; - - - __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; - - ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); - FcbLockAcquired = TRUE; - - ParentMcb = Mcb->Parent; - ParentDcb = ParentMcb->Fcb; - if (ParentDcb == NULL) { - ParentDcb = Ext2AllocateFcb(Vcb, ParentMcb); - } - if (ParentDcb) { - Ext2ReferXcb(&ParentDcb->ReferenceCount); - } - - if (!Mcb || !IsInodeSymLink(&Mcb->Inode) || - !IsFlagOn(Ccb->Flags, CCB_OPEN_REPARSE_POINT)) { - Status = STATUS_NOT_A_REPARSE_POINT; - __leave; - } - - Fcb = Ext2AllocateFcb (Vcb, Mcb); - if (Fcb) { - Ext2ReferXcb(&Fcb->ReferenceCount); - } else { - Status = STATUS_INSUFFICIENT_RESOURCES; - __leave; - } - - if (FcbLockAcquired) { - ExReleaseResourceLite(&Vcb->FcbLock); - FcbLockAcquired = FALSE; - } - - 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; - } - - /* inode is to be removed */ - SetFlag(Ccb->Flags, CCB_DELETE_ON_CLOSE); - - } __finally { - - if (FcbLockAcquired) { - ExReleaseResourceLite(&Vcb->FcbLock); - } - - if (MainResourceAcquired) { - ExReleaseResourceLite(&Fcb->MainResource); - } - - if (NT_SUCCESS(Status)) { - Ext2NotifyReportChange( - IrpContext, - Vcb, - Mcb, - FILE_NOTIFY_CHANGE_ATTRIBUTES, - FILE_ACTION_MODIFIED ); - - } - - if (!AbnormalTermination()) { - if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { - Status = Ext2QueueRequest(IrpContext); - } else { - Ext2CompleteIrpContext(IrpContext, Status); - } - } - - if (ParentDcb) { - Ext2ReleaseFcb(ParentDcb); - } - - if (Fcb) { - Ext2ReleaseFcb(Fcb); - } - } - - return Status; -} - -NTSTATUS -Ext2UserFsRequest (IN PEXT2_IRP_CONTEXT IrpContext) -{ - PIRP Irp; - PIO_STACK_LOCATION IoStackLocation; - ULONG FsControlCode; - NTSTATUS Status; - - ASSERT(IrpContext); - - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - Irp = IrpContext->Irp; - IoStackLocation = IoGetCurrentIrpStackLocation(Irp); - -#ifndef _GNU_NTIFS_ - FsControlCode = - IoStackLocation->Parameters.FileSystemControl.FsControlCode; -#else - FsControlCode = ((PEXTENDED_IO_STACK_LOCATION) - IoStackLocation)->Parameters.FileSystemControl.FsControlCode; -#endif - - 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; - - case FSCTL_UNLOCK_VOLUME: - Status = Ext2UnlockVolume(IrpContext); - break; - - case FSCTL_DISMOUNT_VOLUME: - Status = Ext2DismountVolume(IrpContext); - break; - - case FSCTL_IS_VOLUME_MOUNTED: - Status = Ext2IsVolumeMounted(IrpContext); - break; - - case FSCTL_INVALIDATE_VOLUMES: - Status = Ext2InvalidateVolumes(IrpContext); - break; - -#if (_WIN32_WINNT >= 0x0500) - case FSCTL_ALLOW_EXTENDED_DASD_IO: - Status = Ext2AllowExtendedDasdIo(IrpContext); - break; -#endif //(_WIN32_WINNT >= 0x0500) - - case FSCTL_REQUEST_OPLOCK_LEVEL_1: - case FSCTL_REQUEST_OPLOCK_LEVEL_2: - case FSCTL_REQUEST_BATCH_OPLOCK: - case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: - case FSCTL_OPBATCH_ACK_CLOSE_PENDING: - case FSCTL_OPLOCK_BREAK_NOTIFY: - case FSCTL_OPLOCK_BREAK_ACK_NO_2: - - Status = Ext2OplockRequest(IrpContext); - break; - - case FSCTL_IS_VOLUME_DIRTY: - Status = Ext2IsVolumeDirty(IrpContext); - break; - - case FSCTL_QUERY_RETRIEVAL_POINTERS: - Status = Ext2QueryRetrievalPointers(IrpContext); - break; - - case FSCTL_GET_RETRIEVAL_POINTERS: - Status = Ext2GetRetrievalPointers(IrpContext); - break; - - case FSCTL_GET_RETRIEVAL_POINTER_BASE: - Status = Ext2GetRetrievalPointerBase(IrpContext); - break; - - default: - - DEBUG(DL_INF, ( "Ext2UserFsRequest: Invalid User Request: %xh.\n", FsControlCode)); - Status = STATUS_INVALID_DEVICE_REQUEST; - - Ext2CompleteIrpContext(IrpContext, Status); - } - - return Status; -} - -BOOLEAN -Ext2IsMediaWriteProtected ( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PDEVICE_OBJECT TargetDevice -) -{ - PIRP Irp; - KEVENT Event; - NTSTATUS Status; - IO_STATUS_BLOCK IoStatus; - - KeInitializeEvent(&Event, NotificationEvent, FALSE); - - Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_IS_WRITABLE, - TargetDevice, - NULL, - 0, - NULL, - 0, - FALSE, - &Event, - &IoStatus ); - - if (Irp == NULL) { - return FALSE; - } - - SetFlag(IoGetNextIrpStackLocation(Irp)->Flags, SL_OVERRIDE_VERIFY_VOLUME); - - Status = IoCallDriver(TargetDevice, Irp); - - if (Status == STATUS_PENDING) { - - (VOID) KeWaitForSingleObject( &Event, - Executive, - KernelMode, - FALSE, - (PLARGE_INTEGER)NULL ); - - Status = IoStatus.Status; - } - - return (BOOLEAN)(Status == STATUS_MEDIA_WRITE_PROTECTED); -} - -NTSTATUS -Ext2MountVolume (IN PEXT2_IRP_CONTEXT IrpContext) -{ - PDEVICE_OBJECT MainDeviceObject; - BOOLEAN GlobalDataResourceAcquired = FALSE; - PIRP Irp; - PIO_STACK_LOCATION IoStackLocation; - PDEVICE_OBJECT TargetDeviceObject; - NTSTATUS Status = STATUS_UNRECOGNIZED_VOLUME; - PDEVICE_OBJECT VolumeDeviceObject = NULL; - PEXT2_VCB Vcb = NULL, OldVcb = NULL; - PVPB OldVpb = NULL, Vpb = NULL; - PEXT2_SUPER_BLOCK Ext2Sb = NULL; - ULONG dwBytes; - DISK_GEOMETRY DiskGeometry; - - __try { - - ASSERT(IrpContext != NULL); - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - MainDeviceObject = IrpContext->DeviceObject; - - // - // Make sure we can wait. - // - - SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); - - // - // This request is only allowed on the main device object - // - if (!IsExt2FsDevice(MainDeviceObject)) { - Status = STATUS_INVALID_DEVICE_REQUEST; - __leave; - } - - if (IsFlagOn(Ext2Global->Flags, EXT2_UNLOAD_PENDING)) { - Status = STATUS_UNRECOGNIZED_VOLUME; - __leave; - } - -#if 0 - if (IrpContext->RealDevice->Size >= sizeof(ULONG) + sizeof(DEVICE_OBJECT) && - *((PULONG)IrpContext->RealDevice->DeviceExtension) == 'DSSA') { - } else { - Status = STATUS_UNRECOGNIZED_VOLUME; - __leave; - } -#endif - - Irp = IrpContext->Irp; - IoStackLocation = IoGetCurrentIrpStackLocation(Irp); - TargetDeviceObject = - IoStackLocation->Parameters.MountVolume.DeviceObject; - - dwBytes = sizeof(DISK_GEOMETRY); - Status = Ext2DiskIoControl( - TargetDeviceObject, - IOCTL_DISK_GET_DRIVE_GEOMETRY, - NULL, - 0, - &DiskGeometry, - &dwBytes ); - - if (!NT_SUCCESS(Status)) { - __leave; - } - - Status = IoCreateDevice( - MainDeviceObject->DriverObject, - sizeof(EXT2_VCB), - NULL, - FILE_DEVICE_DISK_FILE_SYSTEM, - 0, - FALSE, - &VolumeDeviceObject ); - - if (!NT_SUCCESS(Status)) { - __leave; - } - INC_MEM_COUNT(PS_VCB, VolumeDeviceObject, sizeof(EXT2_VCB)); - -#ifdef _PNP_POWER_ - /* don't care about power management requests */ - VolumeDeviceObject->DeviceObjectExtension->PowerControlNeeded = FALSE; -#endif - - VolumeDeviceObject->StackSize = (CCHAR)(TargetDeviceObject->StackSize + 1); - ClearFlag(VolumeDeviceObject->Flags, DO_DEVICE_INITIALIZING); - -/* - These are for buffer-address alignment requirements. - Never do this check, unless you want fail user requests :) - - if (TargetDeviceObject->AlignmentRequirement > - VolumeDeviceObject->AlignmentRequirement) { - - VolumeDeviceObject->AlignmentRequirement = - TargetDeviceObject->AlignmentRequirement; - } - - if (DiskGeometry.BytesPerSector - 1 > - VolumeDeviceObject->AlignmentRequirement) { - VolumeDeviceObject->AlignmentRequirement = - DiskGeometry.BytesPerSector - 1; - TargetDeviceObject->AlignmentRequirement = - DiskGeometry.BytesPerSector - 1; - } -*/ - (IoStackLocation->Parameters.MountVolume.Vpb)->DeviceObject = - VolumeDeviceObject; - Vpb = IoStackLocation->Parameters.MountVolume.Vpb; - - Vcb = (PEXT2_VCB) VolumeDeviceObject->DeviceExtension; - - RtlZeroMemory(Vcb, sizeof(EXT2_VCB)); - Vcb->Identifier.Type = EXT2VCB; - Vcb->Identifier.Size = sizeof(EXT2_VCB); - Vcb->TargetDeviceObject = TargetDeviceObject; - Vcb->DiskGeometry = DiskGeometry; - InitializeListHead(&Vcb->Next); - - Status = Ext2LoadSuper(Vcb, FALSE, &Ext2Sb); - if (!NT_SUCCESS(Status)) { - Vcb = NULL; - Status = STATUS_UNRECOGNIZED_VOLUME; - __leave; - } - ASSERT (NULL != Ext2Sb); - - /* check Linux Ext2/Ext3 volume magic */ - if (Ext2Sb->s_magic == EXT2_SUPER_MAGIC) { - DEBUG(DL_INF, ( "Volume of ext2 file system is found.\n")); - } else { - Status = STATUS_UNRECOGNIZED_VOLUME; - Vcb = NULL; - __leave; - } - - DEBUG(DL_DBG, ("Ext2MountVolume: DevObject=%p Vcb=%p\n", VolumeDeviceObject, Vcb)); - - /* initialize Vcb structure */ - Status = Ext2InitializeVcb( IrpContext, Vcb, Ext2Sb, - TargetDeviceObject, - VolumeDeviceObject, Vpb); - - if (NT_SUCCESS(Status)) { - - PLIST_ENTRY List; - - ExAcquireResourceExclusiveLite(&(Ext2Global->Resource), TRUE); - GlobalDataResourceAcquired = TRUE; - - for (List = Ext2Global->VcbList.Flink; - List != &Ext2Global->VcbList; - List = List->Flink) { - - OldVcb = CONTAINING_RECORD(List, EXT2_VCB, Next); - OldVpb = OldVcb->Vpb; - - /* in case we are already in the queue, should not happen */ - if (OldVpb == Vpb) { - continue; - } - - if ( (OldVpb->SerialNumber == Vpb->SerialNumber) && - (!IsMounted(OldVcb)) && (IsFlagOn(OldVcb->Flags, VCB_NEW_VPB)) && - (OldVpb->RealDevice == TargetDeviceObject) && - (OldVpb->VolumeLabelLength == Vpb->VolumeLabelLength) && - (RtlEqualMemory(&OldVpb->VolumeLabel[0], - &Vpb->VolumeLabel[0], - Vpb->VolumeLabelLength)) && - (RtlEqualMemory(&OldVcb->SuperBlock->s_uuid[0], - &Vcb->SuperBlock->s_uuid[0], 16)) ) { - ClearFlag(OldVcb->Flags, VCB_MOUNTED); - } - } - - SetLongFlag(Vcb->Flags, VCB_MOUNTED); - SetFlag(Vcb->Vpb->Flags, VPB_MOUNTED); - Ext2InsertVcb(Vcb); - Vcb = NULL; - Vpb = NULL; - ObDereferenceObject(TargetDeviceObject); - - } else { - - Vcb = NULL; - } - - } __finally { - - if (GlobalDataResourceAcquired) { - ExReleaseResourceLite(&Ext2Global->Resource); - } - - if (!NT_SUCCESS(Status)) { - - if (!NT_SUCCESS(Status)) { - if ( Vpb != NULL ) { - Vpb->DeviceObject = NULL; - } - } - - if (Vcb) { - Ext2DestroyVcb(Vcb); - } else { - if (Ext2Sb) { - Ext2FreePool(Ext2Sb, EXT2_SB_MAGIC); - } - if (VolumeDeviceObject) { - IoDeleteDevice(VolumeDeviceObject); - DEC_MEM_COUNT(PS_VCB, VolumeDeviceObject, sizeof(EXT2_VCB)); - } - } - } - - if (!IrpContext->ExceptionInProgress) { - Ext2CompleteIrpContext(IrpContext, Status); - } - } - - return Status; -} - -VOID -Ext2VerifyVcb (IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb ) -{ - NTSTATUS Status = STATUS_SUCCESS; - - BOOLEAN bVerify = FALSE; - ULONG ChangeCount = 0; - ULONG dwBytes; - - PIRP Irp; - PEXTENDED_IO_STACK_LOCATION IrpSp; - - __try { - - ASSERT(IrpContext != NULL); - - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - Irp = IrpContext->Irp; - IrpSp = (PEXTENDED_IO_STACK_LOCATION)IoGetCurrentIrpStackLocation(Irp); - - bVerify = IsFlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); - - if ( (IsFlagOn(Vcb->Flags, VCB_REMOVABLE_MEDIA) || - IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) && !bVerify ) { - - dwBytes = sizeof(ULONG); - Status = Ext2DiskIoControl( - Vcb->TargetDeviceObject, - IOCTL_DISK_CHECK_VERIFY, - NULL, - 0, - &ChangeCount, - &dwBytes ); - - if ( STATUS_VERIFY_REQUIRED == Status || - STATUS_DEVICE_NOT_READY == Status || - STATUS_NO_MEDIA_IN_DEVICE == Status || - (NT_SUCCESS(Status) && - (ChangeCount != Vcb->ChangeCount))) { - - KIRQL Irql; - - IoAcquireVpbSpinLock(&Irql); - if (Vcb->Vpb == Vcb->Vpb->RealDevice->Vpb) { - SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); - } - IoReleaseVpbSpinLock(Irql); - - } else { - - if (!NT_SUCCESS(Status)) { - Ext2NormalizeAndRaiseStatus(IrpContext, Status); - } - } - } - - if ( IsFlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) { - IoSetHardErrorOrVerifyDevice( Irp, Vcb->Vpb->RealDevice ); - Ext2NormalizeAndRaiseStatus ( IrpContext, - STATUS_VERIFY_REQUIRED ); - } - - if (IsMounted(Vcb)) { - - if ( (IrpContext->MajorFunction == IRP_MJ_WRITE) || - (IrpContext->MajorFunction == IRP_MJ_SET_INFORMATION) || - (IrpContext->MajorFunction == IRP_MJ_SET_EA) || - (IrpContext->MajorFunction == IRP_MJ_FLUSH_BUFFERS) || - (IrpContext->MajorFunction == IRP_MJ_SET_VOLUME_INFORMATION) || - (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && - IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST && - IrpSp->Parameters.FileSystemControl.FsControlCode == - FSCTL_MARK_VOLUME_DIRTY)) { - - if (IsFlagOn(Vcb->Flags, VCB_WRITE_PROTECTED)) { - - KIRQL Irql; - - IoAcquireVpbSpinLock(&Irql); - if (Vcb->Vpb == Vcb->Vpb->RealDevice->Vpb) { - SetFlag (Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); - } - IoReleaseVpbSpinLock(Irql); - - IoSetHardErrorOrVerifyDevice( Irp, Vcb->Vpb->RealDevice ); - - Ext2RaiseStatus(IrpContext, STATUS_MEDIA_WRITE_PROTECTED); - } - } - } - - } __finally { - - } - -} - - -NTSTATUS -Ext2VerifyVolume (IN PEXT2_IRP_CONTEXT IrpContext) -{ - PDEVICE_OBJECT DeviceObject; - NTSTATUS Status = STATUS_UNSUCCESSFUL; - PEXT2_SUPER_BLOCK ext2_sb = NULL; - PEXT2_VCB Vcb = NULL; - BOOLEAN VcbResourceAcquired = FALSE; - PIRP Irp; - ULONG ChangeCount = 0; - ULONG dwBytes; - - __try { - - ASSERT(IrpContext != NULL); - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - DeviceObject = IrpContext->DeviceObject; - // - // This request is not allowed on the main device object - // - if (IsExt2FsDevice(DeviceObject)) { - Status = STATUS_INVALID_DEVICE_REQUEST; - __leave; - } - - Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; - ASSERT(Vcb != NULL); - ASSERT((Vcb->Identifier.Type == EXT2VCB) && - (Vcb->Identifier.Size == sizeof(EXT2_VCB))); - - VcbResourceAcquired = - ExAcquireResourceExclusiveLite( - &Vcb->MainResource, - TRUE ); - - if (!FlagOn(Vcb->TargetDeviceObject->Flags, DO_VERIFY_VOLUME)) { - Status = STATUS_SUCCESS; - __leave; - } - - if (!IsMounted(Vcb)) { - Status = STATUS_WRONG_VOLUME; - __leave; - } - - dwBytes = sizeof(ULONG); - Status = Ext2DiskIoControl( - Vcb->TargetDeviceObject, - IOCTL_DISK_CHECK_VERIFY, - NULL, - 0, - &ChangeCount, - &dwBytes ); - - - if (!NT_SUCCESS(Status)) { - Status = STATUS_WRONG_VOLUME; - __leave; - } else { - Vcb->ChangeCount = ChangeCount; - } - - Irp = IrpContext->Irp; - - Status = Ext2LoadSuper(Vcb, TRUE, &ext2_sb); - - if (!NT_SUCCESS(Status)) { - __leave; - } - - ASSERT(NULL != ext2_sb); - if ((ext2_sb->s_magic == EXT2_SUPER_MAGIC) && - (memcmp(ext2_sb->s_uuid, SUPER_BLOCK->s_uuid, 16) == 0) && - (memcmp(ext2_sb->s_volume_name, SUPER_BLOCK->s_volume_name, 16) ==0)) { - - ClearFlag(Vcb->TargetDeviceObject->Flags, DO_VERIFY_VOLUME); - - if (Ext2IsMediaWriteProtected(IrpContext, Vcb->TargetDeviceObject)) { - SetLongFlag(Vcb->Flags, VCB_WRITE_PROTECTED); - } else { - ClearLongFlag(Vcb->Flags, VCB_WRITE_PROTECTED); - } - - DEBUG(DL_INF, ( "Ext2VerifyVolume: Volume verify succeeded.\n")); - - } else { - - Status = STATUS_WRONG_VOLUME; - Ext2PurgeVolume(Vcb, FALSE); - - SetLongFlag(Vcb->Flags, VCB_DISMOUNT_PENDING); - ClearFlag(Vcb->TargetDeviceObject->Flags, DO_VERIFY_VOLUME); - - DEBUG(DL_INF, ( "Ext2VerifyVolume: Volume verify failed.\n")); - } - - } __finally { - - if (ext2_sb) - Ext2FreePool(ext2_sb, EXT2_SB_MAGIC); - - if (VcbResourceAcquired) { - ExReleaseResourceLite(&Vcb->MainResource); - } - - if (!IrpContext->ExceptionInProgress) { - Ext2CompleteIrpContext(IrpContext, Status); - } - } - - return Status; -} - - -NTSTATUS -Ext2IsVolumeMounted (IN PEXT2_IRP_CONTEXT IrpContext) -{ - PDEVICE_OBJECT DeviceObject; - PEXT2_VCB Vcb = 0; - NTSTATUS Status = STATUS_SUCCESS; - - ASSERT(IrpContext); - - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - - DeviceObject = IrpContext->DeviceObject; - - Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; - - ASSERT(IsMounted(Vcb)); - - Ext2VerifyVcb (IrpContext, Vcb); - - Ext2CompleteIrpContext(IrpContext, Status); - - return Status; -} - - -NTSTATUS -Ext2DismountVolume (IN PEXT2_IRP_CONTEXT IrpContext) -{ - PDEVICE_OBJECT DeviceObject; - NTSTATUS Status = STATUS_UNSUCCESSFUL; - PEXT2_VCB Vcb = NULL; - BOOLEAN VcbResourceAcquired = FALSE; - - __try { - - ASSERT(IrpContext != NULL); - - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - DeviceObject = IrpContext->DeviceObject; - - // - // This request is not allowed on the main device object - // - if (IsExt2FsDevice(DeviceObject)) { - Status = STATUS_INVALID_DEVICE_REQUEST; - __leave; - } - - Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; - - ASSERT(Vcb != NULL); - - ASSERT((Vcb->Identifier.Type == EXT2VCB) && - (Vcb->Identifier.Size == sizeof(EXT2_VCB))); - - ASSERT(IsMounted(Vcb)); - - ExAcquireResourceExclusiveLite( - &Vcb->MainResource, - TRUE ); - - VcbResourceAcquired = TRUE; - - if ( IsFlagOn(Vcb->Flags, VCB_DISMOUNT_PENDING)) { - Status = STATUS_VOLUME_DISMOUNTED; - __leave; - } - - Ext2FlushFiles(IrpContext, Vcb, FALSE); - Ext2FlushVolume(IrpContext, Vcb, FALSE); - - ExReleaseResourceLite(&Vcb->MainResource); - VcbResourceAcquired = FALSE; - - Ext2PurgeVolume(Vcb, TRUE); - Ext2CheckDismount(IrpContext, Vcb, TRUE); - - DEBUG(DL_INF, ( "Ext2Dismount: Volume dismount pending.\n")); - Status = STATUS_SUCCESS; - - } __finally { - - if (VcbResourceAcquired) { - ExReleaseResourceLite(&Vcb->MainResource); - } - - if (!IrpContext->ExceptionInProgress) { - Ext2CompleteIrpContext(IrpContext, Status); - } - } - - return Status; -} - -BOOLEAN -Ext2CheckDismount ( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN BOOLEAN bForce ) -{ - KIRQL Irql; - PVPB Vpb = Vcb->Vpb, NewVpb = NULL; - BOOLEAN bDeleted = FALSE, bTearDown = FALSE; - ULONG UnCleanCount = 0; - - NewVpb = ExAllocatePoolWithTag(NonPagedPool, VPB_SIZE, TAG_VPB); - if (NewVpb == NULL) { - DEBUG(DL_ERR, ( "Ex2CheckDismount: failed to allocate NewVpb.\n")); - return FALSE; - } - DEBUG(DL_DBG, ("Ext2CheckDismount: NewVpb allocated: %p\n", NewVpb)); - INC_MEM_COUNT(PS_VPB, NewVpb, sizeof(VPB)); - memset(NewVpb, '_', VPB_SIZE); - RtlZeroMemory(NewVpb, sizeof(VPB)); - - ExAcquireResourceExclusiveLite( - &Ext2Global->Resource, TRUE ); - - ExAcquireResourceExclusiveLite( - &Vcb->MainResource, TRUE ); - - if (IrpContext && - IrpContext->MajorFunction == IRP_MJ_CREATE && - IrpContext->RealDevice == Vcb->RealDevice) { - UnCleanCount = 2; - } else { - UnCleanCount = 1; - } - - IoAcquireVpbSpinLock (&Irql); - - DEBUG(DL_DBG, ("Ext2CheckDismount: Vpb %p ioctl=%d Device %p\n", - Vpb, Vpb->ReferenceCount, Vpb->RealDevice)); - - if (Vpb->ReferenceCount <= UnCleanCount) { - - if (!IsFlagOn(Vcb->Flags, VCB_DISMOUNT_PENDING)) { - - ClearFlag(Vpb->Flags, VPB_MOUNTED); - ClearFlag(Vpb->Flags, VPB_LOCKED); - - if ((Vcb->RealDevice != Vpb->RealDevice) && - (Vcb->RealDevice->Vpb == Vpb)) { - SetFlag(Vcb->RealDevice->Flags, DO_DEVICE_INITIALIZING); - SetFlag(Vpb->Flags, VPB_PERSISTENT ); - } - - Ext2RemoveVcb(Vcb); - SetLongFlag(Vcb->Flags, VCB_DISMOUNT_PENDING); - } - - if (Vpb->ReferenceCount) { - bTearDown = TRUE; - } else { - bDeleted = TRUE; - Vpb->DeviceObject = NULL; - } - - DEBUG(DL_DBG, ("Ext2CheckDismount: Vpb: %p bDeleted=%d bTearDown=%d\n", - Vpb, bDeleted, bTearDown)); - - - } else if (bForce) { - - DEBUG(DL_DBG, ( "Ext2CheckDismount: New/Old Vpb %p/%p Realdevice = %p\n", - NewVpb, Vcb->Vpb, Vpb->RealDevice)); - - /* keep vpb president and later we'll free it */ - SetFlag(Vpb->Flags, VPB_PERSISTENT); - - Vcb->Vpb2 = Vcb->Vpb; - NewVpb->Type = IO_TYPE_VPB; - NewVpb->Size = sizeof(VPB); - NewVpb->Flags = Vpb->Flags & VPB_REMOVE_PENDING; - NewVpb->RealDevice = Vpb->RealDevice; - NewVpb->RealDevice->Vpb = NewVpb; - NewVpb = NULL; - ClearFlag(Vpb->Flags, VPB_MOUNTED); - SetLongFlag(Vcb->Flags, VCB_NEW_VPB); - ClearLongFlag(Vcb->Flags, VCB_MOUNTED); - } - - IoReleaseVpbSpinLock(Irql); - - ExReleaseResourceLite(&Vcb->MainResource); - ExReleaseResourceLite(&Ext2Global->Resource); - - if (bTearDown) { - DEBUG(DL_DBG, ( "Ext2CheckDismount: Tearing vcb %p ...\n", Vcb)); - Ext2TearDownStream(Vcb); - } - - if (bDeleted) { - DEBUG(DL_DBG, ( "Ext2CheckDismount: Deleting vcb %p ...\n", Vcb)); - Ext2DestroyVcb(Vcb); - } - - if (NewVpb != NULL) { - DEBUG(DL_DBG, ( "Ext2CheckDismount: freeing new Vpb %p\n", NewVpb)); - ExFreePoolWithTag(NewVpb, TAG_VPB); - DEC_MEM_COUNT(PS_VPB, NewVpb, sizeof(VPB)); - } - - return bDeleted; -} - -NTSTATUS -Ext2PurgeVolume (IN PEXT2_VCB Vcb, - IN BOOLEAN FlushBeforePurge ) -{ - PEXT2_FCB Fcb; - LIST_ENTRY List, *Next; - - BOOLEAN VcbResourceAcquired = FALSE; - BOOLEAN FcbResourceAcquired = FALSE; - BOOLEAN gdResourceAcquired = FALSE; - - __try { - - ASSERT(Vcb != NULL); - ASSERT((Vcb->Identifier.Type == EXT2VCB) && - (Vcb->Identifier.Size == sizeof(EXT2_VCB))); - - ExAcquireResourceExclusiveLite(&Vcb->MainResource, TRUE); - VcbResourceAcquired = TRUE; - - if (IsVcbReadOnly(Vcb)) { - FlushBeforePurge = FALSE; - } - - InitializeListHead(&List); - - ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); - FcbResourceAcquired = TRUE; - - while (!IsListEmpty(&Vcb->FcbList)) { - - Next = RemoveHeadList(&Vcb->FcbList); - Fcb = CONTAINING_RECORD(Next, EXT2_FCB, Next); - - DEBUG(DL_INF, ( "Ext2PurgeVolume: %wZ refercount=%xh\n", - &Fcb->Mcb->FullName, Fcb->ReferenceCount)); - InsertTailList(&List, &Fcb->Next); - } - - while (!IsListEmpty(&List)) { - - Next = RemoveHeadList(&List); - Fcb = CONTAINING_RECORD(Next, EXT2_FCB, Next); - - if (ExAcquireResourceExclusiveLite( - &Fcb->MainResource, - TRUE )) { - - Ext2PurgeFile(Fcb, FlushBeforePurge); - - if (Fcb->ReferenceCount <= 1) { - Fcb->TsDrop.QuadPart = 0; - InsertHeadList(&Vcb->FcbList, &Fcb->Next); - } else { - InsertTailList(&Vcb->FcbList, &Fcb->Next); - } - ExReleaseResourceLite(&Fcb->MainResource); - } - } - - if (FcbResourceAcquired) { - ExReleaseResourceLite(&Vcb->FcbLock); - FcbResourceAcquired = FALSE; - } - - /* acquire bd lock to avoid bh creation */ - ExAcquireResourceExclusiveLite(&Vcb->sbi.s_gd_lock, TRUE); - gdResourceAcquired = TRUE; - - /* discard buffer_headers for group_desc */ - Ext2DropBH(Vcb); - - if (FlushBeforePurge) { - ExAcquireSharedStarveExclusive(&Vcb->PagingIoResource, TRUE); - ExReleaseResourceLite(&Vcb->PagingIoResource); - - CcFlushCache(&Vcb->SectionObject, NULL, 0, NULL); - } - - if (Vcb->SectionObject.ImageSectionObject) { - MmFlushImageSection(&Vcb->SectionObject, MmFlushForWrite); - } - - if (Vcb->SectionObject.DataSectionObject) { - CcPurgeCacheSection(&Vcb->SectionObject, NULL, 0, FALSE); - } - - DEBUG(DL_INF, ( "Ext2PurgeVolume: Volume flushed and purged.\n")); - - } __finally { - - if (gdResourceAcquired) { - ExReleaseResourceLite(&Vcb->sbi.s_gd_lock); - } - - if (FcbResourceAcquired) { - ExReleaseResourceLite(&Vcb->FcbLock); - } - - if (VcbResourceAcquired) { - ExReleaseResourceLite(&Vcb->MainResource); - } - } - - return STATUS_SUCCESS; -} - -NTSTATUS -Ext2PurgeFile ( IN PEXT2_FCB Fcb, - IN BOOLEAN FlushBeforePurge ) -{ - IO_STATUS_BLOCK IoStatus; - - ASSERT(Fcb != NULL); - - ASSERT((Fcb->Identifier.Type == EXT2FCB) && - (Fcb->Identifier.Size == sizeof(EXT2_FCB))); - - - if (!IsVcbReadOnly(Fcb->Vcb) && FlushBeforePurge) { - DEBUG(DL_INF, ( "Ext2PurgeFile: CcFlushCache on %wZ.\n", - &Fcb->Mcb->FullName)); - ExAcquireSharedStarveExclusive(&Fcb->PagingIoResource, TRUE); - ExReleaseResourceLite(&Fcb->PagingIoResource); - CcFlushCache(&Fcb->SectionObject, NULL, 0, &IoStatus); - ClearFlag(Fcb->Flags, FCB_FILE_MODIFIED); - } - - if (Fcb->SectionObject.ImageSectionObject) { - DEBUG(DL_INF, ( "Ext2PurgeFile: MmFlushImageSection on %wZ.\n", - &Fcb->Mcb->FullName)); - MmFlushImageSection(&Fcb->SectionObject, MmFlushForWrite); - } - - if (Fcb->SectionObject.DataSectionObject) { - DEBUG(DL_INF, ( "Ext2PurgeFile: CcPurgeCacheSection on %wZ.\n", - &Fcb->Mcb->FullName)); - CcPurgeCacheSection(&Fcb->SectionObject, NULL, 0, FALSE); - } - - return STATUS_SUCCESS; -} - - -NTSTATUS -Ext2FileSystemControl (IN PEXT2_IRP_CONTEXT IrpContext) -{ - NTSTATUS Status; - - ASSERT(IrpContext); - - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - switch (IrpContext->MinorFunction) { - - case IRP_MN_USER_FS_REQUEST: - Status = Ext2UserFsRequest(IrpContext); - break; - - case IRP_MN_MOUNT_VOLUME: - Status = Ext2MountVolume(IrpContext); - break; - - case IRP_MN_VERIFY_VOLUME: - Status = Ext2VerifyVolume(IrpContext); - break; - - default: - - DEBUG(DL_ERR, ( "Ext2FilsSystemControl: Invalid Device Request.\n")); - Status = STATUS_INVALID_DEVICE_REQUEST; - Ext2CompleteIrpContext(IrpContext, Status); - } - - return Status; -} +/* + * COPYRIGHT: See COPYRIGHT.TXT + * PROJECT: Ext2 File System Driver for WinNT/2K/XP + * FILE: fsctl.c + * PROGRAMMER: Matt Wu + * HOMEPAGE: http://www.ext2fsd.com + * UPDATE HISTORY: + */ + +/* INCLUDES *****************************************************************/ + +#include "ext2fs.h" + +/* GLOBALS ***************************************************************/ + +extern PEXT2_GLOBAL Ext2Global; + +/* DEFINITIONS *************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, Ext2IsHandleCountZero) +#pragma alloc_text(PAGE, Ext2LockVcb) +#pragma alloc_text(PAGE, Ext2LockVolume) +#pragma alloc_text(PAGE, Ext2UnlockVcb) +#pragma alloc_text(PAGE, Ext2UnlockVolume) +#pragma alloc_text(PAGE, Ext2AllowExtendedDasdIo) +#pragma alloc_text(PAGE, Ext2GetRetrievalPointerBase) +#pragma alloc_text(PAGE, Ext2QueryExtentMappings) +#pragma alloc_text(PAGE, Ext2QueryRetrievalPointers) +#pragma alloc_text(PAGE, Ext2GetRetrievalPointers) +#pragma alloc_text(PAGE, Ext2UserFsRequest) +#pragma alloc_text(PAGE, Ext2IsMediaWriteProtected) +#pragma alloc_text(PAGE, Ext2MountVolume) +#pragma alloc_text(PAGE, Ext2PurgeVolume) +#pragma alloc_text(PAGE, Ext2PurgeFile) +#pragma alloc_text(PAGE, Ext2DismountVolume) +#pragma alloc_text(PAGE, Ext2IsVolumeMounted) +#pragma alloc_text(PAGE, Ext2VerifyVolume) +#pragma alloc_text(PAGE, Ext2FileSystemControl) +#endif + + +VOID +Ext2SetVpbFlag ( + IN PVPB Vpb, + IN USHORT Flag ) +{ + KIRQL OldIrql; + + IoAcquireVpbSpinLock(&OldIrql); + Vpb->Flags |= Flag; + IoReleaseVpbSpinLock(OldIrql); +} + +VOID +Ext2ClearVpbFlag ( + IN PVPB Vpb, + IN USHORT Flag ) +{ + KIRQL OldIrql; + + IoAcquireVpbSpinLock(&OldIrql); + Vpb->Flags &= ~Flag; + IoReleaseVpbSpinLock(OldIrql); +} + +BOOLEAN +Ext2IsHandleCountZero(IN PEXT2_VCB Vcb) +{ + PEXT2_FCB Fcb; + PLIST_ENTRY List; + + for ( List = Vcb->FcbList.Flink; + List != &Vcb->FcbList; + List = List->Flink ) { + + Fcb = CONTAINING_RECORD(List, EXT2_FCB, Next); + + ASSERT((Fcb->Identifier.Type == EXT2FCB) && + (Fcb->Identifier.Size == sizeof(EXT2_FCB))); + + DEBUG(DL_INF, ( "Ext2IsHandleCountZero: Inode:%xh File:%S OpenHandleCount=%xh\n", + Fcb->Inode->i_ino, Fcb->Mcb->ShortName.Buffer, Fcb->OpenHandleCount)); + + if (Fcb->OpenHandleCount) { + return FALSE; + } + } + + return TRUE; +} + +NTSTATUS +Ext2LockVcb (IN PEXT2_VCB Vcb, + IN PFILE_OBJECT FileObject) +{ + NTSTATUS Status = STATUS_SUCCESS; + + __try { + + if (FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED)) { + DEBUG(DL_INF, ( "Ext2LockVolume: Volume is already locked.\n")); + Status = STATUS_ACCESS_DENIED; + __leave; + } + + if (Vcb->OpenHandleCount > (ULONG)(FileObject ? 1 : 0)) { + DEBUG(DL_INF, ( "Ext2LockVcb: There are still opened files.\n")); + + Status = STATUS_ACCESS_DENIED; + __leave; + } + + if (!Ext2IsHandleCountZero(Vcb)) { + DEBUG(DL_INF, ( "Ext2LockVcb: Thare are still opened files.\n")); + + Status = STATUS_ACCESS_DENIED; + __leave; + } + + SetLongFlag(Vcb->Flags, VCB_VOLUME_LOCKED); + Ext2SetVpbFlag(Vcb->Vpb, VPB_LOCKED); + Vcb->LockFile = FileObject; + + DEBUG(DL_INF, ( "Ext2LockVcb: Volume locked.\n")); + + } __finally { + // Nothing + } + + return Status; +} + + +NTSTATUS +Ext2LockVolume (IN PEXT2_IRP_CONTEXT IrpContext) +{ + PIO_STACK_LOCATION IrpSp; + PDEVICE_OBJECT DeviceObject; + PEXT2_VCB Vcb = NULL; + NTSTATUS Status; + BOOLEAN VcbResourceAcquired = FALSE; + + __try { + + ASSERT(IrpContext != NULL); + + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + DeviceObject = IrpContext->DeviceObject; + + Status = STATUS_UNSUCCESSFUL; + + // + // This request is not allowed on the main device object + // + if (IsExt2FsDevice(DeviceObject)) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + + Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; + + ASSERT(Vcb != NULL); + + ASSERT((Vcb->Identifier.Type == EXT2VCB) && + (Vcb->Identifier.Size == sizeof(EXT2_VCB))); + + ASSERT(IsMounted(Vcb)); + + IrpSp = IoGetCurrentIrpStackLocation(IrpContext->Irp); + +#if (_WIN32_WINNT >= 0x0500) + CcWaitForCurrentLazyWriterActivity(); +#endif + ExAcquireResourceExclusiveLite( + &Vcb->MainResource, + TRUE ); + + VcbResourceAcquired = TRUE; + + /* flush dirty data before locking the volume */ + if (!IsVcbReadOnly(Vcb)) { + Ext2FlushFiles(IrpContext, Vcb, FALSE); + Ext2FlushVolume(IrpContext, Vcb, FALSE); + } + + Status = Ext2LockVcb(Vcb, IrpSp->FileObject); + + } __finally { + + if (VcbResourceAcquired) { + ExReleaseResourceLite(&Vcb->MainResource); + } + + if (!IrpContext->ExceptionInProgress) { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + + return Status; +} + +NTSTATUS +Ext2UnlockVcb ( IN PEXT2_VCB Vcb, + IN PFILE_OBJECT FileObject ) +{ + NTSTATUS Status; + + __try { + + if (FileObject && FileObject->FsContext != Vcb) { + Status = STATUS_NOT_LOCKED; + __leave; + } + + if (!FlagOn(Vcb->Flags, VCB_VOLUME_LOCKED)) { + DEBUG(DL_ERR, ( ": Ext2UnlockVcb: Volume is not locked.\n")); + Status = STATUS_NOT_LOCKED; + __leave; + } + + if (Vcb->LockFile == FileObject) { + ClearFlag(Vcb->Flags, VCB_VOLUME_LOCKED); + Ext2ClearVpbFlag(Vcb->Vpb, VPB_LOCKED); + DEBUG(DL_INF, ( "Ext2UnlockVcb: Volume unlocked.\n")); + Status = STATUS_SUCCESS; + } else { + Status = STATUS_NOT_LOCKED; + } + + } __finally { + // Nothing + } + + return Status; +} + +NTSTATUS +Ext2UnlockVolume ( + IN PEXT2_IRP_CONTEXT IrpContext +) +{ + PIO_STACK_LOCATION IrpSp = NULL; + PDEVICE_OBJECT DeviceObject = NULL; + PEXT2_VCB Vcb = NULL; + NTSTATUS Status; + BOOLEAN VcbResourceAcquired = FALSE; + + __try { + + ASSERT(IrpContext != NULL); + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + DeviceObject = IrpContext->DeviceObject; + IrpSp = IoGetCurrentIrpStackLocation(IrpContext->Irp); + + // + // This request is not allowed on the main device object + // + if (IsExt2FsDevice(DeviceObject)) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + + Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; + ASSERT(Vcb != NULL); + ASSERT((Vcb->Identifier.Type == EXT2VCB) && + (Vcb->Identifier.Size == sizeof(EXT2_VCB))); + + ExAcquireResourceExclusiveLite( + &Vcb->MainResource, + TRUE ); + VcbResourceAcquired = TRUE; + + Status = Ext2UnlockVcb(Vcb, IrpSp->FileObject); + + } __finally { + + if (VcbResourceAcquired) { + ExReleaseResourceLite(&Vcb->MainResource); + } + + if (!IrpContext->ExceptionInProgress) { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + + return Status; +} + + +NTSTATUS +Ext2InvalidateVolumes ( IN PEXT2_IRP_CONTEXT IrpContext ) +{ + NTSTATUS Status; + PIRP Irp; + PIO_STACK_LOCATION IrpSp; + + PVPB NewVpb = NULL; + HANDLE Handle; + PLIST_ENTRY ListEntry; + + ULONG InputLength = 0; + PFILE_OBJECT FileObject; + PDEVICE_OBJECT DeviceObject; + BOOLEAN GlobalResourceAcquired = FALSE; + + LUID Privilege = {SE_TCB_PRIVILEGE, 0}; + + __try { + + Irp = IrpContext->Irp; + IrpSp = IoGetCurrentIrpStackLocation(Irp); + + if (!IsExt2FsDevice(IrpSp->DeviceObject)) { + Status = STATUS_INVALID_DEVICE_REQUEST; + __leave; + } + + if (!SeSinglePrivilegeCheck(Privilege, Irp->RequestorMode)) { + Status = STATUS_PRIVILEGE_NOT_HELD; + __leave; + } + + +#ifndef _GNU_NTIFS_ + InputLength = IrpSp->Parameters.FileSystemControl.InputBufferLength; +#else + InputLength = ((PEXTENDED_IO_STACK_LOCATION)(IrpSp))-> + Parameters.FileSystemControl.InputBufferLength; +#endif + +#if defined(_WIN64) + if (IoIs32bitProcess(Irp)) { + if (InputLength != sizeof(UINT32)) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + Handle = (HANDLE) LongToHandle( (*(PUINT32)Irp->AssociatedIrp.SystemBuffer) ); + } else +#endif + { + if (InputLength != sizeof(HANDLE)) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + Handle = *(PHANDLE)Irp->AssociatedIrp.SystemBuffer; + } + + Status = ObReferenceObjectByHandle( Handle, + 0, + *IoFileObjectType, + KernelMode, + &FileObject, + NULL ); + + if (!NT_SUCCESS(Status)) { + __leave; + } else { + DeviceObject = FileObject->DeviceObject; + ObDereferenceObject(FileObject); + } + + ExAcquireResourceExclusiveLite(&Ext2Global->Resource, TRUE); + GlobalResourceAcquired = TRUE; + + ListEntry = Ext2Global->VcbList.Flink; + while (ListEntry != &Ext2Global->VcbList) { + + PEXT2_VCB Vcb = CONTAINING_RECORD(ListEntry, EXT2_VCB, Next); + ListEntry = ListEntry->Flink; + + DEBUG(DL_DBG, ( "Ext2InvalidateVolumes: Vcb=%xh Vcb->Vpb=%xh " + "Blink = %p &Vcb->Next = %p\n", + Vcb, Vcb->Vpb, ListEntry->Blink, &Vcb->Next)); + + if (Vcb->Vpb && (Vcb->Vpb->RealDevice == DeviceObject)) { + + DEBUG(DL_DBG, ( "Ext2InvalidateVolumes: Got Vcb=%xh Vcb->Vpb=%xh " + "Blink = %p &Vcb->Next = %p\n", + Vcb, Vcb->Vpb, ListEntry->Blink, &Vcb->Next)); + /* dismount the volume */ + Ext2CheckDismount(IrpContext, Vcb, FALSE); + } + } + + } __finally { + + if (GlobalResourceAcquired) { + ExReleaseResourceLite(&Ext2Global->Resource); + } + + if (!IrpContext->ExceptionInProgress) { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + + return Status; +} + +NTSTATUS +Ext2AllowExtendedDasdIo(IN PEXT2_IRP_CONTEXT IrpContext) +{ + PIO_STACK_LOCATION IrpSp; + PEXT2_VCB Vcb; + PEXT2_CCB Ccb; + NTSTATUS status; + + IrpSp = IoGetCurrentIrpStackLocation(IrpContext->Irp); + + Vcb = (PEXT2_VCB) IrpSp->FileObject->FsContext; + Ccb = (PEXT2_CCB) IrpSp->FileObject->FsContext2; + + ASSERT(Vcb != NULL); + + ASSERT((Vcb->Identifier.Type == EXT2VCB) && + (Vcb->Identifier.Size == sizeof(EXT2_VCB))); + + ASSERT(IsMounted(Vcb)); + + if (Ccb) { + SetLongFlag(Ccb->Flags, CCB_ALLOW_EXTENDED_DASD_IO); + status = STATUS_SUCCESS; + } else { + status = STATUS_INVALID_PARAMETER; + } + + Ext2CompleteIrpContext(IrpContext, status); + return status; +} + +/* + * Ext2OplockRequest + * + * oplock requests handler routine + * + * Arguments: + * IrpContext: the ext2 irp context + * + * Return Value: + * NTSTATUS: The return status for the operation + * + */ + +NTSTATUS +Ext2OplockRequest ( + IN PEXT2_IRP_CONTEXT IrpContext +) +{ + NTSTATUS Status; + + ULONG FsCtrlCode; + PDEVICE_OBJECT DeviceObject; + PFILE_OBJECT FileObject; + + PIRP Irp = NULL; + PIO_STACK_LOCATION IrpSp; + PEXTENDED_IO_STACK_LOCATION EIrpSp; + + PEXT2_VCB Vcb = NULL; + PEXT2_FCB Fcb = NULL; + PEXT2_CCB Ccb = NULL; + + ULONG OplockCount = 0; + + BOOLEAN VcbResourceAcquired = FALSE; + BOOLEAN FcbResourceAcquired = FALSE; + + ASSERT(IrpContext); + + __try { + + Irp = IrpContext->Irp; + ASSERT(Irp); + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + ASSERT(IrpSp); + EIrpSp = (PEXTENDED_IO_STACK_LOCATION)IrpSp; + + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + DeviceObject = IrpContext->DeviceObject; + + // + // This request is not allowed on the main device object + // + if (IsExt2FsDevice(DeviceObject)) { + Status = STATUS_INVALID_DEVICE_REQUEST; + __leave; + } + + Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; + + ASSERT(Vcb != NULL); + + ASSERT((Vcb->Identifier.Type == EXT2VCB) && + (Vcb->Identifier.Size == sizeof(EXT2_VCB))); + + ASSERT(IsMounted(Vcb)); + + FileObject = IrpContext->FileObject; + + Fcb = (PEXT2_FCB) FileObject->FsContext; + + // + // This request is not allowed on volumes + // + + if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + + ASSERT((Fcb->Identifier.Type == EXT2FCB) && + (Fcb->Identifier.Size == sizeof(EXT2_FCB))); + + if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { + Status = STATUS_FILE_DELETED; + __leave; + } + + Ccb = (PEXT2_CCB) FileObject->FsContext2; + if (Ccb == NULL) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + + + ASSERT((Ccb->Identifier.Type == EXT2CCB) && + (Ccb->Identifier.Size == sizeof(EXT2_CCB))); + + FsCtrlCode = EIrpSp->Parameters.FileSystemControl.FsControlCode; + + switch (FsCtrlCode) { + + case FSCTL_REQUEST_OPLOCK_LEVEL_1: + case FSCTL_REQUEST_OPLOCK_LEVEL_2: + case FSCTL_REQUEST_BATCH_OPLOCK: + + VcbResourceAcquired = + ExAcquireResourceSharedLite( + &Vcb->MainResource, + IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT) ); + + ClearFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + FcbResourceAcquired = + ExAcquireResourceExclusiveLite ( + &Fcb->MainResource, + IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); + + if (FsCtrlCode == FSCTL_REQUEST_OPLOCK_LEVEL_2) { + OplockCount = (ULONG) FsRtlAreThereCurrentFileLocks(&Fcb->FileLockAnchor); + } else { + OplockCount = Fcb->OpenHandleCount; + } + + break; + + case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: + case FSCTL_OPBATCH_ACK_CLOSE_PENDING : + case FSCTL_OPLOCK_BREAK_NOTIFY: + case FSCTL_OPLOCK_BREAK_ACK_NO_2: + + FcbResourceAcquired = + ExAcquireResourceSharedLite ( + &Fcb->MainResource, + IsFlagOn(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT)); + + break; + + default: + + Ext2BugCheck(EXT2_BUGCHK_FSCTL, FsCtrlCode, 0, 0); + } + + + // + // Call the FsRtl routine to grant/acknowledge oplock. + // + + Status = FsRtlOplockFsctrl( &Fcb->Oplock, + Irp, + OplockCount ); + + // + // Set the flag indicating if Fast I/O is possible + // + + Fcb->Header.IsFastIoPossible = Ext2IsFastIoPossible(Fcb); + IrpContext->Irp = NULL; + + } __finally { + + if (FcbResourceAcquired) { + ExReleaseResourceLite(&Fcb->MainResource); + } + + if (VcbResourceAcquired) { + ExReleaseResourceLite(&Vcb->MainResource); + } + + if (!AbnormalTermination()) { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + + return Status; +} + +NTSTATUS +Ext2IsVolumeDirty ( + IN PEXT2_IRP_CONTEXT IrpContext +) +{ + NTSTATUS status = STATUS_SUCCESS; + PIRP Irp; + PEXTENDED_IO_STACK_LOCATION IrpSp; + PULONG VolumeState; + + __try { + + Irp = IrpContext->Irp; + IrpSp = (PEXTENDED_IO_STACK_LOCATION)IoGetCurrentIrpStackLocation(Irp); + + // + // Get a pointer to the output buffer. Look at the system buffer field in th + // irp first. Then the Irp Mdl. + // + + if (Irp->AssociatedIrp.SystemBuffer != NULL) { + + VolumeState = Irp->AssociatedIrp.SystemBuffer; + + } else if (Irp->MdlAddress != NULL) { + + VolumeState = MmGetSystemAddressForMdl( Irp->MdlAddress ); + + } else { + + status = STATUS_INVALID_USER_BUFFER; + __leave; + } + + if (IrpSp->Parameters.FileSystemControl.OutputBufferLength < sizeof(ULONG)) { + status = STATUS_INVALID_PARAMETER; + __leave; + } + + *VolumeState = 0; + + } __finally { + + if (!IrpContext->ExceptionInProgress) { + Ext2CompleteIrpContext(IrpContext, status); + } + } + + return status; +} + + +NTSTATUS +Ext2QueryExtentMappings( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_FCB Fcb, + IN PLARGE_INTEGER RequestVbn, + OUT PLARGE_INTEGER * pMappedRuns +) +{ + PLARGE_INTEGER MappedRuns = NULL; + PLARGE_INTEGER PartialRuns = NULL; + + PEXT2_EXTENT Chain = NULL; + PEXT2_EXTENT Extent = NULL; + + LONGLONG Vbn = 0; + ULONG Length = 0; + ULONG i = 0; + + NTSTATUS Status = STATUS_SUCCESS; + + __try { + + /* now building all the request extents */ + while (Vbn < RequestVbn->QuadPart) { + + Length = 0x80000000; /* 2g bytes */ + if (RequestVbn->QuadPart < Vbn + Length) { + Length = (ULONG)(RequestVbn->QuadPart - Vbn); + } + + /* build extents for sub-range */ + Extent = NULL; + Status = Ext2BuildExtents( + IrpContext, + Vcb, + Fcb->Mcb, + Vbn, + Length, + FALSE, + &Extent); + + if (!NT_SUCCESS(Status)) { + __leave; + } + + if (Chain) { + Ext2JointExtents(Chain, Extent); + } else { + Chain = Extent; + } + + /* allocate extent array */ + PartialRuns = Ext2AllocatePool( + NonPagedPool, + (Ext2CountExtents(Chain) + 2) * + (2 * sizeof(LARGE_INTEGER)), + 'RE2E'); + + if (PartialRuns == NULL) { + Status = STATUS_INSUFFICIENT_RESOURCES; + __leave; + } + RtlZeroMemory( PartialRuns, + (Ext2CountExtents(Chain) + 2) * + (2 * sizeof(LARGE_INTEGER))); + + if (MappedRuns) { + RtlMoveMemory(PartialRuns, + MappedRuns, + i * 2 * sizeof(LARGE_INTEGER)); + Ext2FreePool(MappedRuns, 'RE2E'); + } + MappedRuns = PartialRuns; + + /* walk all the Mcb runs in Extent */ + for (; Extent != NULL; Extent = Extent->Next) { + MappedRuns[i*2 + 0].QuadPart = Vbn + Extent->Offset; + MappedRuns[i*2 + 1].QuadPart = Extent->Lba; + i = i+1; + } + + Vbn = Vbn + Length; + } + + *pMappedRuns = MappedRuns; + + } __finally { + + if (!NT_SUCCESS(Status) || Status == STATUS_PENDING) { + if (MappedRuns) { + Ext2FreePool(MappedRuns, 'RE2E'); + } + *pMappedRuns = NULL; + } + + if (Chain) { + Ext2DestroyExtentChain(Chain); + } + } + + return Status; +} + +NTSTATUS +Ext2QueryRetrievalPointers ( + IN PEXT2_IRP_CONTEXT IrpContext +) +{ + PIRP Irp = NULL; + PIO_STACK_LOCATION IrpSp; + PEXTENDED_IO_STACK_LOCATION EIrpSp; + + PDEVICE_OBJECT DeviceObject; + PFILE_OBJECT FileObject; + + PEXT2_VCB Vcb = NULL; + PEXT2_FCB Fcb = NULL; + PEXT2_CCB Ccb = NULL; + + PLARGE_INTEGER RequestVbn; + PLARGE_INTEGER * pMappedRuns; + + ULONG InputSize; + ULONG OutputSize; + + NTSTATUS Status = STATUS_SUCCESS; + + BOOLEAN FcbResourceAcquired = FALSE; + + __try { + + ASSERT(IrpContext); + Irp = IrpContext->Irp; + ASSERT(Irp); + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + EIrpSp = (PEXTENDED_IO_STACK_LOCATION)IrpSp; + ASSERT(IrpSp); + + InputSize = EIrpSp->Parameters.FileSystemControl.InputBufferLength; + OutputSize = EIrpSp->Parameters.FileSystemControl.OutputBufferLength; + + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + DeviceObject = IrpContext->DeviceObject; + + DbgBreak(); + + /* This request is not allowed on the main device object */ + if (IsExt2FsDevice(DeviceObject)) { + Status = STATUS_INVALID_DEVICE_REQUEST; + __leave; + } + + Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; + ASSERT(Vcb != NULL); + ASSERT((Vcb->Identifier.Type == EXT2VCB) && + (Vcb->Identifier.Size == sizeof(EXT2_VCB))); + ASSERT(IsMounted(Vcb)); + + FileObject = IrpContext->FileObject; + Fcb = (PEXT2_FCB) FileObject->FsContext; + + /* check Fcb is valid or not */ + if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + + ASSERT((Fcb->Identifier.Type == EXT2FCB) && + (Fcb->Identifier.Size == sizeof(EXT2_FCB))); + if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { + Status = STATUS_FILE_DELETED; + __leave; + } + + Ccb = (PEXT2_CCB) FileObject->FsContext2; + if (Ccb == NULL) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + + ASSERT((Ccb->Identifier.Type == EXT2CCB) && + (Ccb->Identifier.Size == sizeof(EXT2_CCB))); + + /* Is requstor in kernel and Fcb a paging file ? */ + if (Irp->RequestorMode != KernelMode || + !IsFlagOn(Fcb->Flags, FCB_PAGE_FILE) || + InputSize != sizeof(LARGE_INTEGER) || + OutputSize != sizeof(PVOID)) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + + if (!ExAcquireResourceExclusiveLite ( + &Fcb->MainResource, Ext2CanIWait())) { + Status = STATUS_PENDING; + __leave; + } + FcbResourceAcquired = TRUE; + + RequestVbn = EIrpSp->Parameters.FileSystemControl.Type3InputBuffer; + pMappedRuns = Irp->UserBuffer; + + DbgBreak(); + + /* request size beyonds whole file size */ + if (RequestVbn->QuadPart >= Fcb->Header.AllocationSize.QuadPart) { + Status = STATUS_END_OF_FILE; + __leave; + } + + Status = Ext2QueryExtentMappings( + IrpContext, + Vcb, + Fcb, + RequestVbn, + pMappedRuns + ); + + } __finally { + + if (FcbResourceAcquired) { + ExReleaseResourceLite(&Fcb->MainResource); + } + + if (!AbnormalTermination()) { + if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { + Status = Ext2QueueRequest(IrpContext); + } else { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + } + + return Status; +} + + +NTSTATUS +Ext2GetRetrievalPointers ( + IN PEXT2_IRP_CONTEXT IrpContext +) +{ + PIRP Irp = NULL; + PIO_STACK_LOCATION IrpSp; + PEXTENDED_IO_STACK_LOCATION EIrpSp; + + PDEVICE_OBJECT DeviceObject; + PFILE_OBJECT FileObject; + + PEXT2_VCB Vcb = NULL; + PEXT2_FCB Fcb = NULL; + PEXT2_CCB Ccb = NULL; + + PSTARTING_VCN_INPUT_BUFFER SVIB; + PRETRIEVAL_POINTERS_BUFFER RPSB; + + PEXT2_EXTENT Chain = NULL; + PEXT2_EXTENT Extent = NULL; + + LONGLONG Vbn = 0; + ULONG Length = 0; + ULONG i = 0; + + ULONG UsedSize = 0; + ULONG InputSize; + ULONG OutputSize; + + NTSTATUS Status = STATUS_SUCCESS; + + BOOLEAN FcbResourceAcquired = FALSE; + + __try { + + ASSERT(IrpContext); + Irp = IrpContext->Irp; + ASSERT(Irp); + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + EIrpSp = (PEXTENDED_IO_STACK_LOCATION)IrpSp; + ASSERT(IrpSp); + + InputSize = EIrpSp->Parameters.FileSystemControl.InputBufferLength; + OutputSize = EIrpSp->Parameters.FileSystemControl.OutputBufferLength; + + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + DeviceObject = IrpContext->DeviceObject; + + /* This request is not allowed on the main device object */ + if (IsExt2FsDevice(DeviceObject)) { + Status = STATUS_INVALID_DEVICE_REQUEST; + __leave; + } + + Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; + ASSERT(Vcb != NULL); + ASSERT((Vcb->Identifier.Type == EXT2VCB) && + (Vcb->Identifier.Size == sizeof(EXT2_VCB))); + ASSERT(IsMounted(Vcb)); + + FileObject = IrpContext->FileObject; + Fcb = (PEXT2_FCB) FileObject->FsContext; + + /* check Fcb is valid or not */ + if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + + ASSERT((Fcb->Identifier.Type == EXT2FCB) && + (Fcb->Identifier.Size == sizeof(EXT2_FCB))); + + if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { + Status = STATUS_FILE_DELETED; + __leave; + } + + Ccb = (PEXT2_CCB) FileObject->FsContext2; + if (Ccb == NULL) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + + ASSERT((Ccb->Identifier.Type == EXT2CCB) && + (Ccb->Identifier.Size == sizeof(EXT2_CCB))); + + if (InputSize < sizeof(STARTING_VCN_INPUT_BUFFER) || + OutputSize < sizeof(RETRIEVAL_POINTERS_BUFFER) ) { + Status = STATUS_BUFFER_TOO_SMALL; + __leave; + } + + if (!ExAcquireResourceExclusiveLite ( + &Fcb->MainResource, Ext2CanIWait())) { + Status = STATUS_PENDING; + __leave; + } + FcbResourceAcquired = TRUE; + + SVIB = (PSTARTING_VCN_INPUT_BUFFER) + EIrpSp->Parameters.FileSystemControl.Type3InputBuffer; + RPSB = (PRETRIEVAL_POINTERS_BUFFER) Ext2GetUserBuffer(Irp); + + /* probe user buffer */ + + __try { + if (Irp->RequestorMode != KernelMode) { + ProbeForRead (SVIB, InputSize, sizeof(UCHAR)); + ProbeForWrite(RPSB, OutputSize, sizeof(UCHAR)); + } + } __except(EXCEPTION_EXECUTE_HANDLER) { + Status = STATUS_INVALID_USER_BUFFER; + } + + if (!NT_SUCCESS(Status)) { + __leave; + } + + UsedSize = FIELD_OFFSET(RETRIEVAL_POINTERS_BUFFER, Extents[0]); + + /* request size beyonds whole file size ? */ + DEBUG(DL_DBG, ("Ext2GetRetrievalPointers: Startin from Vbn: %I64xh\n", + SVIB->StartingVcn.QuadPart)); + Vbn = (SVIB->StartingVcn.QuadPart << BLOCK_BITS); + if (Vbn >= Fcb->Header.AllocationSize.QuadPart ) { + Status = STATUS_END_OF_FILE; + __leave; + } + + /* now building all the request extents */ + while (Vbn < Fcb->Header.AllocationSize.QuadPart) { + + ASSERT(Chain == NULL); + Length = 0x80000000; /* 2g bytes */ + if (Fcb->Header.AllocationSize.QuadPart < Vbn + Length) { + Length = (ULONG)(Fcb->Header.AllocationSize.QuadPart - Vbn); + } + + /* build extents for sub-range */ + Status = Ext2BuildExtents( + IrpContext, + Vcb, + Fcb->Mcb, + Vbn, + Length, + FALSE, + &Chain); + + if (!NT_SUCCESS(Status)) { + DbgBreak(); + __leave; + } + + /* fill user buffer of RETRIEVAL_POINTERS_BUFFER */ + Extent = Chain; + while (Extent) { + + DEBUG(DL_MAP, ("Ext2GetRetrievalPointers: %wZ %d Vbn = %I64xh Lbn = %I64xh\n", + &Fcb->Mcb->FullName, i, + ((Vbn + Extent->Offset) >> BLOCK_BITS), + Extent->Lba)); + + RPSB->Extents[i].Lcn.QuadPart = (Extent->Lba >> BLOCK_BITS); + RPSB->Extents[i].NextVcn.QuadPart = ((Vbn + Extent->Offset + Extent->Length) >> BLOCK_BITS); + if (i == 0) { + RPSB->StartingVcn.QuadPart = ((Vbn + Extent->Offset) >> BLOCK_BITS); + } else { + ASSERT(RPSB->Extents[i-1].NextVcn.QuadPart == ((Vbn + Extent->Offset) >> BLOCK_BITS)); + } + if (UsedSize + sizeof(RETRIEVAL_POINTERS_BUFFER) > OutputSize) { + Status = STATUS_BUFFER_OVERFLOW; + __leave; + } + UsedSize += sizeof(LARGE_INTEGER) * 2; + Irp->IoStatus.Information = (ULONG_PTR)UsedSize; + RPSB->ExtentCount = ++i; + Extent = Extent->Next; + } + + if (Chain) { + Ext2DestroyExtentChain(Chain); + Chain = NULL; + } + + Vbn = Vbn + Length; + } + +#if 0 + { + NTSTATUS _s; + ULONG _i = 0; + LARGE_INTEGER RequestVbn = Fcb->Header.AllocationSize; + PLARGE_INTEGER MappedRuns = NULL; + + _s = Ext2QueryExtentMappings( + IrpContext, + Vcb, + Fcb, + &RequestVbn, + &MappedRuns + ); + if (!NT_SUCCESS(_s) || NULL == MappedRuns) { + DbgBreak(); + goto exit_to_get_rps; + } + + while (MappedRuns[_i*2 + 0].QuadPart != 0 || + MappedRuns[_i*2 + 1].QuadPart != 0 ) { + DEBUG(DL_MAP, ("Ext2QueryExtentMappings: %wZ %d Vbn = %I64xh Lbn = %I64xh\n", + &Fcb->Mcb->FullName, _i, + MappedRuns[_i*2 + 0].QuadPart, + MappedRuns[_i*2 + 1].QuadPart)); + _i++; + } + +exit_to_get_rps: + + if (MappedRuns) { + Ext2FreePool(MappedRuns, 'RE2E'); + } + } +#endif + + } __finally { + + if (FcbResourceAcquired) { + ExReleaseResourceLite(&Fcb->MainResource); + } + + if (Chain) { + Ext2DestroyExtentChain(Chain); + } + + if (!AbnormalTermination()) { + if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { + Status = Ext2QueueRequest(IrpContext); + } else { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + } + + return Status; +} + +NTSTATUS +Ext2GetRetrievalPointerBase ( + IN PEXT2_IRP_CONTEXT IrpContext +) +{ + PIRP Irp = NULL; + PIO_STACK_LOCATION IrpSp; + PEXTENDED_IO_STACK_LOCATION EIrpSp; + + PDEVICE_OBJECT DeviceObject; + PFILE_OBJECT FileObject; + + PEXT2_VCB Vcb = NULL; + PEXT2_FCB Fcb = NULL; + PEXT2_CCB Ccb = NULL; + + PLARGE_INTEGER FileAreaOffset; + + ULONG OutputSize; + + NTSTATUS Status = STATUS_SUCCESS; + + BOOLEAN FcbResourceAcquired = FALSE; + + __try { + + ASSERT(IrpContext); + Irp = IrpContext->Irp; + ASSERT(Irp); + + IrpSp = IoGetCurrentIrpStackLocation(Irp); + EIrpSp = (PEXTENDED_IO_STACK_LOCATION)IrpSp; + ASSERT(IrpSp); + + OutputSize = EIrpSp->Parameters.FileSystemControl.OutputBufferLength; + + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + DeviceObject = IrpContext->DeviceObject; + + /* This request is not allowed on the main device object */ + if (IsExt2FsDevice(DeviceObject)) { + Status = STATUS_INVALID_DEVICE_REQUEST; + __leave; + } + + Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; + ASSERT(Vcb != NULL); + ASSERT((Vcb->Identifier.Type == EXT2VCB) && + (Vcb->Identifier.Size == sizeof(EXT2_VCB))); + ASSERT(IsMounted(Vcb)); + + FileObject = IrpContext->FileObject; + Fcb = (PEXT2_FCB) FileObject->FsContext; + + /* check Fcb is valid or not */ + if (Fcb == NULL || Fcb->Identifier.Type == EXT2VCB) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + + ASSERT((Fcb->Identifier.Type == EXT2FCB) && + (Fcb->Identifier.Size == sizeof(EXT2_FCB))); + + if (IsFlagOn(Fcb->Mcb->Flags, MCB_FILE_DELETED)) { + Status = STATUS_FILE_DELETED; + __leave; + } + + Ccb = (PEXT2_CCB) FileObject->FsContext2; + if (Ccb == NULL) { + Status = STATUS_INVALID_PARAMETER; + __leave; + } + + ASSERT((Ccb->Identifier.Type == EXT2CCB) && + (Ccb->Identifier.Size == sizeof(EXT2_CCB))); + + if (OutputSize < sizeof(LARGE_INTEGER)) { + Status = STATUS_BUFFER_TOO_SMALL; + __leave; + } + + if (!ExAcquireResourceExclusiveLite ( + &Fcb->MainResource, Ext2CanIWait())) { + Status = STATUS_PENDING; + __leave; + } + FcbResourceAcquired = TRUE; + + FileAreaOffset = (PLARGE_INTEGER) Ext2GetUserBuffer(Irp); + + /* probe user buffer */ + + __try { + if (Irp->RequestorMode != KernelMode) { + ProbeForWrite(FileAreaOffset, OutputSize, sizeof(UCHAR)); + } + + } __except(EXCEPTION_EXECUTE_HANDLER) { + + Status = STATUS_INVALID_USER_BUFFER; + } + + if (!NT_SUCCESS(Status)) { + __leave; + } + + DEBUG(DL_DBG, ("Ext2GetRetrievalPointerBase: FileAreaOffset is 0.\n")); + + FileAreaOffset->QuadPart = 0; // sector offset to the first allocatable unit on the filesystem + + Irp->IoStatus.Information = sizeof(LARGE_INTEGER); + + } __finally { + + if (FcbResourceAcquired) { + ExReleaseResourceLite(&Fcb->MainResource); + } + + if (!AbnormalTermination()) { + if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { + Status = Ext2QueueRequest(IrpContext); + } else { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + } + + 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) || + !IsFlagOn(Ccb->Flags, CCB_OPEN_REPARSE_POINT)) { + 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; + + 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; + + BOOLEAN MainResourceAcquired = FALSE; + BOOLEAN FcbLockAcquired = FALSE; + + __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); + + ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); + FcbLockAcquired = TRUE; + + ParentMcb = Mcb->Parent; + ParentDcb = ParentMcb->Fcb; + if (ParentDcb == NULL) { + ParentDcb = Ext2AllocateFcb(Vcb, ParentMcb); + } + if (ParentDcb) { + Ext2ReferXcb(&ParentDcb->ReferenceCount); + } + + if (!Mcb) + __leave; + + if (FcbLockAcquired) { + ExReleaseResourceLite(&Vcb->FcbLock); + FcbLockAcquired = FALSE; + } + + 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] = '/'; + } + } + + /* free all data blocks of the inode (to be set as symlink) */ + { + LARGE_INTEGER zero = {0}; + Status = Ext2TruncateFile(IrpContext, Vcb, Mcb, &zero); + } + + /* decrease dir count of group desc and vcb stat */ + if (S_ISDIR(Mcb->Inode.i_mode)) { + + ULONG group = (Mcb->Inode.i_ino - 1) / INODES_PER_GROUP; + Ext2UpdateGroupDirStat(IrpContext, Vcb, group); + + /* drop extra reference for dir inode */ + ext3_dec_count(&Mcb->Inode); + } + + /* overwrite inode mode as type SYMLINK */ + Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); + SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_REPARSE_POINT); + + Status = Ext2WriteSymlink(IrpContext, Vcb, Mcb, OemNameBuffer, + OemNameLength, &BytesWritten); + if (NT_SUCCESS(Status)) { + Ext2SetFileType(IrpContext, Vcb, ParentDcb, Mcb, + S_IFLNK | S_IRWXUGO); + } + + } __finally { + + if (FcbLockAcquired) { + ExReleaseResourceLite(&Vcb->FcbLock); + FcbLockAcquired = FALSE; + } + + 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 (!AbnormalTermination()) { + if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { + Status = Ext2QueueRequest(IrpContext); + } else { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + + if (ParentDcb) { + Ext2ReleaseFcb(ParentDcb); + } + } + + 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; + + PEXT2_FCB ParentDcb = NULL; /* Dcb of it's current parent */ + PEXT2_MCB ParentMcb = NULL; + + NTSTATUS Status = STATUS_UNSUCCESSFUL; + + BOOLEAN FcbLockAcquired = FALSE; + BOOLEAN MainResourceAcquired = FALSE; + + + __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; + + ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); + FcbLockAcquired = TRUE; + + ParentMcb = Mcb->Parent; + ParentDcb = ParentMcb->Fcb; + if (ParentDcb == NULL) { + ParentDcb = Ext2AllocateFcb(Vcb, ParentMcb); + } + if (ParentDcb) { + Ext2ReferXcb(&ParentDcb->ReferenceCount); + } + + if (!Mcb || !IsInodeSymLink(&Mcb->Inode) || + !IsFlagOn(Ccb->Flags, CCB_OPEN_REPARSE_POINT)) { + Status = STATUS_NOT_A_REPARSE_POINT; + __leave; + } + + Fcb = Ext2AllocateFcb (Vcb, Mcb); + if (Fcb) { + Ext2ReferXcb(&Fcb->ReferenceCount); + } else { + Status = STATUS_INSUFFICIENT_RESOURCES; + __leave; + } + + if (FcbLockAcquired) { + ExReleaseResourceLite(&Vcb->FcbLock); + FcbLockAcquired = FALSE; + } + + 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; + } + + /* inode is to be removed */ + SetFlag(Ccb->Flags, CCB_DELETE_ON_CLOSE); + + } __finally { + + if (FcbLockAcquired) { + ExReleaseResourceLite(&Vcb->FcbLock); + } + + if (MainResourceAcquired) { + ExReleaseResourceLite(&Fcb->MainResource); + } + + if (NT_SUCCESS(Status)) { + Ext2NotifyReportChange( + IrpContext, + Vcb, + Mcb, + FILE_NOTIFY_CHANGE_ATTRIBUTES, + FILE_ACTION_MODIFIED ); + + } + + if (!AbnormalTermination()) { + if (Status == STATUS_PENDING || Status == STATUS_CANT_WAIT) { + Status = Ext2QueueRequest(IrpContext); + } else { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + + if (ParentDcb) { + Ext2ReleaseFcb(ParentDcb); + } + + if (Fcb) { + Ext2ReleaseFcb(Fcb); + } + } + + return Status; +} + +NTSTATUS +Ext2UserFsRequest (IN PEXT2_IRP_CONTEXT IrpContext) +{ + PIRP Irp; + PIO_STACK_LOCATION IoStackLocation; + ULONG FsControlCode; + NTSTATUS Status; + + ASSERT(IrpContext); + + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + Irp = IrpContext->Irp; + IoStackLocation = IoGetCurrentIrpStackLocation(Irp); + +#ifndef _GNU_NTIFS_ + FsControlCode = + IoStackLocation->Parameters.FileSystemControl.FsControlCode; +#else + FsControlCode = ((PEXTENDED_IO_STACK_LOCATION) + IoStackLocation)->Parameters.FileSystemControl.FsControlCode; +#endif + + 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; + + case FSCTL_UNLOCK_VOLUME: + Status = Ext2UnlockVolume(IrpContext); + break; + + case FSCTL_DISMOUNT_VOLUME: + Status = Ext2DismountVolume(IrpContext); + break; + + case FSCTL_IS_VOLUME_MOUNTED: + Status = Ext2IsVolumeMounted(IrpContext); + break; + + case FSCTL_INVALIDATE_VOLUMES: + Status = Ext2InvalidateVolumes(IrpContext); + break; + +#if (_WIN32_WINNT >= 0x0500) + case FSCTL_ALLOW_EXTENDED_DASD_IO: + Status = Ext2AllowExtendedDasdIo(IrpContext); + break; +#endif //(_WIN32_WINNT >= 0x0500) + + case FSCTL_REQUEST_OPLOCK_LEVEL_1: + case FSCTL_REQUEST_OPLOCK_LEVEL_2: + case FSCTL_REQUEST_BATCH_OPLOCK: + case FSCTL_OPLOCK_BREAK_ACKNOWLEDGE: + case FSCTL_OPBATCH_ACK_CLOSE_PENDING: + case FSCTL_OPLOCK_BREAK_NOTIFY: + case FSCTL_OPLOCK_BREAK_ACK_NO_2: + + Status = Ext2OplockRequest(IrpContext); + break; + + case FSCTL_IS_VOLUME_DIRTY: + Status = Ext2IsVolumeDirty(IrpContext); + break; + + case FSCTL_QUERY_RETRIEVAL_POINTERS: + Status = Ext2QueryRetrievalPointers(IrpContext); + break; + + case FSCTL_GET_RETRIEVAL_POINTERS: + Status = Ext2GetRetrievalPointers(IrpContext); + break; + + case FSCTL_GET_RETRIEVAL_POINTER_BASE: + Status = Ext2GetRetrievalPointerBase(IrpContext); + break; + + default: + + DEBUG(DL_INF, ( "Ext2UserFsRequest: Invalid User Request: %xh.\n", FsControlCode)); + Status = STATUS_INVALID_DEVICE_REQUEST; + + Ext2CompleteIrpContext(IrpContext, Status); + } + + return Status; +} + +BOOLEAN +Ext2IsMediaWriteProtected ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PDEVICE_OBJECT TargetDevice +) +{ + PIRP Irp; + KEVENT Event; + NTSTATUS Status; + IO_STATUS_BLOCK IoStatus; + + KeInitializeEvent(&Event, NotificationEvent, FALSE); + + Irp = IoBuildDeviceIoControlRequest( IOCTL_DISK_IS_WRITABLE, + TargetDevice, + NULL, + 0, + NULL, + 0, + FALSE, + &Event, + &IoStatus ); + + if (Irp == NULL) { + return FALSE; + } + + SetFlag(IoGetNextIrpStackLocation(Irp)->Flags, SL_OVERRIDE_VERIFY_VOLUME); + + Status = IoCallDriver(TargetDevice, Irp); + + if (Status == STATUS_PENDING) { + + (VOID) KeWaitForSingleObject( &Event, + Executive, + KernelMode, + FALSE, + (PLARGE_INTEGER)NULL ); + + Status = IoStatus.Status; + } + + return (BOOLEAN)(Status == STATUS_MEDIA_WRITE_PROTECTED); +} + +NTSTATUS +Ext2MountVolume (IN PEXT2_IRP_CONTEXT IrpContext) +{ + PDEVICE_OBJECT MainDeviceObject; + BOOLEAN GlobalDataResourceAcquired = FALSE; + PIRP Irp; + PIO_STACK_LOCATION IoStackLocation; + PDEVICE_OBJECT TargetDeviceObject; + NTSTATUS Status = STATUS_UNRECOGNIZED_VOLUME; + PDEVICE_OBJECT VolumeDeviceObject = NULL; + PEXT2_VCB Vcb = NULL, OldVcb = NULL; + PVPB OldVpb = NULL, Vpb = NULL; + PEXT2_SUPER_BLOCK Ext2Sb = NULL; + ULONG dwBytes; + DISK_GEOMETRY DiskGeometry; + + __try { + + ASSERT(IrpContext != NULL); + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + MainDeviceObject = IrpContext->DeviceObject; + + // + // Make sure we can wait. + // + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + + // + // This request is only allowed on the main device object + // + if (!IsExt2FsDevice(MainDeviceObject)) { + Status = STATUS_INVALID_DEVICE_REQUEST; + __leave; + } + + if (IsFlagOn(Ext2Global->Flags, EXT2_UNLOAD_PENDING)) { + Status = STATUS_UNRECOGNIZED_VOLUME; + __leave; + } + +#if 0 + if (IrpContext->RealDevice->Size >= sizeof(ULONG) + sizeof(DEVICE_OBJECT) && + *((PULONG)IrpContext->RealDevice->DeviceExtension) == 'DSSA') { + } else { + Status = STATUS_UNRECOGNIZED_VOLUME; + __leave; + } +#endif + + Irp = IrpContext->Irp; + IoStackLocation = IoGetCurrentIrpStackLocation(Irp); + TargetDeviceObject = + IoStackLocation->Parameters.MountVolume.DeviceObject; + + dwBytes = sizeof(DISK_GEOMETRY); + Status = Ext2DiskIoControl( + TargetDeviceObject, + IOCTL_DISK_GET_DRIVE_GEOMETRY, + NULL, + 0, + &DiskGeometry, + &dwBytes ); + + if (!NT_SUCCESS(Status)) { + __leave; + } + + Status = IoCreateDevice( + MainDeviceObject->DriverObject, + sizeof(EXT2_VCB), + NULL, + FILE_DEVICE_DISK_FILE_SYSTEM, + 0, + FALSE, + &VolumeDeviceObject ); + + if (!NT_SUCCESS(Status)) { + __leave; + } + INC_MEM_COUNT(PS_VCB, VolumeDeviceObject, sizeof(EXT2_VCB)); + +#ifdef _PNP_POWER_ + /* don't care about power management requests */ + VolumeDeviceObject->DeviceObjectExtension->PowerControlNeeded = FALSE; +#endif + + VolumeDeviceObject->StackSize = (CCHAR)(TargetDeviceObject->StackSize + 1); + ClearFlag(VolumeDeviceObject->Flags, DO_DEVICE_INITIALIZING); + +/* + These are for buffer-address alignment requirements. + Never do this check, unless you want fail user requests :) + + if (TargetDeviceObject->AlignmentRequirement > + VolumeDeviceObject->AlignmentRequirement) { + + VolumeDeviceObject->AlignmentRequirement = + TargetDeviceObject->AlignmentRequirement; + } + + if (DiskGeometry.BytesPerSector - 1 > + VolumeDeviceObject->AlignmentRequirement) { + VolumeDeviceObject->AlignmentRequirement = + DiskGeometry.BytesPerSector - 1; + TargetDeviceObject->AlignmentRequirement = + DiskGeometry.BytesPerSector - 1; + } +*/ + (IoStackLocation->Parameters.MountVolume.Vpb)->DeviceObject = + VolumeDeviceObject; + Vpb = IoStackLocation->Parameters.MountVolume.Vpb; + + Vcb = (PEXT2_VCB) VolumeDeviceObject->DeviceExtension; + + RtlZeroMemory(Vcb, sizeof(EXT2_VCB)); + Vcb->Identifier.Type = EXT2VCB; + Vcb->Identifier.Size = sizeof(EXT2_VCB); + Vcb->TargetDeviceObject = TargetDeviceObject; + Vcb->DiskGeometry = DiskGeometry; + InitializeListHead(&Vcb->Next); + + Status = Ext2LoadSuper(Vcb, FALSE, &Ext2Sb); + if (!NT_SUCCESS(Status)) { + Vcb = NULL; + Status = STATUS_UNRECOGNIZED_VOLUME; + __leave; + } + ASSERT (NULL != Ext2Sb); + + /* check Linux Ext2/Ext3 volume magic */ + if (Ext2Sb->s_magic == EXT2_SUPER_MAGIC) { + DEBUG(DL_INF, ( "Volume of ext2 file system is found.\n")); + } else { + Status = STATUS_UNRECOGNIZED_VOLUME; + Vcb = NULL; + __leave; + } + + DEBUG(DL_DBG, ("Ext2MountVolume: DevObject=%p Vcb=%p\n", VolumeDeviceObject, Vcb)); + + /* initialize Vcb structure */ + Status = Ext2InitializeVcb( IrpContext, Vcb, Ext2Sb, + TargetDeviceObject, + VolumeDeviceObject, Vpb); + + if (NT_SUCCESS(Status)) { + + PLIST_ENTRY List; + + ExAcquireResourceExclusiveLite(&(Ext2Global->Resource), TRUE); + GlobalDataResourceAcquired = TRUE; + + for (List = Ext2Global->VcbList.Flink; + List != &Ext2Global->VcbList; + List = List->Flink) { + + OldVcb = CONTAINING_RECORD(List, EXT2_VCB, Next); + OldVpb = OldVcb->Vpb; + + /* in case we are already in the queue, should not happen */ + if (OldVpb == Vpb) { + continue; + } + + if ( (OldVpb->SerialNumber == Vpb->SerialNumber) && + (!IsMounted(OldVcb)) && (IsFlagOn(OldVcb->Flags, VCB_NEW_VPB)) && + (OldVpb->RealDevice == TargetDeviceObject) && + (OldVpb->VolumeLabelLength == Vpb->VolumeLabelLength) && + (RtlEqualMemory(&OldVpb->VolumeLabel[0], + &Vpb->VolumeLabel[0], + Vpb->VolumeLabelLength)) && + (RtlEqualMemory(&OldVcb->SuperBlock->s_uuid[0], + &Vcb->SuperBlock->s_uuid[0], 16)) ) { + ClearFlag(OldVcb->Flags, VCB_MOUNTED); + } + } + + if (!ext4_superblock_csum_verify(&Vcb->sb, Ext2Sb)) { + DbgPrint("Found ext4 filesystem with invalid superblock checksum. Run e2fsck?\n"); + } + + SetLongFlag(Vcb->Flags, VCB_MOUNTED); + SetFlag(Vcb->Vpb->Flags, VPB_MOUNTED); + Ext2InsertVcb(Vcb); + Vcb = NULL; + Vpb = NULL; + ObDereferenceObject(TargetDeviceObject); + + } else { + + Vcb = NULL; + } + + } __finally { + + if (GlobalDataResourceAcquired) { + ExReleaseResourceLite(&Ext2Global->Resource); + } + + if (!NT_SUCCESS(Status)) { + + if (!NT_SUCCESS(Status)) { + if ( Vpb != NULL ) { + Vpb->DeviceObject = NULL; + } + } + + if (Vcb) { + Ext2DestroyVcb(Vcb); + } else { + if (Ext2Sb) { + Ext2FreePool(Ext2Sb, EXT2_SB_MAGIC); + } + if (VolumeDeviceObject) { + IoDeleteDevice(VolumeDeviceObject); + DEC_MEM_COUNT(PS_VCB, VolumeDeviceObject, sizeof(EXT2_VCB)); + } + } + } + + if (!IrpContext->ExceptionInProgress) { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + + return Status; +} + +VOID +Ext2VerifyVcb (IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb ) +{ + NTSTATUS Status = STATUS_SUCCESS; + + BOOLEAN bVerify = FALSE; + ULONG ChangeCount = 0; + ULONG dwBytes; + + PIRP Irp; + PEXTENDED_IO_STACK_LOCATION IrpSp; + + __try { + + ASSERT(IrpContext != NULL); + + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + Irp = IrpContext->Irp; + IrpSp = (PEXTENDED_IO_STACK_LOCATION)IoGetCurrentIrpStackLocation(Irp); + + bVerify = IsFlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + + if ( (IsFlagOn(Vcb->Flags, VCB_REMOVABLE_MEDIA) || + IsFlagOn(Vcb->Flags, VCB_FLOPPY_DISK)) && !bVerify ) { + + dwBytes = sizeof(ULONG); + Status = Ext2DiskIoControl( + Vcb->TargetDeviceObject, + IOCTL_DISK_CHECK_VERIFY, + NULL, + 0, + &ChangeCount, + &dwBytes ); + + if ( STATUS_VERIFY_REQUIRED == Status || + STATUS_DEVICE_NOT_READY == Status || + STATUS_NO_MEDIA_IN_DEVICE == Status || + (NT_SUCCESS(Status) && + (ChangeCount != Vcb->ChangeCount))) { + + KIRQL Irql; + + IoAcquireVpbSpinLock(&Irql); + if (Vcb->Vpb == Vcb->Vpb->RealDevice->Vpb) { + SetFlag(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + } + IoReleaseVpbSpinLock(Irql); + + } else { + + if (!NT_SUCCESS(Status)) { + Ext2NormalizeAndRaiseStatus(IrpContext, Status); + } + } + } + + if ( IsFlagOn(Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME)) { + IoSetHardErrorOrVerifyDevice( Irp, Vcb->Vpb->RealDevice ); + Ext2NormalizeAndRaiseStatus ( IrpContext, + STATUS_VERIFY_REQUIRED ); + } + + if (IsMounted(Vcb)) { + + if ( (IrpContext->MajorFunction == IRP_MJ_WRITE) || + (IrpContext->MajorFunction == IRP_MJ_SET_INFORMATION) || + (IrpContext->MajorFunction == IRP_MJ_SET_EA) || + (IrpContext->MajorFunction == IRP_MJ_FLUSH_BUFFERS) || + (IrpContext->MajorFunction == IRP_MJ_SET_VOLUME_INFORMATION) || + (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL && + IrpContext->MinorFunction == IRP_MN_USER_FS_REQUEST && + IrpSp->Parameters.FileSystemControl.FsControlCode == + FSCTL_MARK_VOLUME_DIRTY)) { + + if (IsFlagOn(Vcb->Flags, VCB_WRITE_PROTECTED)) { + + KIRQL Irql; + + IoAcquireVpbSpinLock(&Irql); + if (Vcb->Vpb == Vcb->Vpb->RealDevice->Vpb) { + SetFlag (Vcb->Vpb->RealDevice->Flags, DO_VERIFY_VOLUME); + } + IoReleaseVpbSpinLock(Irql); + + IoSetHardErrorOrVerifyDevice( Irp, Vcb->Vpb->RealDevice ); + + Ext2RaiseStatus(IrpContext, STATUS_MEDIA_WRITE_PROTECTED); + } + } + } + + } __finally { + + } + +} + + +NTSTATUS +Ext2VerifyVolume (IN PEXT2_IRP_CONTEXT IrpContext) +{ + PDEVICE_OBJECT DeviceObject; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + PEXT2_SUPER_BLOCK ext2_sb = NULL; + PEXT2_VCB Vcb = NULL; + BOOLEAN VcbResourceAcquired = FALSE; + PIRP Irp; + ULONG ChangeCount = 0; + ULONG dwBytes; + + __try { + + ASSERT(IrpContext != NULL); + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + DeviceObject = IrpContext->DeviceObject; + // + // This request is not allowed on the main device object + // + if (IsExt2FsDevice(DeviceObject)) { + Status = STATUS_INVALID_DEVICE_REQUEST; + __leave; + } + + Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; + ASSERT(Vcb != NULL); + ASSERT((Vcb->Identifier.Type == EXT2VCB) && + (Vcb->Identifier.Size == sizeof(EXT2_VCB))); + + VcbResourceAcquired = + ExAcquireResourceExclusiveLite( + &Vcb->MainResource, + TRUE ); + + if (!FlagOn(Vcb->TargetDeviceObject->Flags, DO_VERIFY_VOLUME)) { + Status = STATUS_SUCCESS; + __leave; + } + + if (!IsMounted(Vcb)) { + Status = STATUS_WRONG_VOLUME; + __leave; + } + + dwBytes = sizeof(ULONG); + Status = Ext2DiskIoControl( + Vcb->TargetDeviceObject, + IOCTL_DISK_CHECK_VERIFY, + NULL, + 0, + &ChangeCount, + &dwBytes ); + + + if (!NT_SUCCESS(Status)) { + Status = STATUS_WRONG_VOLUME; + __leave; + } else { + Vcb->ChangeCount = ChangeCount; + } + + Irp = IrpContext->Irp; + + Status = Ext2LoadSuper(Vcb, TRUE, &ext2_sb); + + if (!NT_SUCCESS(Status)) { + __leave; + } + + ASSERT(NULL != ext2_sb); + if ((ext2_sb->s_magic == EXT2_SUPER_MAGIC) && + (memcmp(ext2_sb->s_uuid, SUPER_BLOCK->s_uuid, 16) == 0) && + (memcmp(ext2_sb->s_volume_name, SUPER_BLOCK->s_volume_name, 16) ==0)) { + + ClearFlag(Vcb->TargetDeviceObject->Flags, DO_VERIFY_VOLUME); + + if (Ext2IsMediaWriteProtected(IrpContext, Vcb->TargetDeviceObject)) { + SetLongFlag(Vcb->Flags, VCB_WRITE_PROTECTED); + } else { + ClearLongFlag(Vcb->Flags, VCB_WRITE_PROTECTED); + } + + DEBUG(DL_INF, ( "Ext2VerifyVolume: Volume verify succeeded.\n")); + + } else { + + Status = STATUS_WRONG_VOLUME; + Ext2PurgeVolume(Vcb, FALSE); + + SetLongFlag(Vcb->Flags, VCB_DISMOUNT_PENDING); + ClearFlag(Vcb->TargetDeviceObject->Flags, DO_VERIFY_VOLUME); + + DEBUG(DL_INF, ( "Ext2VerifyVolume: Volume verify failed.\n")); + } + + } __finally { + + if (ext2_sb) + Ext2FreePool(ext2_sb, EXT2_SB_MAGIC); + + if (VcbResourceAcquired) { + ExReleaseResourceLite(&Vcb->MainResource); + } + + if (!IrpContext->ExceptionInProgress) { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + + return Status; +} + + +NTSTATUS +Ext2IsVolumeMounted (IN PEXT2_IRP_CONTEXT IrpContext) +{ + PDEVICE_OBJECT DeviceObject; + PEXT2_VCB Vcb = 0; + NTSTATUS Status = STATUS_SUCCESS; + + ASSERT(IrpContext); + + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + + DeviceObject = IrpContext->DeviceObject; + + Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; + + ASSERT(IsMounted(Vcb)); + + Ext2VerifyVcb (IrpContext, Vcb); + + Ext2CompleteIrpContext(IrpContext, Status); + + return Status; +} + + +NTSTATUS +Ext2DismountVolume (IN PEXT2_IRP_CONTEXT IrpContext) +{ + PDEVICE_OBJECT DeviceObject; + NTSTATUS Status = STATUS_UNSUCCESSFUL; + PEXT2_VCB Vcb = NULL; + BOOLEAN VcbResourceAcquired = FALSE; + + __try { + + ASSERT(IrpContext != NULL); + + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + DeviceObject = IrpContext->DeviceObject; + + // + // This request is not allowed on the main device object + // + if (IsExt2FsDevice(DeviceObject)) { + Status = STATUS_INVALID_DEVICE_REQUEST; + __leave; + } + + Vcb = (PEXT2_VCB) DeviceObject->DeviceExtension; + + ASSERT(Vcb != NULL); + + ASSERT((Vcb->Identifier.Type == EXT2VCB) && + (Vcb->Identifier.Size == sizeof(EXT2_VCB))); + + ASSERT(IsMounted(Vcb)); + + ExAcquireResourceExclusiveLite( + &Vcb->MainResource, + TRUE ); + + VcbResourceAcquired = TRUE; + + if ( IsFlagOn(Vcb->Flags, VCB_DISMOUNT_PENDING)) { + Status = STATUS_VOLUME_DISMOUNTED; + __leave; + } + + Ext2FlushFiles(IrpContext, Vcb, FALSE); + Ext2FlushVolume(IrpContext, Vcb, FALSE); + + ExReleaseResourceLite(&Vcb->MainResource); + VcbResourceAcquired = FALSE; + + Ext2PurgeVolume(Vcb, TRUE); + Ext2CheckDismount(IrpContext, Vcb, TRUE); + + DEBUG(DL_INF, ( "Ext2Dismount: Volume dismount pending.\n")); + Status = STATUS_SUCCESS; + + } __finally { + + if (VcbResourceAcquired) { + ExReleaseResourceLite(&Vcb->MainResource); + } + + if (!IrpContext->ExceptionInProgress) { + Ext2CompleteIrpContext(IrpContext, Status); + } + } + + return Status; +} + +BOOLEAN +Ext2CheckDismount ( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN BOOLEAN bForce ) +{ + KIRQL Irql; + PVPB Vpb = Vcb->Vpb, NewVpb = NULL; + BOOLEAN bDeleted = FALSE, bTearDown = FALSE; + ULONG UnCleanCount = 0; + + NewVpb = ExAllocatePoolWithTag(NonPagedPool, VPB_SIZE, TAG_VPB); + if (NewVpb == NULL) { + DEBUG(DL_ERR, ( "Ex2CheckDismount: failed to allocate NewVpb.\n")); + return FALSE; + } + DEBUG(DL_DBG, ("Ext2CheckDismount: NewVpb allocated: %p\n", NewVpb)); + INC_MEM_COUNT(PS_VPB, NewVpb, sizeof(VPB)); + memset(NewVpb, '_', VPB_SIZE); + RtlZeroMemory(NewVpb, sizeof(VPB)); + + ExAcquireResourceExclusiveLite( + &Ext2Global->Resource, TRUE ); + + ExAcquireResourceExclusiveLite( + &Vcb->MainResource, TRUE ); + + if (IrpContext && + IrpContext->MajorFunction == IRP_MJ_CREATE && + IrpContext->RealDevice == Vcb->RealDevice) { + UnCleanCount = 2; + } else { + UnCleanCount = 1; + } + + IoAcquireVpbSpinLock (&Irql); + + DEBUG(DL_DBG, ("Ext2CheckDismount: Vpb %p ioctl=%d Device %p\n", + Vpb, Vpb->ReferenceCount, Vpb->RealDevice)); + + if (Vpb->ReferenceCount <= UnCleanCount) { + + if (!IsFlagOn(Vcb->Flags, VCB_DISMOUNT_PENDING)) { + + ClearFlag(Vpb->Flags, VPB_MOUNTED); + ClearFlag(Vpb->Flags, VPB_LOCKED); + + if ((Vcb->RealDevice != Vpb->RealDevice) && + (Vcb->RealDevice->Vpb == Vpb)) { + SetFlag(Vcb->RealDevice->Flags, DO_DEVICE_INITIALIZING); + SetFlag(Vpb->Flags, VPB_PERSISTENT ); + } + + Ext2RemoveVcb(Vcb); + SetLongFlag(Vcb->Flags, VCB_DISMOUNT_PENDING); + } + + if (Vpb->ReferenceCount) { + bTearDown = TRUE; + } else { + bDeleted = TRUE; + Vpb->DeviceObject = NULL; + } + + DEBUG(DL_DBG, ("Ext2CheckDismount: Vpb: %p bDeleted=%d bTearDown=%d\n", + Vpb, bDeleted, bTearDown)); + + + } else if (bForce) { + + DEBUG(DL_DBG, ( "Ext2CheckDismount: New/Old Vpb %p/%p Realdevice = %p\n", + NewVpb, Vcb->Vpb, Vpb->RealDevice)); + + /* keep vpb president and later we'll free it */ + SetFlag(Vpb->Flags, VPB_PERSISTENT); + + Vcb->Vpb2 = Vcb->Vpb; + NewVpb->Type = IO_TYPE_VPB; + NewVpb->Size = sizeof(VPB); + NewVpb->Flags = Vpb->Flags & VPB_REMOVE_PENDING; + NewVpb->RealDevice = Vpb->RealDevice; + NewVpb->RealDevice->Vpb = NewVpb; + NewVpb = NULL; + ClearFlag(Vpb->Flags, VPB_MOUNTED); + SetLongFlag(Vcb->Flags, VCB_NEW_VPB); + ClearLongFlag(Vcb->Flags, VCB_MOUNTED); + } + + IoReleaseVpbSpinLock(Irql); + + ExReleaseResourceLite(&Vcb->MainResource); + ExReleaseResourceLite(&Ext2Global->Resource); + + if (bTearDown) { + DEBUG(DL_DBG, ( "Ext2CheckDismount: Tearing vcb %p ...\n", Vcb)); + Ext2TearDownStream(Vcb); + } + + if (bDeleted) { + DEBUG(DL_DBG, ( "Ext2CheckDismount: Deleting vcb %p ...\n", Vcb)); + Ext2DestroyVcb(Vcb); + } + + if (NewVpb != NULL) { + DEBUG(DL_DBG, ( "Ext2CheckDismount: freeing new Vpb %p\n", NewVpb)); + ExFreePoolWithTag(NewVpb, TAG_VPB); + DEC_MEM_COUNT(PS_VPB, NewVpb, sizeof(VPB)); + } + + return bDeleted; +} + +NTSTATUS +Ext2PurgeVolume (IN PEXT2_VCB Vcb, + IN BOOLEAN FlushBeforePurge ) +{ + PEXT2_FCB Fcb; + LIST_ENTRY List, *Next; + + BOOLEAN VcbResourceAcquired = FALSE; + BOOLEAN FcbResourceAcquired = FALSE; + BOOLEAN gdResourceAcquired = FALSE; + + __try { + + ASSERT(Vcb != NULL); + ASSERT((Vcb->Identifier.Type == EXT2VCB) && + (Vcb->Identifier.Size == sizeof(EXT2_VCB))); + + ExAcquireResourceExclusiveLite(&Vcb->MainResource, TRUE); + VcbResourceAcquired = TRUE; + + if (IsVcbReadOnly(Vcb)) { + FlushBeforePurge = FALSE; + } + + InitializeListHead(&List); + + ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); + FcbResourceAcquired = TRUE; + + while (!IsListEmpty(&Vcb->FcbList)) { + + Next = RemoveHeadList(&Vcb->FcbList); + Fcb = CONTAINING_RECORD(Next, EXT2_FCB, Next); + + DEBUG(DL_INF, ( "Ext2PurgeVolume: %wZ refercount=%xh\n", + &Fcb->Mcb->FullName, Fcb->ReferenceCount)); + InsertTailList(&List, &Fcb->Next); + } + + while (!IsListEmpty(&List)) { + + Next = RemoveHeadList(&List); + Fcb = CONTAINING_RECORD(Next, EXT2_FCB, Next); + + if (ExAcquireResourceExclusiveLite( + &Fcb->MainResource, + TRUE )) { + + Ext2PurgeFile(Fcb, FlushBeforePurge); + + if (Fcb->ReferenceCount <= 1) { + Fcb->TsDrop.QuadPart = 0; + InsertHeadList(&Vcb->FcbList, &Fcb->Next); + } else { + InsertTailList(&Vcb->FcbList, &Fcb->Next); + } + ExReleaseResourceLite(&Fcb->MainResource); + } + } + + if (FcbResourceAcquired) { + ExReleaseResourceLite(&Vcb->FcbLock); + FcbResourceAcquired = FALSE; + } + + /* acquire bd lock to avoid bh creation */ + ExAcquireResourceExclusiveLite(&Vcb->sbi.s_gd_lock, TRUE); + gdResourceAcquired = TRUE; + + /* discard buffer_headers for group_desc */ + Ext2DropBH(Vcb); + + if (FlushBeforePurge) { + ExAcquireSharedStarveExclusive(&Vcb->PagingIoResource, TRUE); + ExReleaseResourceLite(&Vcb->PagingIoResource); + + CcFlushCache(&Vcb->SectionObject, NULL, 0, NULL); + } + + if (Vcb->SectionObject.ImageSectionObject) { + MmFlushImageSection(&Vcb->SectionObject, MmFlushForWrite); + } + + if (Vcb->SectionObject.DataSectionObject) { + CcPurgeCacheSection(&Vcb->SectionObject, NULL, 0, FALSE); + } + + DEBUG(DL_INF, ( "Ext2PurgeVolume: Volume flushed and purged.\n")); + + } __finally { + + if (gdResourceAcquired) { + ExReleaseResourceLite(&Vcb->sbi.s_gd_lock); + } + + if (FcbResourceAcquired) { + ExReleaseResourceLite(&Vcb->FcbLock); + } + + if (VcbResourceAcquired) { + ExReleaseResourceLite(&Vcb->MainResource); + } + } + + return STATUS_SUCCESS; +} + +NTSTATUS +Ext2PurgeFile ( IN PEXT2_FCB Fcb, + IN BOOLEAN FlushBeforePurge ) +{ + IO_STATUS_BLOCK IoStatus; + + ASSERT(Fcb != NULL); + + ASSERT((Fcb->Identifier.Type == EXT2FCB) && + (Fcb->Identifier.Size == sizeof(EXT2_FCB))); + + + if (!IsVcbReadOnly(Fcb->Vcb) && FlushBeforePurge) { + DEBUG(DL_INF, ( "Ext2PurgeFile: CcFlushCache on %wZ.\n", + &Fcb->Mcb->FullName)); + ExAcquireSharedStarveExclusive(&Fcb->PagingIoResource, TRUE); + ExReleaseResourceLite(&Fcb->PagingIoResource); + CcFlushCache(&Fcb->SectionObject, NULL, 0, &IoStatus); + ClearFlag(Fcb->Flags, FCB_FILE_MODIFIED); + } + + if (Fcb->SectionObject.ImageSectionObject) { + DEBUG(DL_INF, ( "Ext2PurgeFile: MmFlushImageSection on %wZ.\n", + &Fcb->Mcb->FullName)); + MmFlushImageSection(&Fcb->SectionObject, MmFlushForWrite); + } + + if (Fcb->SectionObject.DataSectionObject) { + DEBUG(DL_INF, ( "Ext2PurgeFile: CcPurgeCacheSection on %wZ.\n", + &Fcb->Mcb->FullName)); + CcPurgeCacheSection(&Fcb->SectionObject, NULL, 0, FALSE); + } + + return STATUS_SUCCESS; +} + + +NTSTATUS +Ext2FileSystemControl (IN PEXT2_IRP_CONTEXT IrpContext) +{ + NTSTATUS Status; + + ASSERT(IrpContext); + + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + switch (IrpContext->MinorFunction) { + + case IRP_MN_USER_FS_REQUEST: + Status = Ext2UserFsRequest(IrpContext); + break; + + case IRP_MN_MOUNT_VOLUME: + Status = Ext2MountVolume(IrpContext); + break; + + case IRP_MN_VERIFY_VOLUME: + Status = Ext2VerifyVolume(IrpContext); + break; + + default: + + DEBUG(DL_ERR, ( "Ext2FilsSystemControl: Invalid Device Request.\n")); + Status = STATUS_INVALID_DEVICE_REQUEST; + Ext2CompleteIrpContext(IrpContext, Status); + } + + return Status; +} diff --git a/Ext4Fsd/include/ext2fs.h b/Ext4Fsd/include/ext2fs.h index 2830893..546b45a 100644 --- a/Ext4Fsd/include/ext2fs.h +++ b/Ext4Fsd/include/ext2fs.h @@ -17,9 +17,6 @@ #include #include #include -#include -#include -#include #include /* DEBUG ****************************************************************/ @@ -2021,10 +2018,10 @@ loff_t ext3_max_size(int blkbits, int has_huge_files); loff_t ext3_max_bitmap_size(int bits, int has_huge_files); -__le16 ext4_group_desc_csum(struct ext3_sb_info *sbi, __u32 block_group, +/*__le16 ext4_group_desc_csum(struct ext3_sb_info *sbi, __u32 block_group, struct ext4_group_desc *gdp); int ext4_group_desc_csum_verify(struct ext3_sb_info *sbi, __u32 block_group, - struct ext4_group_desc *gdp); + struct ext4_group_desc *gdp);*/ ext3_fsblk_t descriptor_loc(struct super_block *sb, ext3_fsblk_t logical_sb_block, unsigned int nr); diff --git a/Ext4Fsd/include/linux/ext3_jbd.h b/Ext4Fsd/include/linux/ext3_jbd.h index acee54f..37117ce 100644 --- a/Ext4Fsd/include/linux/ext3_jbd.h +++ b/Ext4Fsd/include/linux/ext3_jbd.h @@ -17,7 +17,6 @@ #include #include -#include #define EXT3_JOURNAL(inode) (EXT3_SB((inode)->i_sb)->s_journal) diff --git a/Ext4Fsd/include/linux/ext4.h b/Ext4Fsd/include/linux/ext4.h index 2aa4a39..454b8e9 100644 --- a/Ext4Fsd/include/linux/ext4.h +++ b/Ext4Fsd/include/linux/ext4.h @@ -1,122 +1,3394 @@ -#ifndef _EXT4_H -#define _EXT4_H - -#include -/* Temporarily we need this. */ -#include - -typedef unsigned __int16 uint16_t; -typedef unsigned __int32 uint32_t; -typedef unsigned __int64 uint64_t; - -typedef uint32_t ext4_lblk_t; -typedef uint64_t ext4_fsblk_t; - -/* - * Flags used by ext4_map_blocks() - */ - /* Allocate any needed blocks and/or convert an unwritten - extent to be an initialized ext4 */ -#define EXT4_GET_BLOCKS_CREATE 0x0001 - /* Request the creation of an unwritten extent */ -#define EXT4_GET_BLOCKS_UNWRIT_EXT 0x0002 -#define EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT (EXT4_GET_BLOCKS_UNWRIT_EXT|\ - EXT4_GET_BLOCKS_CREATE) - /* Caller is from the delayed allocation writeout path - * finally doing the actual allocation of delayed blocks */ -#define EXT4_GET_BLOCKS_DELALLOC_RESERVE 0x0004 - /* caller is from the direct IO path, request to creation of an - unwritten extents if not allocated, split the unwritten - extent if blocks has been preallocated already*/ -#define EXT4_GET_BLOCKS_PRE_IO 0x0008 -#define EXT4_GET_BLOCKS_CONVERT 0x0010 -#define EXT4_GET_BLOCKS_IO_CREATE_EXT (EXT4_GET_BLOCKS_PRE_IO|\ - EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT) - /* Convert extent to initialized after IO complete */ -#define EXT4_GET_BLOCKS_IO_CONVERT_EXT (EXT4_GET_BLOCKS_CONVERT|\ - EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT) - /* Eventual metadata allocation (due to growing extent tree) - * should not fail, so try to use reserved blocks for that.*/ -#define EXT4_GET_BLOCKS_METADATA_NOFAIL 0x0020 - /* Don't normalize allocation size (used for fallocate) */ -#define EXT4_GET_BLOCKS_NO_NORMALIZE 0x0040 - /* Request will not result in inode size update (user for fallocate) */ -#define EXT4_GET_BLOCKS_KEEP_SIZE 0x0080 - /* Do not take i_data_sem locking in ext4_map_blocks */ -#define EXT4_GET_BLOCKS_NO_LOCK 0x0100 - /* Do not put hole in extent cache */ -#define EXT4_GET_BLOCKS_NO_PUT_HOLE 0x0200 - /* Convert written extents to unwritten */ -#define EXT4_GET_BLOCKS_CONVERT_UNWRITTEN 0x0400 - -/* - * The bit position of these flags must not overlap with any of the - * EXT4_GET_BLOCKS_*. They are used by ext4_find_extent(), - * read_extent_tree_block(), ext4_split_extent_at(), - * ext4_ext_insert_extent(), and ext4_ext_create_new_leaf(). - * EXT4_EX_NOCACHE is used to indicate that the we shouldn't be - * caching the extents when reading from the extent tree while a - * truncate or punch hole operation is in progress. - */ -#define EXT4_EX_NOCACHE 0x40000000 -#define EXT4_EX_FORCE_CACHE 0x20000000 - -/* - * Flags used by ext4_free_blocks - */ -#define EXT4_FREE_BLOCKS_METADATA 0x0001 -#define EXT4_FREE_BLOCKS_FORGET 0x0002 -#define EXT4_FREE_BLOCKS_VALIDATED 0x0004 -#define EXT4_FREE_BLOCKS_NO_QUOT_UPDATE 0x0008 -#define EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER 0x0010 -#define EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER 0x0020 - -/* - * Flags used in mballoc's allocation_context flags field. - * - * Also used to show what's going on for debugging purposes when the - * flag field is exported via the traceport interface - */ - -/* prefer goal again. length */ -#define EXT4_MB_HINT_MERGE 0x0001 -/* blocks already reserved */ -#define EXT4_MB_HINT_RESERVED 0x0002 -/* metadata is being allocated */ -#define EXT4_MB_HINT_METADATA 0x0004 -/* first blocks in the file */ -#define EXT4_MB_HINT_FIRST 0x0008 -/* search for the best chunk */ -#define EXT4_MB_HINT_BEST 0x0010 -/* data is being allocated */ -#define EXT4_MB_HINT_DATA 0x0020 -/* don't preallocate (for tails) */ -#define EXT4_MB_HINT_NOPREALLOC 0x0040 -/* allocate for locality group */ -#define EXT4_MB_HINT_GROUP_ALLOC 0x0080 -/* allocate goal blocks or none */ -#define EXT4_MB_HINT_GOAL_ONLY 0x0100 -/* goal is meaningful */ -#define EXT4_MB_HINT_TRY_GOAL 0x0200 -/* blocks already pre-reserved by delayed allocation */ -#define EXT4_MB_DELALLOC_RESERVED 0x0400 -/* We are doing stream allocation */ -#define EXT4_MB_STREAM_ALLOC 0x0800 -/* Use reserved root blocks if needed */ -#define EXT4_MB_USE_ROOT_BLOCKS 0x1000 -/* Use blocks from reserved pool */ -#define EXT4_MB_USE_RESERVED 0x2000 - - -#define ext4_sb_info ext3_sb_info - -static inline struct ext4_sb_info * EXT4_SB(struct super_block *sb) -{ - return sb->s_fs_info; -} -#define EXT4_I(i) (i) - -#include -#include - -#endif /* _EXT4_H */ +// SPDX-License-Identifier: GPL-2.0 +/* + * ext4.h + * + * Copyright (C) 1992, 1993, 1994, 1995 + * Remy Card (card@masi.ibp.fr) + * Laboratoire MASI - Institut Blaise Pascal + * Universite Pierre et Marie Curie (Paris VI) + * + * from + * + * linux/include/linux/minix_fs.h + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#ifndef _EXT4_H +#define _EXT4_H + +// +// Use 1 byte packing of on-disk structures +// +#include + +#include +//#include +#include +//#include +//#include +//#include +#include +//#include +#include +#include +#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +//#include +#ifdef __KERNEL__ +//#include +#endif +typedef struct handle_s handle_t; +typedef unsigned long long ext4_fsblk_t; +#include +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; +#include + +#define __FS_HAS_ENCRYPTION IS_ENABLED(CONFIG_EXT4_FS_ENCRYPTION) +//#include + +//#include + +/* + * First declarations to be able to compile the Windows driver with + * ext4.h from Linux. When the driver is changed to use ext4_ names + * instead of ext3_ and ext2_ many of these can be removed. + */ + +#define S_IFMT 0x0F000 /* 017 0000 */ +#define S_IFSOCK 0x0C000 /* 014 0000 */ +#define S_IFLNK 0x0A000 /* 012 0000 */ +#define S_IFREG 0x08000 /* 010 0000 */ +#define S_IFBLK 0x06000 /* 006 0000 */ +#define S_IFDIR 0x04000 /* 004 0000 */ +#define S_IFCHR 0x02000 /* 002 0000 */ +#define S_IFIFO 0x01000 /* 001 0000 */ + +#define S_ISUID 0x00800 /* 000 4000 */ +#define S_ISGID 0x00400 /* 000 2000 */ +#define S_ISVTX 0x00200 /* 000 1000 */ + +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#define S_ISFIL(m) (((m) & S_IFMT) == S_IFFIL) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) + +#define S_NOQUOTA 32 /* Inode is not counted to quota */ +#define IS_NOQUOTA(inode) ((inode)->i_flags & S_NOQUOTA) + +#undef BUG_ON +#define BUG_ON(x) +#define BUILD_BUG_ON(x) +#define EXT4_I(inode) (inode) +#define INODE_HAS_EXTENT(i) ((i)->i_flags & EXT2_EXTENTS_FL) +#define EXT2_GOOD_OLD_INODE_SIZE EXT4_GOOD_OLD_INODE_SIZE +#define EXT3_GOOD_OLD_REV EXT4_GOOD_OLD_REV +#define EXT2_ROOT_INO EXT4_ROOT_INO +#define EXT2_EXTENTS_FL EXT4_EXTENTS_FL +#define EXT2_NAME_LEN EXT4_NAME_LEN +#define EXT3_NAME_LEN EXT4_NAME_LEN +#define EXT4_NAME_LEN 255 +#define EXT2_MIN_BLOCK_SIZE EXT4_MIN_BLOCK_SIZE +#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE EXT4_FEATURE_RO_COMPAT_LARGE_FILE +#define EXT2_DIR_REC_LEN EXT4_DIR_REC_LEN +#define EXT3_DIR_REC_LEN EXT4_DIR_REC_LEN +#define EXT2_NDIR_BLOCKS EXT4_NDIR_BLOCKS +#define EXT3_NDIR_BLOCKS EXT4_NDIR_BLOCKS +#define EXT3_BLOCKS_PER_GROUP EXT4_BLOCKS_PER_GROUP +#define EXT3_INODES_PER_GROUP EXT4_INODES_PER_GROUP +#define EXT3_BLOCK_SIZE_BITS EXT4_BLOCK_SIZE_BITS +#define EXT3_HTREE_EOF 0x7fffffff +#define EXT3_DIRENT_LUFID 0x10 + +#define ext3_super_block ext4_super_block +#define ext3_inode ext4_inode +#define ext3_dir_entry_2 ext4_dir_entry_2 +#define ext3_sb_info ext4_sb_info +#define ext3_group_t ext4_group_t +#define ext3_lblk_t ext4_lblk_t + +#define s_blocks_count s_blocks_count_lo +#define s_r_blocks_count s_r_blocks_count_lo +#define s_free_blocks_count s_free_blocks_count_lo +#define s_blocks_count s_blocks_count_lo +#define s_free_blocks_count s_free_blocks_count_lo +#define s_r_blocks_count s_r_blocks_count_lo + +#define bg_block_bitmap bg_block_bitmap_lo +#define bg_inode_bitmap bg_inode_bitmap_lo +#define bg_inode_table bg_inode_table_lo +#define bg_free_blocks_count bg_free_blocks_count_lo +#define bg_free_inodes_count bg_free_inodes_count_lo +#define bg_used_dirs_count bg_used_dirs_count_lo +#define bg_itable_unused bg_itable_unused_lo + +#define EXT3_MIN_BLOCK_SIZE EXT4_MIN_BLOCK_SIZE +#define EXT3_FEATURE_COMPAT_DIR_INDEX EXT4_FEATURE_COMPAT_DIR_INDEX +#define EXT3_FEATURE_RO_COMPAT_SPARSE_SUPER EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER +#define EXT3_FEATURE_INCOMPAT_FILETYPE EXT4_FEATURE_INCOMPAT_FILETYPE +#define EXT3_FEATURE_INCOMPAT_RECOVER EXT4_FEATURE_INCOMPAT_RECOVER +#define EXT4_HAS_RO_COMPAT_FEATURE EXT3_HAS_RO_COMPAT_FEATURE +#define EXT4_HAS_INCOMPAT_FEATURE EXT3_HAS_INCOMPAT_FEATURE +#define EXT3_SB EXT4_SB +#define EXT3_I EXT4_I +#define EXT3_INDEX_FL EXT4_INDEX_FL + +#define EXT3_FT_UNKNOWN EXT4_FT_UNKNOWN +#define EXT3_FT_REG_FILE EXT4_FT_REG_FILE +#define EXT3_FT_DIR EXT4_FT_DIR +#define EXT3_FT_CHRDEV EXT4_FT_CHRDEV +#define EXT3_FT_BLKDEV EXT4_FT_BLKDEV +#define EXT3_FT_FIFO EXT4_FT_FIFO +#define EXT3_FT_SOCK EXT4_FT_SOCK +#define EXT3_FT_SYMLINK EXT4_FT_SYMLINK +#define EXT3_FT_MAX EXT4_FT_MAX +#define EXT3_FT_MASK 0xf + +#define EXT3_HAS_COMPAT_FEATURE(sb,mask) \ + ( EXT3_SB(sb)->s_es->s_feature_compat & cpu_to_le32(mask) ) +#define EXT3_HAS_RO_COMPAT_FEATURE(sb,mask) \ + ( EXT3_SB(sb)->s_es->s_feature_ro_compat & cpu_to_le32(mask) ) +#define EXT3_HAS_INCOMPAT_FEATURE(sb,mask) \ + ( EXT3_SB(sb)->s_es->s_feature_incompat & cpu_to_le32(mask) ) +#define EXT3_SET_COMPAT_FEATURE(sb,mask) \ + EXT3_SB(sb)->s_es->s_feature_compat |= cpu_to_le32(mask) +#define EXT3_SET_RO_COMPAT_FEATURE(sb,mask) \ + EXT3_SB(sb)->s_es->s_feature_ro_compat |= cpu_to_le32(mask) +#define EXT3_SET_INCOMPAT_FEATURE(sb,mask) \ + EXT3_SB(sb)->s_es->s_feature_incompat |= cpu_to_le32(mask) +#define EXT3_CLEAR_COMPAT_FEATURE(sb,mask) \ + EXT3_SB(sb)->s_es->s_feature_compat &= ~cpu_to_le32(mask) +#define EXT3_CLEAR_RO_COMPAT_FEATURE(sb,mask) \ + EXT3_SB(sb)->s_es->s_feature_ro_compat &= ~cpu_to_le32(mask) +#define EXT3_CLEAR_INCOMPAT_FEATURE(sb,mask) \ + EXT3_SB(sb)->s_es->s_feature_incompat &= ~cpu_to_le32(mask) + +typedef unsigned long long ext3_fsblk_t; +typedef unsigned int ssize_t, tid_t, vm_fault_t, qsize_t, kprojid_t; +typedef unsigned __int64 atomic64_t; + +struct timespec64 { + __u64 tv_sec; /* seconds */ + __u64 tv_nsec; /* nanoseconds */ +}; + +struct fscrypt_str { + unsigned char *name; + __u32 len; +}; + +struct ext3_gd { + ext3_fsblk_t block; + struct ext4_group_desc *gd; + struct buffer_head *bh; +}; + +/* The hash is always the low bits of hash_len */ +#ifdef __LITTLE_ENDIAN + #define HASH_LEN_DECLARE u32 hash; u32 len + #define bytemask_from_count(cnt) (~(~0ul << (cnt)*8)) +#else + #define HASH_LEN_DECLARE u32 len; u32 hash + #define bytemask_from_count(cnt) (~(~0ul >> (cnt)*8)) +#endif + +/* + * "quick string" -- eases parameter passing, but more importantly + * saves "metadata" about the string (ie length and the hash). + * + * hash comes first so it snuggles against d_parent in the + * dentry. + */ +struct qstr { + union { + struct { + HASH_LEN_DECLARE; + }; + u64 hash_len; + }; + const unsigned char *name; +}; + +#define QSTR_INIT(n,l) { { { .len = l } }, .name = n } + +/* + * Ext2 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +enum { + EXT2_FT_UNKNOWN, + EXT2_FT_REG_FILE, + EXT2_FT_DIR, + EXT2_FT_CHRDEV, + EXT2_FT_BLKDEV, + EXT2_FT_FIFO, + EXT2_FT_SOCK, + EXT2_FT_SYMLINK, + EXT2_FT_MAX +}; + +#define EXT3_MAX_REC_LEN ((1<<16)-1) + +int ext3_release_dir (struct inode * inode, struct file * filp); + +/* + * Below follows ext4.h from the Linux 5.0 source code. + * struct ext4_inode_info and struct ext4_sb_info are specialized for + * the Windows driver, other from that there are only small changes + * to be able to use the include file on Windows. + */ + +/* + * The fourth extended filesystem constants/structures + */ + +/* + * with AGGRESSIVE_CHECK allocator runs consistency checks over + * structures. these checks slow things down a lot + */ +#define AGGRESSIVE_CHECK__ + +/* + * with DOUBLE_CHECK defined mballoc creates persistent in-core + * bitmaps, maintains and uses them to check for double allocations + */ +#define DOUBLE_CHECK__ + +/* + * Define EXT4FS_DEBUG to produce debug messages + */ +#undef EXT4FS_DEBUG + +/* + * Debug code + */ +#ifdef EXT4FS_DEBUG +#define ext4_debug(f, a...) \ + do { \ + printk(KERN_DEBUG "EXT4-fs DEBUG (%s, %d): %s:", \ + __FILE__, __LINE__, __func__); \ + printk(KERN_DEBUG f, ## a); \ + } while (0) +#else +#define ext4_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__) +#endif + +/* + * Turn on EXT_DEBUG to get lots of info about extents operations. + */ +#define EXT_DEBUG__ +#ifdef EXT_DEBUG +#define ext_debug(fmt, ...) printk(fmt, ##__VA_ARGS__) +#else +#define ext_debug(fmt, ...) no_printk(fmt, ##__VA_ARGS__) +#endif + +/* data type for block offset of block group */ +typedef int ext4_grpblk_t; + +/* data type for filesystem-wide blocks number */ +typedef unsigned long long ext4_fsblk_t; + +/* data type for file logical block number */ +typedef __u32 ext4_lblk_t; + +/* data type for block group number */ +typedef unsigned int ext4_group_t; + +enum SHIFT_DIRECTION { + SHIFT_LEFT = 0, + SHIFT_RIGHT, +}; + +/* + * Flags used in mballoc's allocation_context flags field. + * + * Also used to show what's going on for debugging purposes when the + * flag field is exported via the traceport interface + */ + +/* prefer goal again. length */ +#define EXT4_MB_HINT_MERGE 0x0001 +/* blocks already reserved */ +#define EXT4_MB_HINT_RESERVED 0x0002 +/* metadata is being allocated */ +#define EXT4_MB_HINT_METADATA 0x0004 +/* first blocks in the file */ +#define EXT4_MB_HINT_FIRST 0x0008 +/* search for the best chunk */ +#define EXT4_MB_HINT_BEST 0x0010 +/* data is being allocated */ +#define EXT4_MB_HINT_DATA 0x0020 +/* don't preallocate (for tails) */ +#define EXT4_MB_HINT_NOPREALLOC 0x0040 +/* allocate for locality group */ +#define EXT4_MB_HINT_GROUP_ALLOC 0x0080 +/* allocate goal blocks or none */ +#define EXT4_MB_HINT_GOAL_ONLY 0x0100 +/* goal is meaningful */ +#define EXT4_MB_HINT_TRY_GOAL 0x0200 +/* blocks already pre-reserved by delayed allocation */ +#define EXT4_MB_DELALLOC_RESERVED 0x0400 +/* We are doing stream allocation */ +#define EXT4_MB_STREAM_ALLOC 0x0800 +/* Use reserved root blocks if needed */ +#define EXT4_MB_USE_ROOT_BLOCKS 0x1000 +/* Use blocks from reserved pool */ +#define EXT4_MB_USE_RESERVED 0x2000 + +struct ext4_allocation_request { + /* target inode for block we're allocating */ + struct inode *inode; + /* how many blocks we want to allocate */ + unsigned int len; + /* logical block in target inode */ + ext4_lblk_t logical; + /* the closest logical allocated block to the left */ + ext4_lblk_t lleft; + /* the closest logical allocated block to the right */ + ext4_lblk_t lright; + /* phys. target (a hint) */ + ext4_fsblk_t goal; + /* phys. block for the closest logical allocated block to the left */ + ext4_fsblk_t pleft; + /* phys. block for the closest logical allocated block to the right */ + ext4_fsblk_t pright; + /* flags. see above EXT4_MB_HINT_* */ + unsigned int flags; +}; + +/* + * Logical to physical block mapping, used by ext4_map_blocks() + * + * This structure is used to pass requests into ext4_map_blocks() as + * well as to store the information returned by ext4_map_blocks(). It + * takes less room on the stack than a struct buffer_head. + */ +#define EXT4_MAP_NEW (1 << BH_New) +#define EXT4_MAP_MAPPED (1 << BH_Mapped) +#define EXT4_MAP_UNWRITTEN (1 << BH_Unwritten) +#define EXT4_MAP_BOUNDARY (1 << BH_Boundary) +#define EXT4_MAP_FLAGS (EXT4_MAP_NEW | EXT4_MAP_MAPPED |\ + EXT4_MAP_UNWRITTEN | EXT4_MAP_BOUNDARY) + +struct ext4_map_blocks { + ext4_fsblk_t m_pblk; + ext4_lblk_t m_lblk; + unsigned int m_len; + unsigned int m_flags; +}; + +/* + * Flags for ext4_io_end->flags + */ +#define EXT4_IO_END_UNWRITTEN 0x0001 + +/* + * For converting unwritten extents on a work queue. 'handle' is used for + * buffered writeback. + */ +typedef struct ext4_io_end { + struct list_head list; /* per-file finished IO list */ + handle_t *handle; /* handle reserved for extent + * conversion */ + struct inode *inode; /* file being written to */ + struct bio *bio; /* Linked list of completed + * bios covering the extent */ + unsigned int flag; /* unwritten or not */ + atomic_t count; /* reference counter */ + loff_t offset; /* offset in the file */ + ssize_t size; /* size of the extent */ +} ext4_io_end_t; + +struct ext4_io_submit { + struct writeback_control *io_wbc; + struct bio *io_bio; + ext4_io_end_t *io_end; + sector_t io_next_block; +}; + +/* + * Special inodes numbers + */ +#define EXT4_BAD_INO 1 /* Bad blocks inode */ +#define EXT4_ROOT_INO 2 /* Root inode */ +#define EXT4_USR_QUOTA_INO 3 /* User quota inode */ +#define EXT4_GRP_QUOTA_INO 4 /* Group quota inode */ +#define EXT4_BOOT_LOADER_INO 5 /* Boot loader inode */ +#define EXT4_UNDEL_DIR_INO 6 /* Undelete directory inode */ +#define EXT4_RESIZE_INO 7 /* Reserved group descriptors inode */ +#define EXT4_JOURNAL_INO 8 /* Journal inode */ + +/* First non-reserved inode for old ext4 filesystems */ +#define EXT4_GOOD_OLD_FIRST_INO 11 + +/* + * Maximal count of links to a file + */ +#define EXT4_LINK_MAX 65000 + +/* + * Macro-instructions used to manage several block sizes + */ +#define EXT4_MIN_BLOCK_SIZE 1024 +#define EXT4_MAX_BLOCK_SIZE 65536 +#define EXT4_MIN_BLOCK_LOG_SIZE 10 +#define EXT4_MAX_BLOCK_LOG_SIZE 16 +#define EXT4_MAX_CLUSTER_LOG_SIZE 30 +#ifdef __KERNEL__ +# define EXT4_BLOCK_SIZE(s) ((s)->s_blocksize) +#else +# define EXT4_BLOCK_SIZE(s) (EXT4_MIN_BLOCK_SIZE << (s)->s_log_block_size) +#endif +#define EXT4_ADDR_PER_BLOCK(s) (EXT4_BLOCK_SIZE(s) / sizeof(__u32)) +#define EXT4_CLUSTER_SIZE(s) (EXT4_BLOCK_SIZE(s) << \ + EXT4_SB(s)->s_cluster_bits) +#ifdef __KERNEL__ +# define EXT4_BLOCK_SIZE_BITS(s) ((s)->s_blocksize_bits) +# define EXT4_CLUSTER_BITS(s) (EXT4_SB(s)->s_cluster_bits) +#else +# define EXT4_BLOCK_SIZE_BITS(s) ((s)->s_log_block_size + 10) +#endif +#ifdef __KERNEL__ +#define EXT4_ADDR_PER_BLOCK_BITS(s) (EXT4_SB(s)->s_addr_per_block_bits) +#define EXT4_INODE_SIZE(s) (EXT4_SB(s)->s_inode_size) +#define EXT4_FIRST_INO(s) (EXT4_SB(s)->s_first_ino) +#else +#define EXT4_INODE_SIZE(s) (((s)->s_rev_level == EXT4_GOOD_OLD_REV) ? \ + EXT4_GOOD_OLD_INODE_SIZE : \ + (s)->s_inode_size) +#define EXT4_FIRST_INO(s) (((s)->s_rev_level == EXT4_GOOD_OLD_REV) ? \ + EXT4_GOOD_OLD_FIRST_INO : \ + (s)->s_first_ino) +#endif +#define EXT4_BLOCK_ALIGN(size, blkbits) ALIGN((size), (1 << (blkbits))) +#define EXT4_MAX_BLOCKS(size, offset, blkbits) \ + ((EXT4_BLOCK_ALIGN(size + offset, blkbits) >> blkbits) - (offset >> \ + blkbits)) + +/* Translate a block number to a cluster number */ +#define EXT4_B2C(sbi, blk) ((blk) >> (sbi)->s_cluster_bits) +/* Translate a cluster number to a block number */ +#define EXT4_C2B(sbi, cluster) ((cluster) << (sbi)->s_cluster_bits) +/* Translate # of blks to # of clusters */ +#define EXT4_NUM_B2C(sbi, blks) (((blks) + (sbi)->s_cluster_ratio - 1) >> \ + (sbi)->s_cluster_bits) +/* Mask out the low bits to get the starting block of the cluster */ +#define EXT4_PBLK_CMASK(s, pblk) ((pblk) & \ + ~((ext4_fsblk_t) (s)->s_cluster_ratio - 1)) +#define EXT4_LBLK_CMASK(s, lblk) ((lblk) & \ + ~((ext4_lblk_t) (s)->s_cluster_ratio - 1)) +/* Get the cluster offset */ +#define EXT4_PBLK_COFF(s, pblk) ((pblk) & \ + ((ext4_fsblk_t) (s)->s_cluster_ratio - 1)) +#define EXT4_LBLK_COFF(s, lblk) ((lblk) & \ + ((ext4_lblk_t) (s)->s_cluster_ratio - 1)) + +/* + * Structure of a blocks group descriptor + */ +struct ext4_group_desc +{ + __le32 bg_block_bitmap_lo; /* Blocks bitmap block */ + __le32 bg_inode_bitmap_lo; /* Inodes bitmap block */ + __le32 bg_inode_table_lo; /* Inodes table block */ + __le16 bg_free_blocks_count_lo;/* Free blocks count */ + __le16 bg_free_inodes_count_lo;/* Free inodes count */ + __le16 bg_used_dirs_count_lo; /* Directories count */ + __le16 bg_flags; /* EXT4_BG_flags (INODE_UNINIT, etc) */ + __le32 bg_exclude_bitmap_lo; /* Exclude bitmap for snapshots */ + __le16 bg_block_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+bbitmap) LE */ + __le16 bg_inode_bitmap_csum_lo;/* crc32c(s_uuid+grp_num+ibitmap) LE */ + __le16 bg_itable_unused_lo; /* Unused inodes count */ + __le16 bg_checksum; /* crc16(sb_uuid+group+desc) */ + __le32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */ + __le32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */ + __le32 bg_inode_table_hi; /* Inodes table block MSB */ + __le16 bg_free_blocks_count_hi;/* Free blocks count MSB */ + __le16 bg_free_inodes_count_hi;/* Free inodes count MSB */ + __le16 bg_used_dirs_count_hi; /* Directories count MSB */ + __le16 bg_itable_unused_hi; /* Unused inodes count MSB */ + __le32 bg_exclude_bitmap_hi; /* Exclude bitmap block MSB */ + __le16 bg_block_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+bbitmap) BE */ + __le16 bg_inode_bitmap_csum_hi;/* crc32c(s_uuid+grp_num+ibitmap) BE */ + __u32 bg_reserved; +}; + +#define EXT4_BG_INODE_BITMAP_CSUM_HI_END \ + (offsetof(struct ext4_group_desc, bg_inode_bitmap_csum_hi) + \ + sizeof(__le16)) +#define EXT4_BG_BLOCK_BITMAP_CSUM_HI_END \ + (offsetof(struct ext4_group_desc, bg_block_bitmap_csum_hi) + \ + sizeof(__le16)) + +/* + * Structure of a flex block group info + */ + +struct flex_groups { + atomic64_t free_clusters; + atomic_t free_inodes; + atomic_t used_dirs; +}; + +#define EXT4_BG_INODE_UNINIT 0x0001 /* Inode table/bitmap not in use */ +#define EXT4_BG_BLOCK_UNINIT 0x0002 /* Block bitmap not in use */ +#define EXT4_BG_INODE_ZEROED 0x0004 /* On-disk itable initialized to zero */ + +/* + * Macro-instructions used to manage group descriptors + */ +#define EXT4_MIN_DESC_SIZE 32 +#define EXT4_MIN_DESC_SIZE_64BIT 64 +#define EXT4_MAX_DESC_SIZE EXT4_MIN_BLOCK_SIZE +#define EXT4_DESC_SIZE(s) (EXT4_SB(s)->s_desc_size) +#ifdef __KERNEL__ +# define EXT4_BLOCKS_PER_GROUP(s) (EXT4_SB(s)->s_blocks_per_group) +# define EXT4_CLUSTERS_PER_GROUP(s) (EXT4_SB(s)->s_clusters_per_group) +# define EXT4_DESC_PER_BLOCK(s) (EXT4_SB(s)->s_desc_per_block) +# define EXT4_INODES_PER_GROUP(s) (EXT4_SB(s)->s_inodes_per_group) +# define EXT4_DESC_PER_BLOCK_BITS(s) (EXT4_SB(s)->s_desc_per_block_bits) +#else +# define EXT4_BLOCKS_PER_GROUP(s) ((s)->s_blocks_per_group) +# define EXT4_DESC_PER_BLOCK(s) (EXT4_BLOCK_SIZE(s) / EXT4_DESC_SIZE(s)) +# define EXT4_INODES_PER_GROUP(s) ((s)->s_inodes_per_group) +#endif + +/* + * Constants relative to the data blocks + */ +#define EXT4_NDIR_BLOCKS 12 +#define EXT4_IND_BLOCK EXT4_NDIR_BLOCKS +#define EXT4_DIND_BLOCK (EXT4_IND_BLOCK + 1) +#define EXT4_TIND_BLOCK (EXT4_DIND_BLOCK + 1) +#define EXT4_N_BLOCKS (EXT4_TIND_BLOCK + 1) + +/* + * Inode flags + */ +#define EXT4_SECRM_FL 0x00000001 /* Secure deletion */ +#define EXT4_UNRM_FL 0x00000002 /* Undelete */ +#define EXT4_COMPR_FL 0x00000004 /* Compress file */ +#define EXT4_SYNC_FL 0x00000008 /* Synchronous updates */ +#define EXT4_IMMUTABLE_FL 0x00000010 /* Immutable file */ +#define EXT4_APPEND_FL 0x00000020 /* writes to file may only append */ +#define EXT4_NODUMP_FL 0x00000040 /* do not dump file */ +#define EXT4_NOATIME_FL 0x00000080 /* do not update atime */ +/* Reserved for compression usage... */ +#define EXT4_DIRTY_FL 0x00000100 +#define EXT4_COMPRBLK_FL 0x00000200 /* One or more compressed clusters */ +#define EXT4_NOCOMPR_FL 0x00000400 /* Don't compress */ + /* nb: was previously EXT2_ECOMPR_FL */ +#define EXT4_ENCRYPT_FL 0x00000800 /* encrypted file */ +/* End compression flags --- maybe not all used */ +#define EXT4_INDEX_FL 0x00001000 /* hash-indexed directory */ +#define EXT4_IMAGIC_FL 0x00002000 /* AFS directory */ +#define EXT4_JOURNAL_DATA_FL 0x00004000 /* file data should be journaled */ +#define EXT4_NOTAIL_FL 0x00008000 /* file tail should not be merged */ +#define EXT4_DIRSYNC_FL 0x00010000 /* dirsync behaviour (directories only) */ +#define EXT4_TOPDIR_FL 0x00020000 /* Top of directory hierarchies*/ +#define EXT4_HUGE_FILE_FL 0x00040000 /* Set to each huge file */ +#define EXT4_EXTENTS_FL 0x00080000 /* Inode uses extents */ +#define EXT4_EA_INODE_FL 0x00200000 /* Inode used for large EA */ +#define EXT4_EOFBLOCKS_FL 0x00400000 /* Blocks allocated beyond EOF */ +#define EXT4_INLINE_DATA_FL 0x10000000 /* Inode has inline data. */ +#define EXT4_PROJINHERIT_FL 0x20000000 /* Create with parents projid */ +#define EXT4_RESERVED_FL 0x80000000 /* reserved for ext4 lib */ + +#define EXT4_FL_USER_VISIBLE 0x304BDFFF /* User visible flags */ +#define EXT4_FL_USER_MODIFIABLE 0x204BC0FF /* User modifiable flags */ + +/* Flags we can manipulate with through EXT4_IOC_FSSETXATTR */ +#define EXT4_FL_XFLAG_VISIBLE (EXT4_SYNC_FL | \ + EXT4_IMMUTABLE_FL | \ + EXT4_APPEND_FL | \ + EXT4_NODUMP_FL | \ + EXT4_NOATIME_FL | \ + EXT4_PROJINHERIT_FL) + +/* Flags that should be inherited by new inodes from their parent. */ +#define EXT4_FL_INHERITED (EXT4_SECRM_FL | EXT4_UNRM_FL | EXT4_COMPR_FL |\ + EXT4_SYNC_FL | EXT4_NODUMP_FL | EXT4_NOATIME_FL |\ + EXT4_NOCOMPR_FL | EXT4_JOURNAL_DATA_FL |\ + EXT4_NOTAIL_FL | EXT4_DIRSYNC_FL |\ + EXT4_PROJINHERIT_FL) + +/* Flags that are appropriate for regular files (all but dir-specific ones). */ +#define EXT4_REG_FLMASK (~(EXT4_DIRSYNC_FL | EXT4_TOPDIR_FL)) + +/* Flags that are appropriate for non-directories/regular files. */ +#define EXT4_OTHER_FLMASK (EXT4_NODUMP_FL | EXT4_NOATIME_FL) + +/* Mask out flags that are inappropriate for the given type of inode. */ +static inline __u32 ext4_mask_flags(umode_t mode, __u32 flags) +{ + if (S_ISDIR(mode)) + return flags; + else if (S_ISREG(mode)) + return flags & EXT4_REG_FLMASK; + else + return flags & EXT4_OTHER_FLMASK; +} + +/* + * Inode flags used for atomic set/get + */ +enum { + EXT4_INODE_SECRM = 0, /* Secure deletion */ + EXT4_INODE_UNRM = 1, /* Undelete */ + EXT4_INODE_COMPR = 2, /* Compress file */ + EXT4_INODE_SYNC = 3, /* Synchronous updates */ + EXT4_INODE_IMMUTABLE = 4, /* Immutable file */ + EXT4_INODE_APPEND = 5, /* writes to file may only append */ + EXT4_INODE_NODUMP = 6, /* do not dump file */ + EXT4_INODE_NOATIME = 7, /* do not update atime */ +/* Reserved for compression usage... */ + EXT4_INODE_DIRTY = 8, + EXT4_INODE_COMPRBLK = 9, /* One or more compressed clusters */ + EXT4_INODE_NOCOMPR = 10, /* Don't compress */ + EXT4_INODE_ENCRYPT = 11, /* Encrypted file */ +/* End compression flags --- maybe not all used */ + EXT4_INODE_INDEX = 12, /* hash-indexed directory */ + EXT4_INODE_IMAGIC = 13, /* AFS directory */ + EXT4_INODE_JOURNAL_DATA = 14, /* file data should be journaled */ + EXT4_INODE_NOTAIL = 15, /* file tail should not be merged */ + EXT4_INODE_DIRSYNC = 16, /* dirsync behaviour (directories only) */ + EXT4_INODE_TOPDIR = 17, /* Top of directory hierarchies*/ + EXT4_INODE_HUGE_FILE = 18, /* Set to each huge file */ + EXT4_INODE_EXTENTS = 19, /* Inode uses extents */ + EXT4_INODE_EA_INODE = 21, /* Inode used for large EA */ + EXT4_INODE_EOFBLOCKS = 22, /* Blocks allocated beyond EOF */ + EXT4_INODE_INLINE_DATA = 28, /* Data in inode. */ + EXT4_INODE_PROJINHERIT = 29, /* Create with parents projid */ + EXT4_INODE_RESERVED = 31, /* reserved for ext4 lib */ +}; + +/* + * Since it's pretty easy to mix up bit numbers and hex values, we use a + * build-time check to make sure that EXT4_XXX_FL is consistent with respect to + * EXT4_INODE_XXX. If all is well, the macros will be dropped, so, it won't cost + * any extra space in the compiled kernel image, otherwise, the build will fail. + * It's important that these values are the same, since we are using + * EXT4_INODE_XXX to test for flag values, but EXT4_XXX_FL must be consistent + * with the values of FS_XXX_FL defined in include/linux/fs.h and the on-disk + * values found in ext2, ext3 and ext4 filesystems, and of course the values + * defined in e2fsprogs. + * + * It's not paranoia if the Murphy's Law really *is* out to get you. :-) + */ +#define TEST_FLAG_VALUE(FLAG) (EXT4_##FLAG##_FL == (1 << EXT4_INODE_##FLAG)) +#define CHECK_FLAG_VALUE(FLAG) BUILD_BUG_ON(!TEST_FLAG_VALUE(FLAG)) + +static inline void ext4_check_flag_values(void) +{ + CHECK_FLAG_VALUE(SECRM); + CHECK_FLAG_VALUE(UNRM); + CHECK_FLAG_VALUE(COMPR); + CHECK_FLAG_VALUE(SYNC); + CHECK_FLAG_VALUE(IMMUTABLE); + CHECK_FLAG_VALUE(APPEND); + CHECK_FLAG_VALUE(NODUMP); + CHECK_FLAG_VALUE(NOATIME); + CHECK_FLAG_VALUE(DIRTY); + CHECK_FLAG_VALUE(COMPRBLK); + CHECK_FLAG_VALUE(NOCOMPR); + CHECK_FLAG_VALUE(ENCRYPT); + CHECK_FLAG_VALUE(INDEX); + CHECK_FLAG_VALUE(IMAGIC); + CHECK_FLAG_VALUE(JOURNAL_DATA); + CHECK_FLAG_VALUE(NOTAIL); + CHECK_FLAG_VALUE(DIRSYNC); + CHECK_FLAG_VALUE(TOPDIR); + CHECK_FLAG_VALUE(HUGE_FILE); + CHECK_FLAG_VALUE(EXTENTS); + CHECK_FLAG_VALUE(EA_INODE); + CHECK_FLAG_VALUE(EOFBLOCKS); + CHECK_FLAG_VALUE(INLINE_DATA); + CHECK_FLAG_VALUE(PROJINHERIT); + CHECK_FLAG_VALUE(RESERVED); +} + +/* Used to pass group descriptor data when online resize is done */ +struct ext4_new_group_input { + __u32 group; /* Group number for this data */ + __u64 block_bitmap; /* Absolute block number of block bitmap */ + __u64 inode_bitmap; /* Absolute block number of inode bitmap */ + __u64 inode_table; /* Absolute block number of inode table start */ + __u32 blocks_count; /* Total number of blocks in this group */ + __u16 reserved_blocks; /* Number of reserved blocks in this group */ + __u16 unused; +}; + +#if defined(__KERNEL__) && defined(CONFIG_COMPAT) +struct compat_ext4_new_group_input { + __u32 group; + compat_u64 block_bitmap; + compat_u64 inode_bitmap; + compat_u64 inode_table; + __u32 blocks_count; + __u16 reserved_blocks; + __u16 unused; +}; +#endif + +/* The struct ext4_new_group_input in kernel space, with free_blocks_count */ +struct ext4_new_group_data { + __u32 group; + __u64 block_bitmap; + __u64 inode_bitmap; + __u64 inode_table; + __u32 blocks_count; + __u16 reserved_blocks; + __u16 mdata_blocks; + __u32 free_clusters_count; +}; + +/* Indexes used to index group tables in ext4_new_group_data */ +enum { + BLOCK_BITMAP = 0, /* block bitmap */ + INODE_BITMAP, /* inode bitmap */ + INODE_TABLE, /* inode tables */ + GROUP_TABLE_COUNT, +}; + +/* + * Flags used by ext4_map_blocks() + */ + /* Allocate any needed blocks and/or convert an unwritten + extent to be an initialized ext4 */ +#define EXT4_GET_BLOCKS_CREATE 0x0001 + /* Request the creation of an unwritten extent */ +#define EXT4_GET_BLOCKS_UNWRIT_EXT 0x0002 +#define EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT (EXT4_GET_BLOCKS_UNWRIT_EXT|\ + EXT4_GET_BLOCKS_CREATE) + /* Caller is from the delayed allocation writeout path + * finally doing the actual allocation of delayed blocks */ +#define EXT4_GET_BLOCKS_DELALLOC_RESERVE 0x0004 + /* caller is from the direct IO path, request to creation of an + unwritten extents if not allocated, split the unwritten + extent if blocks has been preallocated already*/ +#define EXT4_GET_BLOCKS_PRE_IO 0x0008 +#define EXT4_GET_BLOCKS_CONVERT 0x0010 +#define EXT4_GET_BLOCKS_IO_CREATE_EXT (EXT4_GET_BLOCKS_PRE_IO|\ + EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT) + /* Convert extent to initialized after IO complete */ +#define EXT4_GET_BLOCKS_IO_CONVERT_EXT (EXT4_GET_BLOCKS_CONVERT|\ + EXT4_GET_BLOCKS_CREATE_UNWRIT_EXT) + /* Eventual metadata allocation (due to growing extent tree) + * should not fail, so try to use reserved blocks for that.*/ +#define EXT4_GET_BLOCKS_METADATA_NOFAIL 0x0020 + /* Don't normalize allocation size (used for fallocate) */ +#define EXT4_GET_BLOCKS_NO_NORMALIZE 0x0040 + /* Request will not result in inode size update (user for fallocate) */ +#define EXT4_GET_BLOCKS_KEEP_SIZE 0x0080 + /* Convert written extents to unwritten */ +#define EXT4_GET_BLOCKS_CONVERT_UNWRITTEN 0x0100 + /* Write zeros to newly created written extents */ +#define EXT4_GET_BLOCKS_ZERO 0x0200 +#define EXT4_GET_BLOCKS_CREATE_ZERO (EXT4_GET_BLOCKS_CREATE |\ + EXT4_GET_BLOCKS_ZERO) + /* Caller will submit data before dropping transaction handle. This + * allows jbd2 to avoid submitting data before commit. */ +#define EXT4_GET_BLOCKS_IO_SUBMIT 0x0400 + +/* + * The bit position of these flags must not overlap with any of the + * EXT4_GET_BLOCKS_*. They are used by ext4_find_extent(), + * read_extent_tree_block(), ext4_split_extent_at(), + * ext4_ext_insert_extent(), and ext4_ext_create_new_leaf(). + * EXT4_EX_NOCACHE is used to indicate that the we shouldn't be + * caching the extents when reading from the extent tree while a + * truncate or punch hole operation is in progress. + */ +#define EXT4_EX_NOCACHE 0x40000000 +#define EXT4_EX_FORCE_CACHE 0x20000000 + +/* + * Flags used by ext4_free_blocks + */ +#define EXT4_FREE_BLOCKS_METADATA 0x0001 +#define EXT4_FREE_BLOCKS_FORGET 0x0002 +#define EXT4_FREE_BLOCKS_VALIDATED 0x0004 +#define EXT4_FREE_BLOCKS_NO_QUOT_UPDATE 0x0008 +#define EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER 0x0010 +#define EXT4_FREE_BLOCKS_NOFREE_LAST_CLUSTER 0x0020 +#define EXT4_FREE_BLOCKS_RERESERVE_CLUSTER 0x0040 + +/* + * ioctl commands + */ +#define EXT4_IOC_GETFLAGS FS_IOC_GETFLAGS +#define EXT4_IOC_SETFLAGS FS_IOC_SETFLAGS +#define EXT4_IOC_GETVERSION _IOR('f', 3, long) +#define EXT4_IOC_SETVERSION _IOW('f', 4, long) +#define EXT4_IOC_GETVERSION_OLD FS_IOC_GETVERSION +#define EXT4_IOC_SETVERSION_OLD FS_IOC_SETVERSION +#define EXT4_IOC_GETRSVSZ _IOR('f', 5, long) +#define EXT4_IOC_SETRSVSZ _IOW('f', 6, long) +#define EXT4_IOC_GROUP_EXTEND _IOW('f', 7, unsigned long) +#define EXT4_IOC_GROUP_ADD _IOW('f', 8, struct ext4_new_group_input) +#define EXT4_IOC_MIGRATE _IO('f', 9) + /* note ioctl 10 reserved for an early version of the FIEMAP ioctl */ + /* note ioctl 11 reserved for filesystem-independent FIEMAP ioctl */ +#define EXT4_IOC_ALLOC_DA_BLKS _IO('f', 12) +#define EXT4_IOC_MOVE_EXT _IOWR('f', 15, struct move_extent) +#define EXT4_IOC_RESIZE_FS _IOW('f', 16, __u64) +#define EXT4_IOC_SWAP_BOOT _IO('f', 17) +#define EXT4_IOC_PRECACHE_EXTENTS _IO('f', 18) +#define EXT4_IOC_SET_ENCRYPTION_POLICY FS_IOC_SET_ENCRYPTION_POLICY +#define EXT4_IOC_GET_ENCRYPTION_PWSALT FS_IOC_GET_ENCRYPTION_PWSALT +#define EXT4_IOC_GET_ENCRYPTION_POLICY FS_IOC_GET_ENCRYPTION_POLICY + +#define EXT4_IOC_FSGETXATTR FS_IOC_FSGETXATTR +#define EXT4_IOC_FSSETXATTR FS_IOC_FSSETXATTR + +#define EXT4_IOC_SHUTDOWN _IOR ('X', 125, __u32) + +/* + * Flags for going down operation + */ +#define EXT4_GOING_FLAGS_DEFAULT 0x0 /* going down */ +#define EXT4_GOING_FLAGS_LOGFLUSH 0x1 /* flush log but not data */ +#define EXT4_GOING_FLAGS_NOLOGFLUSH 0x2 /* don't flush log nor data */ + + +#if defined(__KERNEL__) && defined(CONFIG_COMPAT) +/* + * ioctl commands in 32 bit emulation + */ +#define EXT4_IOC32_GETFLAGS FS_IOC32_GETFLAGS +#define EXT4_IOC32_SETFLAGS FS_IOC32_SETFLAGS +#define EXT4_IOC32_GETVERSION _IOR('f', 3, int) +#define EXT4_IOC32_SETVERSION _IOW('f', 4, int) +#define EXT4_IOC32_GETRSVSZ _IOR('f', 5, int) +#define EXT4_IOC32_SETRSVSZ _IOW('f', 6, int) +#define EXT4_IOC32_GROUP_EXTEND _IOW('f', 7, unsigned int) +#define EXT4_IOC32_GROUP_ADD _IOW('f', 8, struct compat_ext4_new_group_input) +#define EXT4_IOC32_GETVERSION_OLD FS_IOC32_GETVERSION +#define EXT4_IOC32_SETVERSION_OLD FS_IOC32_SETVERSION +#endif + +/* Max physical block we can address w/o extents */ +#define EXT4_MAX_BLOCK_FILE_PHYS 0xFFFFFFFF + +/* Max logical block we can support */ +#define EXT4_MAX_LOGICAL_BLOCK 0xFFFFFFFF + +/* + * Structure of an inode on the disk + */ +struct ext4_inode { + __le16 i_mode; /* File mode */ + __le16 i_uid; /* Low 16 bits of Owner Uid */ + __le32 i_size_lo; /* Size in bytes */ + __le32 i_atime; /* Access time */ + __le32 i_ctime; /* Inode Change time */ + __le32 i_mtime; /* Modification time */ + __le32 i_dtime; /* Deletion Time */ + __le16 i_gid; /* Low 16 bits of Group Id */ + __le16 i_links_count; /* Links count */ + __le32 i_blocks_lo; /* Blocks count */ + __le32 i_flags; /* File flags */ + union { + struct { + __le32 l_i_version; + } linux1; + struct { + __u32 h_i_translator; + } hurd1; + struct { + __u32 m_i_reserved1; + } masix1; + } osd1; /* OS dependent 1 */ + __le32 i_block[EXT4_N_BLOCKS];/* Pointers to blocks */ + __le32 i_generation; /* File version (for NFS) */ + __le32 i_file_acl_lo; /* File ACL */ + __le32 i_size_high; + __le32 i_obso_faddr; /* Obsoleted fragment address */ + union { + struct { + __le16 l_i_blocks_high; /* were l_i_reserved1 */ + __le16 l_i_file_acl_high; + __le16 l_i_uid_high; /* these 2 fields */ + __le16 l_i_gid_high; /* were reserved2[0] */ + __le16 l_i_checksum_lo;/* crc32c(uuid+inum+inode) LE */ + __le16 l_i_reserved; + } linux2; + struct { + __le16 h_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */ + __u16 h_i_mode_high; + __u16 h_i_uid_high; + __u16 h_i_gid_high; + __u32 h_i_author; + } hurd2; + struct { + __le16 h_i_reserved1; /* Obsoleted fragment number/size which are removed in ext4 */ + __le16 m_i_file_acl_high; + __u32 m_i_reserved2[2]; + } masix2; + } osd2; /* OS dependent 2 */ + __le16 i_extra_isize; + __le16 i_checksum_hi; /* crc32c(uuid+inum+inode) BE */ + __le32 i_ctime_extra; /* extra Change time (nsec << 2 | epoch) */ + __le32 i_mtime_extra; /* extra Modification time(nsec << 2 | epoch) */ + __le32 i_atime_extra; /* extra Access time (nsec << 2 | epoch) */ + __le32 i_crtime; /* File Creation time */ + __le32 i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */ + __le32 i_version_hi; /* high 32 bits for 64-bit version */ + __le32 i_projid; /* Project ID */ +}; + +struct move_extent { + __u32 reserved; /* should be zero */ + __u32 donor_fd; /* donor file descriptor */ + __u64 orig_start; /* logical start offset in block for orig */ + __u64 donor_start; /* logical start offset in block for donor */ + __u64 len; /* block length to be moved */ + __u64 moved_len; /* moved block length */ +}; + +#define EXT4_EPOCH_BITS 2 +#define EXT4_EPOCH_MASK ((1 << EXT4_EPOCH_BITS) - 1) +#define EXT4_NSEC_MASK (~0UL << EXT4_EPOCH_BITS) + +/* + * Extended fields will fit into an inode if the filesystem was formatted + * with large inodes (-I 256 or larger) and there are not currently any EAs + * consuming all of the available space. For new inodes we always reserve + * enough space for the kernel's known extended fields, but for inodes + * created with an old kernel this might not have been the case. None of + * the extended inode fields is critical for correct filesystem operation. + * This macro checks if a certain field fits in the inode. Note that + * inode-size = GOOD_OLD_INODE_SIZE + i_extra_isize + */ +/*#define EXT4_FITS_IN_INODE(ext4_inode, einode, field) \ + ((offsetof(typeof(*ext4_inode), field) + \ + sizeof((ext4_inode)->field)) \ + <= (EXT4_GOOD_OLD_INODE_SIZE + \ + (einode)->i_extra_isize))*/ +#define EXT4_FITS_IN_INODE(ext4_inode, einode, field) 1 +/* + * We use an encoding that preserves the times for extra epoch "00": + * + * extra msb of adjust for signed + * epoch 32-bit 32-bit tv_sec to + * bits time decoded 64-bit tv_sec 64-bit tv_sec valid time range + * 0 0 1 -0x80000000..-0x00000001 0x000000000 1901-12-13..1969-12-31 + * 0 0 0 0x000000000..0x07fffffff 0x000000000 1970-01-01..2038-01-19 + * 0 1 1 0x080000000..0x0ffffffff 0x100000000 2038-01-19..2106-02-07 + * 0 1 0 0x100000000..0x17fffffff 0x100000000 2106-02-07..2174-02-25 + * 1 0 1 0x180000000..0x1ffffffff 0x200000000 2174-02-25..2242-03-16 + * 1 0 0 0x200000000..0x27fffffff 0x200000000 2242-03-16..2310-04-04 + * 1 1 1 0x280000000..0x2ffffffff 0x300000000 2310-04-04..2378-04-22 + * 1 1 0 0x300000000..0x37fffffff 0x300000000 2378-04-22..2446-05-10 + * + * Note that previous versions of the kernel on 64-bit systems would + * incorrectly use extra epoch bits 1,1 for dates between 1901 and + * 1970. e2fsck will correct this, assuming that it is run on the + * affected filesystem before 2242. + */ + +static inline __le32 ext4_encode_extra_time(struct timespec64 *time) +{ + __u32 extra =((time->tv_sec - (__s32)time->tv_sec) >> 32) & EXT4_EPOCH_MASK; + return cpu_to_le32(extra | (time->tv_nsec << EXT4_EPOCH_BITS)); +} + +static inline void ext4_decode_extra_time(struct timespec64 *time, + __le32 extra) +{ + if (unlikely(extra & cpu_to_le32(EXT4_EPOCH_MASK))) { + +#if 1 + /* Handle legacy encoding of pre-1970 dates with epoch + * bits 1,1. (This backwards compatibility may be removed + * at the discretion of the ext4 developers.) + */ + __u64 extra_bits = le32_to_cpu(extra) & EXT4_EPOCH_MASK; + if (extra_bits == 3 && ((time->tv_sec) & 0x80000000) != 0) + extra_bits = 0; + time->tv_sec += extra_bits << 32; +#else + time->tv_sec += (__u64)(le32_to_cpu(extra) & EXT4_EPOCH_MASK) << 32; +#endif + } + time->tv_nsec = (le32_to_cpu(extra) & EXT4_NSEC_MASK) >> EXT4_EPOCH_BITS; +} + +#define EXT4_INODE_SET_XTIME(xtime, inode, raw_inode) \ +do { \ + (raw_inode)->xtime = cpu_to_le32((inode)->xtime.tv_sec); \ + if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) {\ + (raw_inode)->xtime ## _extra = \ + ext4_encode_extra_time(&(inode)->xtime); \ + } \ +} while (0) + +#define EXT4_EINODE_SET_XTIME(xtime, einode, raw_inode) \ +do { \ + if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime)) \ + (raw_inode)->xtime = cpu_to_le32((einode)->xtime.tv_sec); \ + if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra)) \ + (raw_inode)->xtime ## _extra = \ + ext4_encode_extra_time(&(einode)->xtime); \ +} while (0) + +#define EXT4_INODE_GET_XTIME(xtime, inode, raw_inode) \ +do { \ + (inode)->xtime.tv_sec = (signed)le32_to_cpu((raw_inode)->xtime); \ + if (EXT4_FITS_IN_INODE(raw_inode, EXT4_I(inode), xtime ## _extra)) { \ + ext4_decode_extra_time(&(inode)->xtime, \ + raw_inode->xtime ## _extra); \ + } \ + else \ + (inode)->xtime.tv_nsec = 0; \ +} while (0) + + +#define EXT4_EINODE_GET_XTIME(xtime, einode, raw_inode) \ +do { \ + if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime)) \ + (einode)->xtime.tv_sec = \ + (signed)le32_to_cpu((raw_inode)->xtime); \ + else \ + (einode)->xtime.tv_sec = 0; \ + if (EXT4_FITS_IN_INODE(raw_inode, einode, xtime ## _extra)) \ + ext4_decode_extra_time(&(einode)->xtime, \ + raw_inode->xtime ## _extra); \ + else \ + (einode)->xtime.tv_nsec = 0; \ +} while (0) + +#define i_disk_version osd1.linux1.l_i_version + +#if defined(__KERNEL__) || defined(__linux__) +#define i_reserved1 osd1.linux1.l_i_reserved1 +#define i_file_acl_high osd2.linux2.l_i_file_acl_high +#define i_blocks_high osd2.linux2.l_i_blocks_high +#define i_uid_low i_uid +#define i_gid_low i_gid +#define i_uid_high osd2.linux2.l_i_uid_high +#define i_gid_high osd2.linux2.l_i_gid_high +#define i_checksum_lo osd2.linux2.l_i_checksum_lo + +#elif defined(__GNU__) + +#define i_translator osd1.hurd1.h_i_translator +#define i_uid_high osd2.hurd2.h_i_uid_high +#define i_gid_high osd2.hurd2.h_i_gid_high +#define i_author osd2.hurd2.h_i_author + +#elif defined(__masix__) + +#define i_reserved1 osd1.masix1.m_i_reserved1 +#define i_file_acl_high osd2.masix2.m_i_file_acl_high +#define i_reserved2 osd2.masix2.m_i_reserved2 + +#endif /* defined(__KERNEL__) || defined(__linux__) */ + +//#include "extents_status.h" + +/* + * Lock subclasses for i_data_sem in the ext4_inode_info structure. + * + * These are needed to avoid lockdep false positives when we need to + * allocate blocks to the quota inode during ext4_map_blocks(), while + * holding i_data_sem for a normal (non-quota) inode. Since we don't + * do quota tracking for the quota inode, this avoids deadlock (as + * well as infinite recursion, since it isn't turtles all the way + * down...) + * + * I_DATA_SEM_NORMAL - Used for most inodes + * I_DATA_SEM_OTHER - Used by move_inode.c for the second normal inode + * where the second inode has larger inode number + * than the first + * I_DATA_SEM_QUOTA - Used for quota inodes only + */ +enum { + I_DATA_SEM_NORMAL = 0, + I_DATA_SEM_OTHER, + I_DATA_SEM_QUOTA, +}; + + +/* + * fourth extended file system inode data in memory + */ +struct ext4_inode_info { + __u32 i_size_lo; /* Size in bytes */ + __u32 i_blocks_lo; /* Blocks count */ + +#if (BITS_PER_LONG < 64) + unsigned long i_state_flags; /* Dynamic state flags */ +#endif + unsigned long i_flags; + + /* Precomputed uuid+inum+igen checksum for seeding inode checksums */ + __u32 i_csum_seed; +}; + +/* + * File system states + */ +#define EXT4_VALID_FS 0x0001 /* Unmounted cleanly */ +#define EXT4_ERROR_FS 0x0002 /* Errors detected */ +#define EXT4_ORPHAN_FS 0x0004 /* Orphans being recovered */ + +/* + * Misc. filesystem flags + */ +#define EXT2_FLAGS_SIGNED_HASH 0x0001 /* Signed dirhash in use */ +#define EXT2_FLAGS_UNSIGNED_HASH 0x0002 /* Unsigned dirhash in use */ +#define EXT2_FLAGS_TEST_FILESYS 0x0004 /* to test development code */ + +/* + * Mount flags set via mount options or defaults + */ +#define EXT4_MOUNT_NO_MBCACHE 0x00001 /* Do not use mbcache */ +#define EXT4_MOUNT_GRPID 0x00004 /* Create files with directory's group */ +#define EXT4_MOUNT_DEBUG 0x00008 /* Some debugging messages */ +#define EXT4_MOUNT_ERRORS_CONT 0x00010 /* Continue on errors */ +#define EXT4_MOUNT_ERRORS_RO 0x00020 /* Remount fs ro on errors */ +#define EXT4_MOUNT_ERRORS_PANIC 0x00040 /* Panic on errors */ +#define EXT4_MOUNT_ERRORS_MASK 0x00070 +#define EXT4_MOUNT_MINIX_DF 0x00080 /* Mimics the Minix statfs */ +#define EXT4_MOUNT_NOLOAD 0x00100 /* Don't use existing journal*/ +#ifdef CONFIG_FS_DAX +#define EXT4_MOUNT_DAX 0x00200 /* Direct Access */ +#else +#define EXT4_MOUNT_DAX 0 +#endif +#define EXT4_MOUNT_DATA_FLAGS 0x00C00 /* Mode for data writes: */ +#define EXT4_MOUNT_JOURNAL_DATA 0x00400 /* Write data to journal */ +#define EXT4_MOUNT_ORDERED_DATA 0x00800 /* Flush data before commit */ +#define EXT4_MOUNT_WRITEBACK_DATA 0x00C00 /* No data ordering */ +#define EXT4_MOUNT_UPDATE_JOURNAL 0x01000 /* Update the journal format */ +#define EXT4_MOUNT_NO_UID32 0x02000 /* Disable 32-bit UIDs */ +#define EXT4_MOUNT_XATTR_USER 0x04000 /* Extended user attributes */ +#define EXT4_MOUNT_POSIX_ACL 0x08000 /* POSIX Access Control Lists */ +#define EXT4_MOUNT_NO_AUTO_DA_ALLOC 0x10000 /* No auto delalloc mapping */ +#define EXT4_MOUNT_BARRIER 0x20000 /* Use block barriers */ +#define EXT4_MOUNT_QUOTA 0x40000 /* Some quota option set */ +#define EXT4_MOUNT_USRQUOTA 0x80000 /* "old" user quota, + * enable enforcement for hidden + * quota files */ +#define EXT4_MOUNT_GRPQUOTA 0x100000 /* "old" group quota, enable + * enforcement for hidden quota + * files */ +#define EXT4_MOUNT_PRJQUOTA 0x200000 /* Enable project quota + * enforcement */ +#define EXT4_MOUNT_DIOREAD_NOLOCK 0x400000 /* Enable support for dio read nolocking */ +#define EXT4_MOUNT_JOURNAL_CHECKSUM 0x800000 /* Journal checksums */ +#define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT 0x1000000 /* Journal Async Commit */ +#define EXT4_MOUNT_WARN_ON_ERROR 0x2000000 /* Trigger WARN_ON on error */ +#define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */ +#define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */ +#define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */ +#define EXT4_MOUNT_DISCARD 0x40000000 /* Issue DISCARD requests */ +#define EXT4_MOUNT_INIT_INODE_TABLE 0x80000000 /* Initialize uninitialized itables */ + +/* + * Mount flags set either automatically (could not be set by mount option) + * based on per file system feature or property or in special cases such as + * distinguishing between explicit mount option definition and default. + */ +#define EXT4_MOUNT2_EXPLICIT_DELALLOC 0x00000001 /* User explicitly + specified delalloc */ +#define EXT4_MOUNT2_STD_GROUP_SIZE 0x00000002 /* We have standard group + size of blocksize * 8 + blocks */ +#define EXT4_MOUNT2_HURD_COMPAT 0x00000004 /* Support HURD-castrated + file systems */ + +#define EXT4_MOUNT2_EXPLICIT_JOURNAL_CHECKSUM 0x00000008 /* User explicitly + specified journal checksum */ + +#define clear_opt(sb, opt) EXT4_SB(sb)->s_mount_opt &= \ + ~EXT4_MOUNT_##opt +#define set_opt(sb, opt) EXT4_SB(sb)->s_mount_opt |= \ + EXT4_MOUNT_##opt +#define test_opt(sb, opt) (EXT4_SB(sb)->s_mount_opt & \ + EXT4_MOUNT_##opt) + +#define clear_opt2(sb, opt) EXT4_SB(sb)->s_mount_opt2 &= \ + ~EXT4_MOUNT2_##opt +#define set_opt2(sb, opt) EXT4_SB(sb)->s_mount_opt2 |= \ + EXT4_MOUNT2_##opt +#define test_opt2(sb, opt) (EXT4_SB(sb)->s_mount_opt2 & \ + EXT4_MOUNT2_##opt) + +#define ext4_test_and_set_bit __test_and_set_bit_le +//#define ext4_set_bit __set_bit_le +#define ext4_set_bit_atomic ext2_set_bit_atomic +#define ext4_test_and_clear_bit __test_and_clear_bit_le +#define ext4_clear_bit __clear_bit_le +#define ext4_clear_bit_atomic ext2_clear_bit_atomic +#define ext4_test_bit test_bit_le +#define ext4_find_next_zero_bit find_next_zero_bit_le +#define ext4_find_next_bit find_next_bit_le + +extern void ext4_set_bits(void *bm, int cur, int len); + +/* + * Maximal mount counts between two filesystem checks + */ +#define EXT4_DFL_MAX_MNT_COUNT 20 /* Allow 20 mounts */ +#define EXT4_DFL_CHECKINTERVAL 0 /* Don't use interval check */ + +/* + * Behaviour when detecting errors + */ +#define EXT4_ERRORS_CONTINUE 1 /* Continue execution */ +#define EXT4_ERRORS_RO 2 /* Remount fs read-only */ +#define EXT4_ERRORS_PANIC 3 /* Panic */ +#define EXT4_ERRORS_DEFAULT EXT4_ERRORS_CONTINUE + +/* Metadata checksum algorithm codes */ +#define EXT4_CRC32C_CHKSUM 1 + +/* + * Structure of the super block + */ +struct ext4_super_block { +/*00*/ __le32 s_inodes_count; /* Inodes count */ + __le32 s_blocks_count_lo; /* Blocks count */ + __le32 s_r_blocks_count_lo; /* Reserved blocks count */ + __le32 s_free_blocks_count_lo; /* Free blocks count */ +/*10*/ __le32 s_free_inodes_count; /* Free inodes count */ + __le32 s_first_data_block; /* First Data Block */ + __le32 s_log_block_size; /* Block size */ + __le32 s_log_cluster_size; /* Allocation cluster size */ +/*20*/ __le32 s_blocks_per_group; /* # Blocks per group */ + __le32 s_clusters_per_group; /* # Clusters per group */ + __le32 s_inodes_per_group; /* # Inodes per group */ + __le32 s_mtime; /* Mount time */ +/*30*/ __le32 s_wtime; /* Write time */ + __le16 s_mnt_count; /* Mount count */ + __le16 s_max_mnt_count; /* Maximal mount count */ + __le16 s_magic; /* Magic signature */ + __le16 s_state; /* File system state */ + __le16 s_errors; /* Behaviour when detecting errors */ + __le16 s_minor_rev_level; /* minor revision level */ +/*40*/ __le32 s_lastcheck; /* time of last check */ + __le32 s_checkinterval; /* max. time between checks */ + __le32 s_creator_os; /* OS */ + __le32 s_rev_level; /* Revision level */ +/*50*/ __le16 s_def_resuid; /* Default uid for reserved blocks */ + __le16 s_def_resgid; /* Default gid for reserved blocks */ + /* + * These fields are for EXT4_DYNAMIC_REV superblocks only. + * + * Note: the difference between the compatible feature set and + * the incompatible feature set is that if there is a bit set + * in the incompatible feature set that the kernel doesn't + * know about, it should refuse to mount the filesystem. + * + * e2fsck's requirements are more strict; if it doesn't know + * about a feature in either the compatible or incompatible + * feature set, it must abort and not try to meddle with + * things it doesn't understand... + */ + __le32 s_first_ino; /* First non-reserved inode */ + __le16 s_inode_size; /* size of inode structure */ + __le16 s_block_group_nr; /* block group # of this superblock */ + __le32 s_feature_compat; /* compatible feature set */ +/*60*/ __le32 s_feature_incompat; /* incompatible feature set */ + __le32 s_feature_ro_compat; /* readonly-compatible feature set */ +/*68*/ __u8 s_uuid[16]; /* 128-bit uuid for volume */ +/*78*/ char s_volume_name[16]; /* volume name */ +/*88*/ char s_last_mounted[64]; /* directory where last mounted */ +/*C8*/ __le32 s_algorithm_usage_bitmap; /* For compression */ + /* + * Performance hints. Directory preallocation should only + * happen if the EXT4_FEATURE_COMPAT_DIR_PREALLOC flag is on. + */ + __u8 s_prealloc_blocks; /* Nr of blocks to try to preallocate*/ + __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */ + __le16 s_reserved_gdt_blocks; /* Per group desc for online growth */ + /* + * Journaling support valid if EXT4_FEATURE_COMPAT_HAS_JOURNAL set. + */ +/*D0*/ __u8 s_journal_uuid[16]; /* uuid of journal superblock */ +/*E0*/ __le32 s_journal_inum; /* inode number of journal file */ + __le32 s_journal_dev; /* device number of journal file */ + __le32 s_last_orphan; /* start of list of inodes to delete */ + __le32 s_hash_seed[4]; /* HTREE hash seed */ + __u8 s_def_hash_version; /* Default hash version to use */ + __u8 s_jnl_backup_type; + __le16 s_desc_size; /* size of group descriptor */ +/*100*/ __le32 s_default_mount_opts; + __le32 s_first_meta_bg; /* First metablock block group */ + __le32 s_mkfs_time; /* When the filesystem was created */ + __le32 s_jnl_blocks[17]; /* Backup of the journal inode */ + /* 64bit support valid if EXT4_FEATURE_COMPAT_64BIT */ +/*150*/ __le32 s_blocks_count_hi; /* Blocks count */ + __le32 s_r_blocks_count_hi; /* Reserved blocks count */ + __le32 s_free_blocks_count_hi; /* Free blocks count */ + __le16 s_min_extra_isize; /* All inodes have at least # bytes */ + __le16 s_want_extra_isize; /* New inodes should reserve # bytes */ + __le32 s_flags; /* Miscellaneous flags */ + __le16 s_raid_stride; /* RAID stride */ + __le16 s_mmp_update_interval; /* # seconds to wait in MMP checking */ + __le64 s_mmp_block; /* Block for multi-mount protection */ + __le32 s_raid_stripe_width; /* blocks on all data disks (N*stride)*/ + __u8 s_log_groups_per_flex; /* FLEX_BG group size */ + __u8 s_checksum_type; /* metadata checksum algorithm used */ + __u8 s_encryption_level; /* versioning level for encryption */ + __u8 s_reserved_pad; /* Padding to next 32bits */ + __le64 s_kbytes_written; /* nr of lifetime kilobytes written */ + __le32 s_snapshot_inum; /* Inode number of active snapshot */ + __le32 s_snapshot_id; /* sequential ID of active snapshot */ + __le64 s_snapshot_r_blocks_count; /* reserved blocks for active + snapshot's future use */ + __le32 s_snapshot_list; /* inode number of the head of the + on-disk snapshot list */ +#define EXT4_S_ERR_START offsetof(struct ext4_super_block, s_error_count) + __le32 s_error_count; /* number of fs errors */ + __le32 s_first_error_time; /* first time an error happened */ + __le32 s_first_error_ino; /* inode involved in first error */ + __le64 s_first_error_block; /* block involved of first error */ + __u8 s_first_error_func[32]; /* function where the error happened */ + __le32 s_first_error_line; /* line number where error happened */ + __le32 s_last_error_time; /* most recent time of an error */ + __le32 s_last_error_ino; /* inode involved in last error */ + __le32 s_last_error_line; /* line number where error happened */ + __le64 s_last_error_block; /* block involved of last error */ + __u8 s_last_error_func[32]; /* function where the error happened */ +#define EXT4_S_ERR_END offsetof(struct ext4_super_block, s_mount_opts) + __u8 s_mount_opts[64]; + __le32 s_usr_quota_inum; /* inode for tracking user quota */ + __le32 s_grp_quota_inum; /* inode for tracking group quota */ + __le32 s_overhead_clusters; /* overhead blocks/clusters in fs */ + __le32 s_backup_bgs[2]; /* groups with sparse_super2 SBs */ + __u8 s_encrypt_algos[4]; /* Encryption algorithms in use */ + __u8 s_encrypt_pw_salt[16]; /* Salt used for string2key algorithm */ + __le32 s_lpf_ino; /* Location of the lost+found inode */ + __le32 s_prj_quota_inum; /* inode for tracking project quota */ + __le32 s_checksum_seed; /* crc32c(uuid) if csum_seed set */ + __u8 s_wtime_hi; + __u8 s_mtime_hi; + __u8 s_mkfs_time_hi; + __u8 s_lastcheck_hi; + __u8 s_first_error_time_hi; + __u8 s_last_error_time_hi; + __u8 s_pad[2]; + __le32 s_reserved[96]; /* Padding to the end of the block */ + __le32 s_checksum; /* crc32c(superblock) */ +}; + +#define EXT4_S_ERR_LEN (EXT4_S_ERR_END - EXT4_S_ERR_START) + +#ifdef __KERNEL__ + +/* + * run-time mount flags + */ +#define EXT4_MF_MNTDIR_SAMPLED 0x0001 +#define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */ +#define EXT4_MF_TEST_DUMMY_ENCRYPTION 0x0004 + +#ifdef CONFIG_EXT4_FS_ENCRYPTION +#define DUMMY_ENCRYPTION_ENABLED(sbi) (unlikely((sbi)->s_mount_flags & \ + EXT4_MF_TEST_DUMMY_ENCRYPTION)) +#else +#define DUMMY_ENCRYPTION_ENABLED(sbi) (0) +#endif + +/* Number of quota types we support */ +#define EXT4_MAXQUOTAS 3 + +/* + * fourth extended-fs super-block data in memory + */ +struct ext4_sb_info { + + ERESOURCE s_gd_lock; + struct ext3_gd *s_gd; + + unsigned long s_desc_size; /* size of group desc */ + unsigned long s_gdb_count; /* Number of group descriptor blocks */ + unsigned long s_desc_per_block; /* Number of group descriptors per block */ + unsigned long s_inodes_per_group;/* Number of inodes in a group */ + unsigned long s_inodes_per_block;/* Number of inodes per block */ + unsigned long s_blocks_per_group;/* Number of blocks in a group */ + unsigned long s_groups_count; /* Number of groups in the fs */ + unsigned long s_itb_per_group; /* Number of inode table blocks per group */ + unsigned long s_clusters_per_group; /* Number of clusters in a group */ + + int s_addr_per_block_bits; + int s_desc_per_block_bits; + int s_inode_size; + +#if 0 + unsigned long s_frag_size; /* Size of a fragment in bytes */ + unsigned long s_frags_per_block;/* Number of fragments per block */ + unsigned long s_frags_per_group;/* Number of fragments in a group */ + unsigned long s_inodes_per_group;/* Number of inodes in a group */ + unsigned long s_itb_per_group; /* Number of inode table blocks per group */ + unsigned long s_desc_per_block; /* Number of group descriptors per block */ + unsigned long s_overhead_last; /* Last calculated overhead */ + unsigned long s_blocks_last; /* Last seen block count */ +#endif + + struct ext4_super_block * s_es; /* Pointer to the super block in the buffer */ + + __le32 s_first_ino; + + __u32 s_hash_seed[4]; + int s_def_hash_version; + __le32 s_csum_seed; /* crc32c(uuid) if csum_seed set */ +}; + +static inline struct ext4_sb_info *EXT4_SB(struct super_block *sb) +{ + return sb->s_fs_info; +} +/*static inline struct ext4_inode_info *EXT4_I(struct inode *inode) +{ + return container_of(inode, struct ext4_inode_info, vfs_inode); +}*/ + +static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino) +{ + return ino == EXT4_ROOT_INO || + (ino >= EXT4_FIRST_INO(sb) && + ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count)); +} + +/* + * Inode dynamic state flags + */ +enum { + EXT4_STATE_JDATA, /* journaled data exists */ + EXT4_STATE_NEW, /* inode is newly created */ + EXT4_STATE_XATTR, /* has in-inode xattrs */ + EXT4_STATE_NO_EXPAND, /* No space for expansion */ + EXT4_STATE_DA_ALLOC_CLOSE, /* Alloc DA blks on close */ + EXT4_STATE_EXT_MIGRATE, /* Inode is migrating */ + EXT4_STATE_DIO_UNWRITTEN, /* need convert on dio done*/ + EXT4_STATE_NEWENTRY, /* File just added to dir */ + EXT4_STATE_MAY_INLINE_DATA, /* may have in-inode data */ + EXT4_STATE_EXT_PRECACHED, /* extents have been precached */ + EXT4_STATE_LUSTRE_EA_INODE, /* Lustre-style ea_inode */ +}; + +#define EXT4_INODE_BIT_FNS(name, field, offset) \ +static inline int ext4_test_inode_##name(struct inode *inode, int bit) \ +{ \ + return test_bit(bit + (offset), &EXT4_I(inode)->i_##field); \ +} \ +static inline void ext4_set_inode_##name(struct inode *inode, int bit) \ +{ \ + set_bit(bit + (offset), &EXT4_I(inode)->i_##field); \ +} \ +static inline void ext4_clear_inode_##name(struct inode *inode, int bit) \ +{ \ + clear_bit(bit + (offset), &EXT4_I(inode)->i_##field); \ +} + +/* Add these declarations here only so that these functions can be + * found by name. Otherwise, they are very hard to locate. */ +static inline int ext4_test_inode_flag(struct inode *inode, int bit); +static inline void ext4_set_inode_flag(struct inode *inode, int bit); +static inline void ext4_clear_inode_flag(struct inode *inode, int bit); +EXT4_INODE_BIT_FNS(flag, flags, 0) + +/* Add these declarations here only so that these functions can be + * found by name. Otherwise, they are very hard to locate. */ +static inline int ext4_test_inode_state(struct inode *inode, int bit); +static inline void ext4_set_inode_state(struct inode *inode, int bit); +static inline void ext4_clear_inode_state(struct inode *inode, int bit); +#if (BITS_PER_LONG < 64) +//EXT4_INODE_BIT_FNS(state, state_flags, 0) + +static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) +{ + (ei)->i_state_flags = 0; +} +#else +EXT4_INODE_BIT_FNS(state, flags, 32) + +static inline void ext4_clear_state_flags(struct ext4_inode_info *ei) +{ + /* We depend on the fact that callers will set i_flags */ +} +#endif +#else +/* Assume that user mode programs are passing in an ext4fs superblock, not + * a kernel struct super_block. This will allow us to call the feature-test + * macros from user land. */ +#define EXT4_SB(sb) (sb) +#endif + +/* + * Returns true if the inode is inode is encrypted + */ +#define NEXT_ORPHAN(inode) EXT4_I(inode)->i_dtime + +/* + * Codes for operating systems + */ +#define EXT4_OS_LINUX 0 +#define EXT4_OS_HURD 1 +#define EXT4_OS_MASIX 2 +#define EXT4_OS_FREEBSD 3 +#define EXT4_OS_LITES 4 + +/* + * Revision levels + */ +#define EXT4_GOOD_OLD_REV 0 /* The good old (original) format */ +#define EXT4_DYNAMIC_REV 1 /* V2 format w/ dynamic inode sizes */ + +#define EXT4_CURRENT_REV EXT4_GOOD_OLD_REV +#define EXT4_MAX_SUPP_REV EXT4_DYNAMIC_REV + +#define EXT4_GOOD_OLD_INODE_SIZE 128 + +/* + * Feature set definitions + */ + +#define EXT4_FEATURE_COMPAT_DIR_PREALLOC 0x0001 +#define EXT4_FEATURE_COMPAT_IMAGIC_INODES 0x0002 +#define EXT4_FEATURE_COMPAT_HAS_JOURNAL 0x0004 +#define EXT4_FEATURE_COMPAT_EXT_ATTR 0x0008 +#define EXT4_FEATURE_COMPAT_RESIZE_INODE 0x0010 +#define EXT4_FEATURE_COMPAT_DIR_INDEX 0x0020 +#define EXT4_FEATURE_COMPAT_SPARSE_SUPER2 0x0200 + +#define EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 +#define EXT4_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 +#define EXT4_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 +#define EXT4_FEATURE_RO_COMPAT_HUGE_FILE 0x0008 +#define EXT4_FEATURE_RO_COMPAT_GDT_CSUM 0x0010 +#define EXT4_FEATURE_RO_COMPAT_DIR_NLINK 0x0020 +#define EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE 0x0040 +#define EXT4_FEATURE_RO_COMPAT_QUOTA 0x0100 +#define EXT4_FEATURE_RO_COMPAT_BIGALLOC 0x0200 +/* + * METADATA_CSUM also enables group descriptor checksums (GDT_CSUM). When + * METADATA_CSUM is set, group descriptor checksums use the same algorithm as + * all other data structures' checksums. However, the METADATA_CSUM and + * GDT_CSUM bits are mutually exclusive. + */ +#define EXT4_FEATURE_RO_COMPAT_METADATA_CSUM 0x0400 +#define EXT4_FEATURE_RO_COMPAT_READONLY 0x1000 +#define EXT4_FEATURE_RO_COMPAT_PROJECT 0x2000 + +#define EXT4_FEATURE_INCOMPAT_COMPRESSION 0x0001 +#define EXT4_FEATURE_INCOMPAT_FILETYPE 0x0002 +#define EXT4_FEATURE_INCOMPAT_RECOVER 0x0004 /* Needs recovery */ +#define EXT4_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 /* Journal device */ +#define EXT4_FEATURE_INCOMPAT_META_BG 0x0010 +#define EXT4_FEATURE_INCOMPAT_EXTENTS 0x0040 /* extents support */ +#define EXT4_FEATURE_INCOMPAT_64BIT 0x0080 +#define EXT4_FEATURE_INCOMPAT_MMP 0x0100 +#define EXT4_FEATURE_INCOMPAT_FLEX_BG 0x0200 +#define EXT4_FEATURE_INCOMPAT_EA_INODE 0x0400 /* EA in inode */ +#define EXT4_FEATURE_INCOMPAT_DIRDATA 0x1000 /* data in dirent */ +#define EXT4_FEATURE_INCOMPAT_CSUM_SEED 0x2000 +#define EXT4_FEATURE_INCOMPAT_LARGEDIR 0x4000 /* >2GB or 3-lvl htree */ +#define EXT4_FEATURE_INCOMPAT_INLINE_DATA 0x8000 /* data in inode */ +#define EXT4_FEATURE_INCOMPAT_ENCRYPT 0x10000 + +#define EXT4_FEATURE_COMPAT_FUNCS(name, flagname) \ +static inline bool ext4_has_feature_##name(struct super_block *sb) \ +{ \ + return ((EXT4_SB(sb)->s_es->s_feature_compat & \ + cpu_to_le32(EXT4_FEATURE_COMPAT_##flagname)) != 0); \ +} \ +static inline void ext4_set_feature_##name(struct super_block *sb) \ +{ \ + EXT4_SB(sb)->s_es->s_feature_compat |= \ + cpu_to_le32(EXT4_FEATURE_COMPAT_##flagname); \ +} \ +static inline void ext4_clear_feature_##name(struct super_block *sb) \ +{ \ + EXT4_SB(sb)->s_es->s_feature_compat &= \ + ~cpu_to_le32(EXT4_FEATURE_COMPAT_##flagname); \ +} + +#define EXT4_FEATURE_RO_COMPAT_FUNCS(name, flagname) \ +static inline bool ext4_has_feature_##name(struct super_block *sb) \ +{ \ + return ((EXT4_SB(sb)->s_es->s_feature_ro_compat & \ + cpu_to_le32(EXT4_FEATURE_RO_COMPAT_##flagname)) != 0); \ +} \ +static inline void ext4_set_feature_##name(struct super_block *sb) \ +{ \ + EXT4_SB(sb)->s_es->s_feature_ro_compat |= \ + cpu_to_le32(EXT4_FEATURE_RO_COMPAT_##flagname); \ +} \ +static inline void ext4_clear_feature_##name(struct super_block *sb) \ +{ \ + EXT4_SB(sb)->s_es->s_feature_ro_compat &= \ + ~cpu_to_le32(EXT4_FEATURE_RO_COMPAT_##flagname); \ +} + +#define EXT4_FEATURE_INCOMPAT_FUNCS(name, flagname) \ +static inline bool ext4_has_feature_##name(struct super_block *sb) \ +{ \ + return ((EXT4_SB(sb)->s_es->s_feature_incompat & \ + cpu_to_le32(EXT4_FEATURE_INCOMPAT_##flagname)) != 0); \ +} \ +static inline void ext4_set_feature_##name(struct super_block *sb) \ +{ \ + EXT4_SB(sb)->s_es->s_feature_incompat |= \ + cpu_to_le32(EXT4_FEATURE_INCOMPAT_##flagname); \ +} \ +static inline void ext4_clear_feature_##name(struct super_block *sb) \ +{ \ + EXT4_SB(sb)->s_es->s_feature_incompat &= \ + ~cpu_to_le32(EXT4_FEATURE_INCOMPAT_##flagname); \ +} + +EXT4_FEATURE_COMPAT_FUNCS(dir_prealloc, DIR_PREALLOC) +EXT4_FEATURE_COMPAT_FUNCS(imagic_inodes, IMAGIC_INODES) +EXT4_FEATURE_COMPAT_FUNCS(journal, HAS_JOURNAL) +EXT4_FEATURE_COMPAT_FUNCS(xattr, EXT_ATTR) +EXT4_FEATURE_COMPAT_FUNCS(resize_inode, RESIZE_INODE) +EXT4_FEATURE_COMPAT_FUNCS(dir_index, DIR_INDEX) +EXT4_FEATURE_COMPAT_FUNCS(sparse_super2, SPARSE_SUPER2) + +EXT4_FEATURE_RO_COMPAT_FUNCS(sparse_super, SPARSE_SUPER) +EXT4_FEATURE_RO_COMPAT_FUNCS(large_file, LARGE_FILE) +EXT4_FEATURE_RO_COMPAT_FUNCS(btree_dir, BTREE_DIR) +EXT4_FEATURE_RO_COMPAT_FUNCS(huge_file, HUGE_FILE) +EXT4_FEATURE_RO_COMPAT_FUNCS(gdt_csum, GDT_CSUM) +EXT4_FEATURE_RO_COMPAT_FUNCS(dir_nlink, DIR_NLINK) +EXT4_FEATURE_RO_COMPAT_FUNCS(extra_isize, EXTRA_ISIZE) +EXT4_FEATURE_RO_COMPAT_FUNCS(quota, QUOTA) +EXT4_FEATURE_RO_COMPAT_FUNCS(bigalloc, BIGALLOC) +EXT4_FEATURE_RO_COMPAT_FUNCS(metadata_csum, METADATA_CSUM) +EXT4_FEATURE_RO_COMPAT_FUNCS(readonly, READONLY) +EXT4_FEATURE_RO_COMPAT_FUNCS(project, PROJECT) + +EXT4_FEATURE_INCOMPAT_FUNCS(compression, COMPRESSION) +EXT4_FEATURE_INCOMPAT_FUNCS(filetype, FILETYPE) +EXT4_FEATURE_INCOMPAT_FUNCS(journal_needs_recovery, RECOVER) +EXT4_FEATURE_INCOMPAT_FUNCS(journal_dev, JOURNAL_DEV) +EXT4_FEATURE_INCOMPAT_FUNCS(meta_bg, META_BG) +EXT4_FEATURE_INCOMPAT_FUNCS(extents, EXTENTS) +EXT4_FEATURE_INCOMPAT_FUNCS(64bit, 64BIT) +EXT4_FEATURE_INCOMPAT_FUNCS(mmp, MMP) +EXT4_FEATURE_INCOMPAT_FUNCS(flex_bg, FLEX_BG) +EXT4_FEATURE_INCOMPAT_FUNCS(ea_inode, EA_INODE) +EXT4_FEATURE_INCOMPAT_FUNCS(dirdata, DIRDATA) +EXT4_FEATURE_INCOMPAT_FUNCS(csum_seed, CSUM_SEED) +EXT4_FEATURE_INCOMPAT_FUNCS(largedir, LARGEDIR) +EXT4_FEATURE_INCOMPAT_FUNCS(inline_data, INLINE_DATA) +EXT4_FEATURE_INCOMPAT_FUNCS(encrypt, ENCRYPT) + +#define EXT2_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR +#define EXT2_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \ + EXT4_FEATURE_INCOMPAT_META_BG) +#define EXT2_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_BTREE_DIR) + +#define EXT3_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR +#define EXT3_FEATURE_INCOMPAT_SUPP (EXT4_FEATURE_INCOMPAT_FILETYPE| \ + EXT4_FEATURE_INCOMPAT_RECOVER| \ + EXT4_FEATURE_INCOMPAT_META_BG) +#define EXT3_FEATURE_RO_COMPAT_SUPP (EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_BTREE_DIR) + +#define EXT4_FEATURE_COMPAT_SUPP EXT4_FEATURE_COMPAT_EXT_ATTR +#define EXT4_FEATURE_INCOMPAT_SUPP ( \ + EXT4_FEATURE_INCOMPAT_FILETYPE| \ + EXT4_FEATURE_INCOMPAT_RECOVER| \ + EXT4_FEATURE_INCOMPAT_META_BG| \ + EXT4_FEATURE_INCOMPAT_EXTENTS| \ + EXT4_FEATURE_INCOMPAT_64BIT| \ + EXT4_FEATURE_INCOMPAT_FLEX_BG) +#define EXT4_FEATURE_RO_COMPAT_SUPP ( \ + EXT4_FEATURE_RO_COMPAT_SPARSE_SUPER| \ + EXT4_FEATURE_RO_COMPAT_LARGE_FILE| \ + EXT4_FEATURE_RO_COMPAT_GDT_CSUM| \ + EXT4_FEATURE_RO_COMPAT_DIR_NLINK | \ + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE | \ + EXT4_FEATURE_RO_COMPAT_BTREE_DIR |\ + EXT4_FEATURE_RO_COMPAT_HUGE_FILE |\ + EXT4_FEATURE_RO_COMPAT_METADATA_CSUM) + +#define EXTN_FEATURE_FUNCS(ver) \ +static inline bool ext4_has_unknown_ext##ver##_compat_features(struct super_block *sb) \ +{ \ + return ((EXT4_SB(sb)->s_es->s_feature_compat & \ + cpu_to_le32(~EXT##ver##_FEATURE_COMPAT_SUPP)) != 0); \ +} \ +static inline bool ext4_has_unknown_ext##ver##_ro_compat_features(struct super_block *sb) \ +{ \ + return ((EXT4_SB(sb)->s_es->s_feature_ro_compat & \ + cpu_to_le32(~EXT##ver##_FEATURE_RO_COMPAT_SUPP)) != 0); \ +} \ +static inline bool ext4_has_unknown_ext##ver##_incompat_features(struct super_block *sb) \ +{ \ + return ((EXT4_SB(sb)->s_es->s_feature_incompat & \ + cpu_to_le32(~EXT##ver##_FEATURE_INCOMPAT_SUPP)) != 0); \ +} + +EXTN_FEATURE_FUNCS(2) +EXTN_FEATURE_FUNCS(3) +EXTN_FEATURE_FUNCS(4) + +static inline bool ext4_has_compat_features(struct super_block *sb) +{ + return (EXT4_SB(sb)->s_es->s_feature_compat != 0); +} +static inline bool ext4_has_ro_compat_features(struct super_block *sb) +{ + return (EXT4_SB(sb)->s_es->s_feature_ro_compat != 0); +} +static inline bool ext4_has_incompat_features(struct super_block *sb) +{ + return (EXT4_SB(sb)->s_es->s_feature_incompat != 0); +} + +/* + * Superblock flags + */ +#define EXT4_FLAGS_RESIZING 0 +#define EXT4_FLAGS_SHUTDOWN 1 + +/*static inline int ext4_forced_shutdown(struct ext4_sb_info *sbi) +{ + return test_bit(EXT4_FLAGS_SHUTDOWN, &sbi->s_ext4_flags); +}*/ + + +/* + * Default values for user and/or group using reserved blocks + */ +#define EXT4_DEF_RESUID 0 +#define EXT4_DEF_RESGID 0 + +/* + * Default project ID + */ +#define EXT4_DEF_PROJID 0 + +#define EXT4_DEF_INODE_READAHEAD_BLKS 32 + +/* + * Default mount options + */ +#define EXT4_DEFM_DEBUG 0x0001 +#define EXT4_DEFM_BSDGROUPS 0x0002 +#define EXT4_DEFM_XATTR_USER 0x0004 +#define EXT4_DEFM_ACL 0x0008 +#define EXT4_DEFM_UID16 0x0010 +#define EXT4_DEFM_JMODE 0x0060 +#define EXT4_DEFM_JMODE_DATA 0x0020 +#define EXT4_DEFM_JMODE_ORDERED 0x0040 +#define EXT4_DEFM_JMODE_WBACK 0x0060 +#define EXT4_DEFM_NOBARRIER 0x0100 +#define EXT4_DEFM_BLOCK_VALIDITY 0x0200 +#define EXT4_DEFM_DISCARD 0x0400 +#define EXT4_DEFM_NODELALLOC 0x0800 + +/* + * Default journal batch times + */ +#define EXT4_DEF_MIN_BATCH_TIME 0 +#define EXT4_DEF_MAX_BATCH_TIME 15000 /* 15ms */ + +/* + * Minimum number of groups in a flexgroup before we separate out + * directories into the first block group of a flexgroup + */ +#define EXT4_FLEX_SIZE_DIR_ALLOC_SCHEME 4 + +/* + * Structure of a directory entry + */ +#define EXT4_NAME_LEN 255 + +struct ext4_dir_entry { + __le32 inode; /* Inode number */ + __le16 rec_len; /* Directory entry length */ + __le16 name_len; /* Name length */ + char name[EXT4_NAME_LEN]; /* File name */ +}; + +/* + * The new version of the directory entry. Since EXT4 structures are + * stored in intel byte order, and the name_len field could never be + * bigger than 255 chars, it's safe to reclaim the extra byte for the + * file_type field. + */ +struct ext4_dir_entry_2 { + __le32 inode; /* Inode number */ + __le16 rec_len; /* Directory entry length */ + __u8 name_len; /* Name length */ + __u8 file_type; + char name[EXT4_NAME_LEN]; /* File name */ +}; + +/* + * This is a bogus directory entry at the end of each leaf block that + * records checksums. + */ +struct ext4_dir_entry_tail { + __le32 det_reserved_zero1; /* Pretend to be unused */ + __le16 det_rec_len; /* 12 */ + __u8 det_reserved_zero2; /* Zero name length */ + __u8 det_reserved_ft; /* 0xDE, fake file type */ + __le32 det_checksum; /* crc32c(uuid+inum+dirblock) */ +}; + +#define EXT4_DIRENT_TAIL(block, blocksize) \ + ((struct ext4_dir_entry_tail *)(((unsigned char *)(block)) + \ + ((blocksize) - \ + sizeof(struct ext4_dir_entry_tail)))) + +/* + * Ext4 directory file types. Only the low 3 bits are used. The + * other bits are reserved for now. + */ +#define EXT4_FT_UNKNOWN 0 +#define EXT4_FT_REG_FILE 1 +#define EXT4_FT_DIR 2 +#define EXT4_FT_CHRDEV 3 +#define EXT4_FT_BLKDEV 4 +#define EXT4_FT_FIFO 5 +#define EXT4_FT_SOCK 6 +#define EXT4_FT_SYMLINK 7 + +#define EXT4_FT_MAX 8 + +#define EXT4_FT_DIR_CSUM 0xDE + +/* + * EXT4_DIR_PAD defines the directory entries boundaries + * + * NOTE: It must be a multiple of 4 + */ +#define EXT4_DIR_PAD 4 +#define EXT4_DIR_ROUND (EXT4_DIR_PAD - 1) +#define EXT4_DIR_REC_LEN(name_len) (((name_len) + 8 + EXT4_DIR_ROUND) & \ + ~EXT4_DIR_ROUND) +#define EXT4_MAX_REC_LEN ((1<<16)-1) + +/* + * If we ever get support for fs block sizes > page_size, we'll need + * to remove the #if statements in the next two functions... + */ +static inline unsigned int +ext4_rec_len_from_disk(__le16 dlen, unsigned blocksize) +{ + unsigned len = le16_to_cpu(dlen); + +#if (PAGE_SIZE >= 65536) + if (len == EXT4_MAX_REC_LEN || len == 0) + return blocksize; + return (len & 65532) | ((len & 3) << 16); +#else + return len; +#endif +} + +static inline __le16 ext4_rec_len_to_disk(unsigned len, unsigned blocksize) +{ + if ((len > blocksize) || (blocksize > (1 << 18)) || (len & 3)) + BUG(); +#if (PAGE_SIZE >= 65536) + if (len < 65536) + return cpu_to_le16(len); + if (len == blocksize) { + if (blocksize == 65536) + return cpu_to_le16(EXT4_MAX_REC_LEN); + else + return cpu_to_le16(0); + } + return cpu_to_le16((len & 65532) | ((len >> 16) & 3)); +#else + return cpu_to_le16(len); +#endif +} + +/* + * Hash Tree Directory indexing + * (c) Daniel Phillips, 2001 + */ +#if 0 +#define is_dx(dir) (ext4_has_feature_dir_index((dir)->i_sb) && \ + ext4_test_inode_flag((dir), EXT4_INODE_INDEX)) +#define EXT4_DIR_LINK_MAX(dir) unlikely((dir)->i_nlink >= EXT4_LINK_MAX && \ + !(ext4_has_feature_dir_nlink((dir)->i_sb) && is_dx(dir))) +#define EXT4_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2 || (dir)->i_nlink == 1) +#endif +/* Legal values for the dx_root hash_version field: */ + +#define DX_HASH_LEGACY 0 +#define DX_HASH_HALF_MD4 1 +#define DX_HASH_TEA 2 +#define DX_HASH_LEGACY_UNSIGNED 3 +#define DX_HASH_HALF_MD4_UNSIGNED 4 +#define DX_HASH_TEA_UNSIGNED 5 +#if 0 +static inline __u32 ext4_chksum(struct ext4_sb_info *sbi, __u32 crc, + const void *address, unsigned int length) +{ + struct { + struct shash_desc shash; + char ctx[4]; + } desc; + + BUG_ON(crypto_shash_descsize(sbi->s_chksum_driver)!=sizeof(desc.ctx)); + + desc.shash.tfm = sbi->s_chksum_driver; + desc.shash.flags = 0; + *(__u32 *)desc.ctx = crc; + + BUG_ON(crypto_shash_update(&desc.shash, address, length)); + + return *(__u32 *)desc.ctx; +} +#endif +#ifdef __KERNEL__ + +/* hash info structure used by the directory hash */ +struct dx_hash_info +{ + __u32 hash; + __u32 minor_hash; + int hash_version; + __u32 *seed; +}; + + +/* 32 and 64 bit signed EOF for dx directories */ +#define EXT4_HTREE_EOF_32BIT ((1UL << (32 - 1)) - 1) +#define EXT4_HTREE_EOF_64BIT ((1ULL << (64 - 1)) - 1) + + +/* + * Control parameters used by ext4_htree_next_block + */ +#define HASH_NB_ALWAYS 1 + +struct ext4_filename { + const struct qstr *usr_fname; + struct fscrypt_str disk_name; + struct dx_hash_info hinfo; +#ifdef CONFIG_EXT4_FS_ENCRYPTION + struct fscrypt_str crypto_buf; +#endif +}; + +#define fname_name(p) ((p)->disk_name.name) +#define fname_len(p) ((p)->disk_name.len) + +/* + * Describe an inode's exact location on disk and in memory + */ +struct ext4_iloc +{ + struct buffer_head *bh; + unsigned long offset; + ext4_group_t block_group; +}; + +static inline struct ext4_inode *ext4_raw_inode(struct ext4_iloc *iloc) +{ + return (struct ext4_inode *) (iloc->bh->b_data + iloc->offset); +} + +static inline bool ext4_is_quota_file(struct inode *inode) +{ + return IS_NOQUOTA(inode) && + !(EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL); +} + +/* + * This structure is stuffed into the struct file's private_data field + * for directories. It is where we put information so that we can do + * readdir operations in hash tree order. + */ +struct dir_private_info { + struct rb_root root; + struct rb_node *curr_node; + struct fname *extra_fname; + loff_t last_pos; + __u32 curr_hash; + __u32 curr_minor_hash; + __u32 next_hash; +}; + +/* calculate the first block number of the group */ +static inline ext4_fsblk_t +ext4_group_first_block_no(struct super_block *sb, ext4_group_t group_no) +{ + return group_no * (ext4_fsblk_t)EXT4_BLOCKS_PER_GROUP(sb) + + le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block); +} + +/* + * Special error return code only used by dx_probe() and its callers. + */ +#define ERR_BAD_DX_DIR (-(MAX_ERRNO - 1)) + +/* htree levels for ext4 */ +#define EXT4_HTREE_LEVEL_COMPAT 2 +#define EXT4_HTREE_LEVEL 3 + +static inline int ext4_dir_htree_level(struct super_block *sb) +{ + return ext4_has_feature_largedir(sb) ? + EXT4_HTREE_LEVEL : EXT4_HTREE_LEVEL_COMPAT; +} + +/* + * Timeout and state flag for lazy initialization inode thread. + */ +#define EXT4_DEF_LI_WAIT_MULT 10 +#define EXT4_DEF_LI_MAX_START_DELAY 5 +#define EXT4_LAZYINIT_QUIT 0x0001 +#define EXT4_LAZYINIT_RUNNING 0x0002 + +/* + * Lazy inode table initialization info + */ +struct ext4_lazy_init { + unsigned long li_state; + struct list_head li_request_list; + struct mutex li_list_mtx; +}; + +struct ext4_li_request { + struct super_block *lr_super; + struct ext4_sb_info *lr_sbi; + ext4_group_t lr_next_group; + struct list_head lr_request; + unsigned long lr_next_sched; + unsigned long lr_timeout; +}; + +/*struct ext4_features { + struct kobject f_kobj; + struct completion f_kobj_unregister; +};*/ + +/* + * This structure will be used for multiple mount protection. It will be + * written into the block number saved in the s_mmp_block field in the + * superblock. Programs that check MMP should assume that if + * SEQ_FSCK (or any unknown code above SEQ_MAX) is present then it is NOT safe + * to use the filesystem, regardless of how old the timestamp is. + */ +#define EXT4_MMP_MAGIC 0x004D4D50U /* ASCII for MMP */ +#define EXT4_MMP_SEQ_CLEAN 0xFF4D4D50U /* mmp_seq value for clean unmount */ +#define EXT4_MMP_SEQ_FSCK 0xE24D4D50U /* mmp_seq value when being fscked */ +#define EXT4_MMP_SEQ_MAX 0xE24D4D4FU /* maximum valid mmp_seq value */ + +struct mmp_struct { + __le32 mmp_magic; /* Magic number for MMP */ + __le32 mmp_seq; /* Sequence no. updated periodically */ + + /* + * mmp_time, mmp_nodename & mmp_bdevname are only used for information + * purposes and do not affect the correctness of the algorithm + */ + __le64 mmp_time; /* Time last updated */ + char mmp_nodename[64]; /* Node which last updated MMP block */ + char mmp_bdevname[32]; /* Bdev which last updated MMP block */ + + /* + * mmp_check_interval is used to verify if the MMP block has been + * updated on the block device. The value is updated based on the + * maximum time to write the MMP block during an update cycle. + */ + __le16 mmp_check_interval; + + __le16 mmp_pad1; + __le32 mmp_pad2[226]; + __le32 mmp_checksum; /* crc32c(uuid+mmp_block) */ +}; + +/* arguments passed to the mmp thread */ +struct mmpd_data { + struct buffer_head *bh; /* bh from initial read_mmp_block() */ + struct super_block *sb; /* super block of the fs */ +}; + +/* + * Check interval multiplier + * The MMP block is written every update interval and initially checked every + * update interval x the multiplier (the value is then adapted based on the + * write latency). The reason is that writes can be delayed under load and we + * don't want readers to incorrectly assume that the filesystem is no longer + * in use. + */ +#define EXT4_MMP_CHECK_MULT 2UL + +/* + * Minimum interval for MMP checking in seconds. + */ +#define EXT4_MMP_MIN_CHECK_INTERVAL 5UL + +/* + * Maximum interval for MMP checking in seconds. + */ +#define EXT4_MMP_MAX_CHECK_INTERVAL 300UL + +/* + * Function prototypes + */ + +/* + * Ok, these declarations are also in but none of the + * ext4 source programs needs to include it so they are duplicated here. + */ +# define NORET_TYPE /**/ +# define ATTRIB_NORET __attribute__((noreturn)) +# define NORET_AND noreturn, + +/* bitmap.c */ +extern unsigned int ext4_count_free(char *bitmap, unsigned numchars); +void ext4_inode_bitmap_csum_set(struct super_block *sb, ext4_group_t group, + struct ext4_group_desc *gdp, + struct buffer_head *bh, int sz); +int ext4_inode_bitmap_csum_verify(struct super_block *sb, ext4_group_t group, + struct ext4_group_desc *gdp, + struct buffer_head *bh, int sz); +void ext4_block_bitmap_csum_set(struct super_block *sb, ext4_group_t group, + struct ext4_group_desc *gdp, + struct buffer_head *bh); +int ext4_block_bitmap_csum_verify(struct super_block *sb, ext4_group_t group, + struct ext4_group_desc *gdp, + struct buffer_head *bh); + +/* balloc.c */ +extern void ext4_get_group_no_and_offset(struct super_block *sb, + ext4_fsblk_t blocknr, + ext4_group_t *blockgrpp, + ext4_grpblk_t *offsetp); +extern ext4_group_t ext4_get_group_number(struct super_block *sb, + ext4_fsblk_t block); + +extern unsigned int ext4_block_group(struct super_block *sb, + ext4_fsblk_t blocknr); +extern ext4_grpblk_t ext4_block_group_offset(struct super_block *sb, + ext4_fsblk_t blocknr); +extern int ext4_bg_has_super(struct super_block *sb, ext4_group_t group); +extern unsigned long ext4_bg_num_gdb(struct super_block *sb, + ext4_group_t group); +/*extern ext4_fsblk_t ext4_new_meta_blocks(handle_t *handle, struct inode *inode, + ext4_fsblk_t goal, + unsigned int flags, + unsigned long *count, + int *errp);*/ +extern int ext4_claim_free_clusters(struct ext4_sb_info *sbi, + s64 nclusters, unsigned int flags); +extern ext4_fsblk_t ext4_count_free_clusters(struct super_block *); +extern void ext4_check_blocks_bitmap(struct super_block *); +extern struct ext4_group_desc * ext4_get_group_desc(struct super_block * sb, + ext4_group_t block_group, + struct buffer_head ** bh); +extern int ext4_should_retry_alloc(struct super_block *sb, int *retries); + +extern struct buffer_head *ext4_read_block_bitmap_nowait(struct super_block *sb, + ext4_group_t block_group); +extern int ext4_wait_block_bitmap(struct super_block *sb, + ext4_group_t block_group, + struct buffer_head *bh); +extern struct buffer_head *ext4_read_block_bitmap(struct super_block *sb, + ext4_group_t block_group); +extern unsigned ext4_free_clusters_after_init(struct super_block *sb, + ext4_group_t block_group, + struct ext4_group_desc *gdp); +ext4_fsblk_t ext4_inode_to_goal_block(struct inode *); +#if 0 +static inline bool ext4_encrypted_inode(struct inode *inode) +{ + return ext4_test_inode_flag(inode, EXT4_INODE_ENCRYPT); +} +#endif +#ifdef CONFIG_EXT4_FS_ENCRYPTION +static inline int ext4_fname_setup_filename(struct inode *dir, + const struct qstr *iname, + int lookup, struct ext4_filename *fname) +{ + struct fscrypt_name name; + int err; + + memset(fname, 0, sizeof(struct ext4_filename)); + + err = fscrypt_setup_filename(dir, iname, lookup, &name); + + fname->usr_fname = name.usr_fname; + fname->disk_name = name.disk_name; + fname->hinfo.hash = name.hash; + fname->hinfo.minor_hash = name.minor_hash; + fname->crypto_buf = name.crypto_buf; + return err; +} + +static inline void ext4_fname_free_filename(struct ext4_filename *fname) +{ + struct fscrypt_name name; + + name.crypto_buf = fname->crypto_buf; + fscrypt_free_filename(&name); + + fname->crypto_buf.name = NULL; + fname->usr_fname = NULL; + fname->disk_name.name = NULL; +} +#else +static inline int ext4_fname_setup_filename(struct inode *dir, + const struct qstr *iname, + int lookup, struct ext4_filename *fname) +{ + fname->usr_fname = iname; + fname->disk_name.name = (unsigned char *) iname->name; + fname->disk_name.len = iname->len; + return 0; +} +static inline void ext4_fname_free_filename(struct ext4_filename *fname) { } + +#endif + +/* dir.c */ +extern int __ext4_check_dir_entry(const char *, unsigned int, struct inode *, + struct file *, + struct ext4_dir_entry_2 *, + struct buffer_head *, char *, int, + unsigned int); +#define ext4_check_dir_entry(dir, filp, de, bh, buf, size, offset) \ + unlikely(__ext4_check_dir_entry(__func__, __LINE__, (dir), (filp), \ + (de), (bh), (buf), (size), (offset))) +extern int ext4_htree_store_dirent(struct file *dir_file, __u32 hash, + __u32 minor_hash, + struct ext4_dir_entry_2 *dirent, + struct fscrypt_str *ent_name); +extern void ext4_htree_free_dir_info(struct dir_private_info *p); +extern int ext4_find_dest_de(struct inode *dir, struct inode *inode, + struct buffer_head *bh, + void *buf, int buf_size, + struct ext4_filename *fname, + struct ext4_dir_entry_2 **dest_de); +void ext4_insert_dentry(struct inode *inode, + struct ext4_dir_entry_2 *de, + int buf_size, + struct ext4_filename *fname); +static inline void ext4_update_dx_flag(struct inode *inode) +{ + if (!ext4_has_feature_dir_index(inode->i_sb)) + ext4_clear_inode_flag(inode, EXT4_INODE_INDEX); +} +static const unsigned char ext4_filetype_table[] = { + DT_UNKNOWN, DT_REG, DT_DIR, DT_CHR, DT_BLK, DT_FIFO, DT_SOCK, DT_LNK +}; +#if 0 +static inline unsigned char get_dtype(struct super_block *sb, int filetype) +{ + if (!ext4_has_feature_filetype(sb) || filetype >= EXT4_FT_MAX) + return DT_UNKNOWN; + + return ext4_filetype_table[filetype]; +} +#endif +extern int ext4_check_all_de(struct inode *dir, struct buffer_head *bh, + void *buf, int buf_size); + +/* fsync.c */ +extern int ext4_sync_file(struct file *, loff_t, loff_t, int); + +/* hash.c */ +extern int ext4fs_dirhash(const char *name, int len, struct + dx_hash_info *hinfo); + +/* ialloc.c */ +extern struct inode *__ext4_new_inode(handle_t *, struct inode *, umode_t, + const struct qstr *qstr, __u32 goal, + uid_t *owner, __u32 i_flags, + int handle_type, unsigned int line_no, + int nblocks); + +#define ext4_new_inode(handle, dir, mode, qstr, goal, owner, i_flags) \ + __ext4_new_inode((handle), (dir), (mode), (qstr), (goal), (owner), \ + i_flags, 0, 0, 0) +#define ext4_new_inode_start_handle(dir, mode, qstr, goal, owner, \ + type, nblocks) \ + __ext4_new_inode(NULL, (dir), (mode), (qstr), (goal), (owner), \ + 0, (type), __LINE__, (nblocks)) + + +extern void ext4_free_inode(handle_t *, struct inode *); +extern struct inode * ext4_orphan_get(struct super_block *, unsigned long); +extern unsigned long ext4_count_free_inodes(struct super_block *); +extern unsigned long ext4_count_dirs(struct super_block *); +extern void ext4_check_inodes_bitmap(struct super_block *); +extern void ext4_mark_bitmap_end(int start_bit, int end_bit, char *bitmap); +extern int ext4_init_inode_table(struct super_block *sb, + ext4_group_t group, int barrier); +extern void ext4_end_bitmap_read(struct buffer_head *bh, int uptodate); + +/* mballoc.c */ +extern const struct seq_operations ext4_mb_seq_groups_ops; +extern long ext4_mb_stats; +extern long ext4_mb_max_to_scan; +extern int ext4_mb_init(struct super_block *); +extern int ext4_mb_release(struct super_block *); +extern ext4_fsblk_t ext4_mb_new_blocks(handle_t *, + struct ext4_allocation_request *, int *); +extern int ext4_mb_reserve_blocks(struct super_block *, int); +extern void ext4_discard_preallocations(struct inode *); +extern int __init ext4_init_mballoc(void); +extern void ext4_exit_mballoc(void); +/*extern void ext4_free_blocks(handle_t *handle, struct inode *inode, + struct buffer_head *bh, ext4_fsblk_t block, + unsigned long count, int flags);*/ +extern int ext4_mb_alloc_groupinfo(struct super_block *sb, + ext4_group_t ngroups); +extern int ext4_mb_add_groupinfo(struct super_block *sb, + ext4_group_t i, struct ext4_group_desc *desc); +extern int ext4_group_add_blocks(handle_t *handle, struct super_block *sb, + ext4_fsblk_t block, unsigned long count); +extern int ext4_trim_fs(struct super_block *, struct fstrim_range *); +extern void ext4_process_freed_data(struct super_block *sb, tid_t commit_tid); + +/* inode.c */ +int ext4_inode_is_fast_symlink(struct inode *inode); +struct buffer_head *ext4_getblk(handle_t *, struct inode *, ext4_lblk_t, int); +struct buffer_head *ext4_bread(handle_t *, struct inode *, ext4_lblk_t, int); +int ext4_bread_batch(struct inode *inode, ext4_lblk_t block, int bh_count, + bool wait, struct buffer_head **bhs); +int ext4_get_block_unwritten(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create); +int ext4_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create); +int ext4_dio_get_block(struct inode *inode, sector_t iblock, + struct buffer_head *bh_result, int create); +int ext4_da_get_block_prep(struct inode *inode, sector_t iblock, + struct buffer_head *bh, int create); +int ext4_walk_page_buffers(handle_t *handle, + struct buffer_head *head, + unsigned from, + unsigned to, + int *partial, + int (*fn)(handle_t *handle, + struct buffer_head *bh)); +int do_journal_get_write_access(handle_t *handle, + struct buffer_head *bh); +#define FALL_BACK_TO_NONDELALLOC 1 +#define CONVERT_INLINE_DATA 2 + +typedef enum { + EXT4_IGET_NORMAL = 0, + EXT4_IGET_SPECIAL = 0x0001, /* OK to iget a system inode */ + EXT4_IGET_HANDLE = 0x0002 /* Inode # is from a handle */ +} ext4_iget_flags; + +extern struct inode *__ext4_iget(struct super_block *sb, unsigned long ino, + ext4_iget_flags flags, const char *function, + unsigned int line); + +#define ext4_iget(sb, ino, flags) \ + __ext4_iget((sb), (ino), (flags), __func__, __LINE__) + +extern int ext4_write_inode(struct inode *, struct writeback_control *); +extern int ext4_setattr(struct dentry *, struct iattr *); +extern int ext4_getattr(const struct path *, struct kstat *, __u32, unsigned int); +extern void ext4_evict_inode(struct inode *); +extern void ext4_clear_inode(struct inode *); +extern int ext4_file_getattr(const struct path *, struct kstat *, __u32, unsigned int); +extern int ext4_sync_inode(handle_t *, struct inode *); +extern void ext4_dirty_inode(struct inode *, int); +extern int ext4_change_inode_journal_flag(struct inode *, int); +extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *); +extern int ext4_inode_attach_jinode(struct inode *inode); +extern int ext4_can_truncate(struct inode *inode); +extern int ext4_truncate(struct inode *); +extern int ext4_break_layouts(struct inode *); +extern int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length); +extern int ext4_truncate_restart_trans(handle_t *, struct inode *, int nblocks); +extern void ext4_set_inode_flags(struct inode *); +extern int ext4_alloc_da_blocks(struct inode *inode); +extern void ext4_set_aops(struct inode *inode); +extern int ext4_writepage_trans_blocks(struct inode *); +extern int ext4_chunk_trans_blocks(struct inode *, int nrblocks); +extern int ext4_zero_partial_blocks(handle_t *handle, struct inode *inode, + loff_t lstart, loff_t lend); +extern vm_fault_t ext4_page_mkwrite(struct vm_fault *vmf); +extern vm_fault_t ext4_filemap_fault(struct vm_fault *vmf); +extern qsize_t *ext4_get_reserved_space(struct inode *inode); +extern int ext4_get_projid(struct inode *inode, kprojid_t *projid); +extern void ext4_da_release_space(struct inode *inode, int to_free); +extern void ext4_da_update_reserve_space(struct inode *inode, + int used, int quota_claim); +extern int ext4_issue_zeroout(struct inode *inode, ext4_lblk_t lblk, + ext4_fsblk_t pblk, ext4_lblk_t len); + +/* indirect.c */ +extern int ext4_ind_map_blocks(handle_t *handle, struct inode *inode, + struct ext4_map_blocks *map, int flags); +extern int ext4_ind_calc_metadata_amount(struct inode *inode, sector_t lblock); +extern int ext4_ind_trans_blocks(struct inode *inode, int nrblocks); +extern void ext4_ind_truncate(handle_t *, struct inode *inode); +extern int ext4_ind_remove_space(handle_t *handle, struct inode *inode, + ext4_lblk_t start, ext4_lblk_t end); + +/* ioctl.c */ +extern long ext4_ioctl(struct file *, unsigned int, unsigned long); +extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long); + +/* migrate.c */ +extern int ext4_ext_migrate(struct inode *); +extern int ext4_ind_migrate(struct inode *inode); + +/* namei.c */ +extern int ext4_dirent_csum_verify(struct inode *inode, + struct ext4_dir_entry *dirent); +extern int ext4_orphan_add(handle_t *, struct inode *); +extern int ext4_orphan_del(handle_t *, struct inode *); +extern int ext4_htree_fill_tree(struct file *dir_file, __u32 start_hash, + __u32 start_minor_hash, __u32 *next_hash); +extern int ext4_search_dir(struct buffer_head *bh, + char *search_buf, + int buf_size, + struct inode *dir, + struct ext4_filename *fname, + unsigned int offset, + struct ext4_dir_entry_2 **res_dir); +extern int ext4_generic_delete_entry(handle_t *handle, + struct inode *dir, + struct ext4_dir_entry_2 *de_del, + struct buffer_head *bh, + void *entry_buf, + int buf_size, + int csum_size); +extern bool ext4_empty_dir(struct inode *inode); + +/* resize.c */ +extern int ext4_group_add(struct super_block *sb, + struct ext4_new_group_data *input); +extern int ext4_group_extend(struct super_block *sb, + struct ext4_super_block *es, + ext4_fsblk_t n_blocks_count); +extern int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count); + +/* super.c */ +extern struct buffer_head *ext4_sb_bread(struct super_block *sb, + sector_t block, int op_flags); +extern int ext4_seq_options_show(struct seq_file *seq, void *offset); +extern int ext4_calculate_overhead(struct super_block *sb); +extern void ext4_superblock_csum_set(struct super_block *sb); +extern void *ext4_kvmalloc(size_t size, gfp_t flags); +extern void *ext4_kvzalloc(size_t size, gfp_t flags); +extern int ext4_alloc_flex_bg_array(struct super_block *sb, + ext4_group_t ngroup); +extern const char *ext4_decode_error(struct super_block *sb, int errno, + char nbuf[16]); +extern void ext4_mark_group_bitmap_corrupted(struct super_block *sb, + ext4_group_t block_group, + unsigned int flags); + +//extern __printf(4, 5) +void __ext4_error(struct super_block *, const char *, unsigned int, + const char *, ...); +//extern __printf(5, 6) +void __ext4_error_inode(struct inode *, const char *, unsigned int, ext4_fsblk_t, + const char *, ...); +//extern __printf(5, 6) +void __ext4_error_file(struct file *, const char *, unsigned int, ext4_fsblk_t, + const char *, ...); +extern void __ext4_std_error(struct super_block *, const char *, + unsigned int, int); +//extern __printf(4, 5) +void __ext4_abort(struct super_block *, const char *, unsigned int, + const char *, ...); +//extern __printf(4, 5) +void __ext4_warning(struct super_block *, const char *, unsigned int, + const char *, ...); +//extern __printf(4, 5) +void __ext4_warning_inode(const struct inode *inode, const char *function, + unsigned int line, const char *fmt, ...); +//extern __printf(3, 4) +void __ext4_msg(struct super_block *, const char *, const char *, ...); +extern void __dump_mmp_msg(struct super_block *, struct mmp_struct *mmp, + const char *, unsigned int, const char *); +//extern __printf(7, 8) +void __ext4_grp_locked_error(const char *, unsigned int, + struct super_block *, ext4_group_t, + unsigned long, ext4_fsblk_t, + const char *, ...); +#if 0 +#define EXT4_ERROR_INODE(inode, fmt, a...) \ + ext4_error_inode((inode), __func__, __LINE__, 0, (fmt), ## a) + +#define EXT4_ERROR_INODE_BLOCK(inode, block, fmt, a...) \ + ext4_error_inode((inode), __func__, __LINE__, (block), (fmt), ## a) + +#define EXT4_ERROR_FILE(file, block, fmt, a...) \ + ext4_error_file((file), __func__, __LINE__, (block), (fmt), ## a) +#endif +#ifdef CONFIG_PRINTK + +#define ext4_error_inode(inode, func, line, block, fmt, ...) \ + __ext4_error_inode(inode, func, line, block, fmt, ##__VA_ARGS__) +#define ext4_error_file(file, func, line, block, fmt, ...) \ + __ext4_error_file(file, func, line, block, fmt, ##__VA_ARGS__) +/*#define ext4_error(sb, fmt, ...) \ + __ext4_error(sb, __func__, __LINE__, fmt, ##__VA_ARGS__)*/ +#define ext4_abort(sb, fmt, ...) \ + __ext4_abort(sb, __func__, __LINE__, fmt, ##__VA_ARGS__) +#define ext4_warning(sb, fmt, ...) \ + __ext4_warning(sb, __func__, __LINE__, fmt, ##__VA_ARGS__) +#define ext4_warning_inode(inode, fmt, ...) \ + __ext4_warning_inode(inode, __func__, __LINE__, fmt, ##__VA_ARGS__) +#define ext4_msg(sb, level, fmt, ...) \ + __ext4_msg(sb, level, fmt, ##__VA_ARGS__) +#define dump_mmp_msg(sb, mmp, msg) \ + __dump_mmp_msg(sb, mmp, __func__, __LINE__, msg) +#define ext4_grp_locked_error(sb, grp, ino, block, fmt, ...) \ + __ext4_grp_locked_error(__func__, __LINE__, sb, grp, ino, block, \ + fmt, ##__VA_ARGS__) + +#else + +#define ext4_error_inode(inode, func, line, block, fmt, ...) \ +do { \ + no_printk(fmt, ##__VA_ARGS__); \ + __ext4_error_inode(inode, "", 0, block, " "); \ +} while (0) +#define ext4_error_file(file, func, line, block, fmt, ...) \ +do { \ + no_printk(fmt, ##__VA_ARGS__); \ + __ext4_error_file(file, "", 0, block, " "); \ +} while (0) +#if 0 +#define ext4_error(sb, fmt, ...) \ +do { \ + no_printk(fmt, ##__VA_ARGS__); \ + __ext4_error(sb, "", 0, " "); \ +} while (0) +#endif +#define ext4_abort(sb, fmt, ...) \ +do { \ + no_printk(fmt, ##__VA_ARGS__); \ + __ext4_abort(sb, "", 0, " "); \ +} while (0) +#define ext4_warning(sb, fmt, ...) \ +do { \ + no_printk(fmt, ##__VA_ARGS__); \ + __ext4_warning(sb, "", 0, " "); \ +} while (0) +#define ext4_warning_inode(inode, fmt, ...) \ +do { \ + no_printk(fmt, ##__VA_ARGS__); \ + __ext4_warning_inode(inode, "", 0, " "); \ +} while (0) +#define ext4_msg(sb, level, fmt, ...) \ +do { \ + no_printk(fmt, ##__VA_ARGS__); \ + __ext4_msg(sb, "", " "); \ +} while (0) +#define dump_mmp_msg(sb, mmp, msg) \ + __dump_mmp_msg(sb, mmp, "", 0, "") +#define ext4_grp_locked_error(sb, grp, ino, block, fmt, ...) \ +do { \ + no_printk(fmt, ##__VA_ARGS__); \ + __ext4_grp_locked_error("", 0, sb, grp, ino, block, " "); \ +} while (0) + +#endif + +extern void ext4_update_dynamic_rev(struct super_block *sb); +extern int ext4_update_compat_feature(handle_t *handle, struct super_block *sb, + __u32 compat); +extern int ext4_update_rocompat_feature(handle_t *handle, + struct super_block *sb, __u32 rocompat); +extern int ext4_update_incompat_feature(handle_t *handle, + struct super_block *sb, __u32 incompat); +extern ext4_fsblk_t ext4_block_bitmap(struct super_block *sb, + struct ext4_group_desc *bg); +extern ext4_fsblk_t ext4_inode_bitmap(struct super_block *sb, + struct ext4_group_desc *bg); +extern ext4_fsblk_t ext4_inode_table(struct super_block *sb, + struct ext4_group_desc *bg); +extern __u32 ext4_free_group_clusters(struct super_block *sb, + struct ext4_group_desc *bg); +extern __u32 ext4_free_inodes_count(struct super_block *sb, + struct ext4_group_desc *bg); +extern __u32 ext4_used_dirs_count(struct super_block *sb, + struct ext4_group_desc *bg); +extern __u32 ext4_itable_unused_count(struct super_block *sb, + struct ext4_group_desc *bg); +extern void ext4_block_bitmap_set(struct super_block *sb, + struct ext4_group_desc *bg, ext4_fsblk_t blk); +extern void ext4_inode_bitmap_set(struct super_block *sb, + struct ext4_group_desc *bg, ext4_fsblk_t blk); +extern void ext4_inode_table_set(struct super_block *sb, + struct ext4_group_desc *bg, ext4_fsblk_t blk); +extern void ext4_free_group_clusters_set(struct super_block *sb, + struct ext4_group_desc *bg, + __u32 count); +extern void ext4_free_inodes_set(struct super_block *sb, + struct ext4_group_desc *bg, __u32 count); +extern void ext4_used_dirs_set(struct super_block *sb, + struct ext4_group_desc *bg, __u32 count); +extern void ext4_itable_unused_set(struct super_block *sb, + struct ext4_group_desc *bg, __u32 count); +/*extern int ext4_group_desc_csum_verify(struct super_block *sb, __u32 group, + struct ext4_group_desc *gdp); +extern void ext4_group_desc_csum_set(struct super_block *sb, __u32 group, + struct ext4_group_desc *gdp);*/ +extern int ext4_register_li_request(struct super_block *sb, + ext4_group_t first_not_zeroed); + +static inline int ext4_has_metadata_csum(struct super_block *sb) +{ + /*WARN_ON_ONCE(ext4_has_feature_metadata_csum(sb) && + !EXT4_SB(sb)->s_chksum_driver);*/ + + return ext4_has_feature_metadata_csum(sb)/* && + (EXT4_SB(sb)->s_chksum_driver != NULL)*/; +} + +static inline int ext4_has_group_desc_csum(struct super_block *sb) +{ + return ext4_has_feature_gdt_csum(sb) || ext4_has_metadata_csum(sb); +} + +static inline ext4_fsblk_t ext4_blocks_count(struct ext4_super_block *es) +{ + return ((ext4_fsblk_t)le32_to_cpu(es->s_blocks_count_hi) << 32) | + le32_to_cpu(es->s_blocks_count_lo); +} + +static inline ext4_fsblk_t ext4_r_blocks_count(struct ext4_super_block *es) +{ + return ((ext4_fsblk_t)le32_to_cpu(es->s_r_blocks_count_hi) << 32) | + le32_to_cpu(es->s_r_blocks_count_lo); +} + +static inline ext4_fsblk_t ext4_free_blocks_count(struct ext4_super_block *es) +{ + return ((ext4_fsblk_t)le32_to_cpu(es->s_free_blocks_count_hi) << 32) | + le32_to_cpu(es->s_free_blocks_count_lo); +} + +static inline void ext4_blocks_count_set(struct ext4_super_block *es, + ext4_fsblk_t blk) +{ + es->s_blocks_count_lo = cpu_to_le32((__u32)blk); + es->s_blocks_count_hi = cpu_to_le32(blk >> 32); +} + +static inline void ext4_free_blocks_count_set(struct ext4_super_block *es, + ext4_fsblk_t blk) +{ + es->s_free_blocks_count_lo = cpu_to_le32((__u32)blk); + es->s_free_blocks_count_hi = cpu_to_le32(blk >> 32); +} + +static inline void ext4_r_blocks_count_set(struct ext4_super_block *es, + ext4_fsblk_t blk) +{ + es->s_r_blocks_count_lo = cpu_to_le32((__u32)blk); + es->s_r_blocks_count_hi = cpu_to_le32(blk >> 32); +} + +static inline loff_t ext4_isize(struct super_block *sb, + struct ext4_inode *raw_inode) +{ + if (ext4_has_feature_largedir(sb) || + S_ISREG(le16_to_cpu(raw_inode->i_mode))) + return ((loff_t)le32_to_cpu(raw_inode->i_size_high) << 32) | + le32_to_cpu(raw_inode->i_size_lo); + + return (loff_t) le32_to_cpu(raw_inode->i_size_lo); +} + +static inline void ext4_isize_set(struct ext4_inode *raw_inode, loff_t i_size) +{ + raw_inode->i_size_lo = cpu_to_le32(i_size); + raw_inode->i_size_high = cpu_to_le32(i_size >> 32); +} +#if 0 +static inline +struct ext4_group_info *ext4_get_group_info(struct super_block *sb, + ext4_group_t group) +{ + struct ext4_group_info ***grp_info; + long indexv, indexh; + BUG_ON(group >= EXT4_SB(sb)->s_groups_count); + grp_info = EXT4_SB(sb)->s_group_info; + indexv = group >> (EXT4_DESC_PER_BLOCK_BITS(sb)); + indexh = group & ((EXT4_DESC_PER_BLOCK(sb)) - 1); + return grp_info[indexv][indexh]; +} +#endif +/* + * Reading s_groups_count requires using smp_rmb() afterwards. See + * the locking protocol documented in the comments of ext4_group_add() + * in resize.c + */ +static inline ext4_group_t ext4_get_groups_count(struct super_block *sb) +{ + ext4_group_t ngroups = EXT4_SB(sb)->s_groups_count; + + smp_rmb(); + return ngroups; +} +#if 0 +static inline ext4_group_t ext4_flex_group(struct ext4_sb_info *sbi, + ext4_group_t block_group) +{ + return block_group >> sbi->s_log_groups_per_flex; +} + +static inline unsigned int ext4_flex_bg_size(struct ext4_sb_info *sbi) +{ + return 1 << sbi->s_log_groups_per_flex; +} + +#define ext4_std_error(sb, errno) \ +do { \ + if ((errno)) \ + __ext4_std_error((sb), __func__, __LINE__, (errno)); \ +} while (0) +#endif +#ifdef CONFIG_SMP +/* Each CPU can accumulate percpu_counter_batch clusters in their local + * counters. So we need to make sure we have free clusters more + * than percpu_counter_batch * nr_cpu_ids. Also add a window of 4 times. + */ +#define EXT4_FREECLUSTERS_WATERMARK (4 * (percpu_counter_batch * nr_cpu_ids)) +#else +#define EXT4_FREECLUSTERS_WATERMARK 0 +#endif +#if 0 +/* Update i_disksize. Requires i_mutex to avoid races with truncate */ +static inline void ext4_update_i_disksize(struct inode *inode, loff_t newsize) +{ + /*WARN_ON_ONCE(S_ISREG(inode->i_mode) && + !inode_is_locked(inode));*/ + down_write(&EXT4_I(inode)->i_data_sem); + if (newsize > EXT4_I(inode)->i_disksize) + EXT4_I(inode)->i_disksize = newsize; + up_write(&EXT4_I(inode)->i_data_sem); +} + +/* Update i_size, i_disksize. Requires i_mutex to avoid races with truncate */ +static inline int ext4_update_inode_size(struct inode *inode, loff_t newsize) +{ + int changed = 0; + + if (newsize > inode->i_size) { + i_size_write(inode, newsize); + changed = 1; + } + if (newsize > EXT4_I(inode)->i_disksize) { + ext4_update_i_disksize(inode, newsize); + changed |= 2; + } + return changed; +} +#endif +int ext4_update_disksize_before_punch(struct inode *inode, loff_t offset, + loff_t len); +#if 0 +struct ext4_group_info { + unsigned long bb_state; + struct rb_root bb_free_root; + ext4_grpblk_t bb_first_free; /* first free block */ + ext4_grpblk_t bb_free; /* total free blocks */ + ext4_grpblk_t bb_fragments; /* nr of freespace fragments */ + ext4_grpblk_t bb_largest_free_order;/* order of largest frag in BG */ + struct list_head bb_prealloc_list; +#ifdef DOUBLE_CHECK + void *bb_bitmap; +#endif + //struct rw_semaphore alloc_sem; + ext4_grpblk_t bb_counters[]; /* Nr of free power-of-two-block + * regions, index is order. + * bb_counters[3] = 5 means + * 5 free 8-block regions. */ +}; +#endif +#define EXT4_GROUP_INFO_NEED_INIT_BIT 0 +#define EXT4_GROUP_INFO_WAS_TRIMMED_BIT 1 +#define EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT 2 +#define EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT 3 +#define EXT4_GROUP_INFO_BBITMAP_CORRUPT \ + (1 << EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT) +#define EXT4_GROUP_INFO_IBITMAP_CORRUPT \ + (1 << EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT) + +#define EXT4_MB_GRP_NEED_INIT(grp) \ + (test_bit(EXT4_GROUP_INFO_NEED_INIT_BIT, &((grp)->bb_state))) +#define EXT4_MB_GRP_BBITMAP_CORRUPT(grp) \ + (test_bit(EXT4_GROUP_INFO_BBITMAP_CORRUPT_BIT, &((grp)->bb_state))) +#define EXT4_MB_GRP_IBITMAP_CORRUPT(grp) \ + (test_bit(EXT4_GROUP_INFO_IBITMAP_CORRUPT_BIT, &((grp)->bb_state))) + +#define EXT4_MB_GRP_WAS_TRIMMED(grp) \ + (test_bit(EXT4_GROUP_INFO_WAS_TRIMMED_BIT, &((grp)->bb_state))) +#define EXT4_MB_GRP_SET_TRIMMED(grp) \ + (set_bit(EXT4_GROUP_INFO_WAS_TRIMMED_BIT, &((grp)->bb_state))) +#define EXT4_MB_GRP_CLEAR_TRIMMED(grp) \ + (clear_bit(EXT4_GROUP_INFO_WAS_TRIMMED_BIT, &((grp)->bb_state))) + +#define EXT4_MAX_CONTENTION 8 +#define EXT4_CONTENTION_THRESHOLD 2 +#if 0 +static inline spinlock_t *ext4_group_lock_ptr(struct super_block *sb, + ext4_group_t group) +{ + return bgl_lock_ptr(EXT4_SB(sb)->s_blockgroup_lock, group); +} + +/* + * Returns true if the filesystem is busy enough that attempts to + * access the block group locks has run into contention. + */ +static inline int ext4_fs_is_busy(struct ext4_sb_info *sbi) +{ + return (atomic_read(&sbi->s_lock_busy) > EXT4_CONTENTION_THRESHOLD); +} + +static inline void ext4_lock_group(struct super_block *sb, ext4_group_t group) +{ + spinlock_t *lock = ext4_group_lock_ptr(sb, group); + if (spin_trylock(lock)) + /* + * We're able to grab the lock right away, so drop the + * lock contention counter. + */ + atomic_add_unless(&EXT4_SB(sb)->s_lock_busy, -1, 0); + else { + /* + * The lock is busy, so bump the contention counter, + * and then wait on the spin lock. + */ + atomic_add_unless(&EXT4_SB(sb)->s_lock_busy, 1, + EXT4_MAX_CONTENTION); + spin_lock(lock); + } +} + +static inline void ext4_unlock_group(struct super_block *sb, + ext4_group_t group) +{ + spin_unlock(ext4_group_lock_ptr(sb, group)); +} +#endif +/* + * Block validity checking + */ +#define ext4_check_indirect_blockref(inode, bh) \ + ext4_check_blockref(__func__, __LINE__, inode, \ + (__le32 *)(bh)->b_data, \ + EXT4_ADDR_PER_BLOCK((inode)->i_sb)) + +#define ext4_ind_check_inode(inode) \ + ext4_check_blockref(__func__, __LINE__, inode, \ + EXT4_I(inode)->i_data, \ + EXT4_NDIR_BLOCKS) + +/* + * Inodes and files operations + */ + +/* dir.c */ +extern const struct file_operations ext4_dir_operations; + +/* file.c */ +extern const struct inode_operations ext4_file_inode_operations; +extern const struct file_operations ext4_file_operations; +extern loff_t ext4_llseek(struct file *file, loff_t offset, int origin); + +/* inline.c */ +extern int ext4_get_max_inline_size(struct inode *inode); +extern int ext4_find_inline_data_nolock(struct inode *inode); +extern int ext4_init_inline_data(handle_t *handle, struct inode *inode, + unsigned int len); +extern int ext4_destroy_inline_data(handle_t *handle, struct inode *inode); + +extern int ext4_readpage_inline(struct inode *inode, struct page *page); +extern int ext4_try_to_write_inline_data(struct address_space *mapping, + struct inode *inode, + loff_t pos, unsigned len, + unsigned flags, + struct page **pagep); +extern int ext4_write_inline_data_end(struct inode *inode, + loff_t pos, unsigned len, + unsigned copied, + struct page *page); +extern struct buffer_head * +ext4_journalled_write_inline_data(struct inode *inode, + unsigned len, + struct page *page); +extern int ext4_da_write_inline_data_begin(struct address_space *mapping, + struct inode *inode, + loff_t pos, unsigned len, + unsigned flags, + struct page **pagep, + void **fsdata); +extern int ext4_da_write_inline_data_end(struct inode *inode, loff_t pos, + unsigned len, unsigned copied, + struct page *page); +extern int ext4_try_add_inline_entry(handle_t *handle, + struct ext4_filename *fname, + struct inode *dir, struct inode *inode); +extern int ext4_try_create_inline_dir(handle_t *handle, + struct inode *parent, + struct inode *inode); +extern int ext4_read_inline_dir(struct file *filp, + struct dir_context *ctx, + int *has_inline_data); +extern int htree_inlinedir_to_tree(struct file *dir_file, + struct inode *dir, ext4_lblk_t block, + struct dx_hash_info *hinfo, + __u32 start_hash, __u32 start_minor_hash, + int *has_inline_data); +extern struct buffer_head *ext4_find_inline_entry(struct inode *dir, + struct ext4_filename *fname, + struct ext4_dir_entry_2 **res_dir, + int *has_inline_data); +extern int ext4_delete_inline_entry(handle_t *handle, + struct inode *dir, + struct ext4_dir_entry_2 *de_del, + struct buffer_head *bh, + int *has_inline_data); +extern bool empty_inline_dir(struct inode *dir, int *has_inline_data); +extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode, + struct ext4_dir_entry_2 **parent_de, + int *retval); +extern int ext4_inline_data_fiemap(struct inode *inode, + struct fiemap_extent_info *fieinfo, + int *has_inline, __u64 start, __u64 len); + +struct iomap; +extern int ext4_inline_data_iomap(struct inode *inode, struct iomap *iomap); + +extern int ext4_inline_data_truncate(struct inode *inode, int *has_inline); + +extern int ext4_convert_inline_data(struct inode *inode); +#if 0 +static inline int ext4_has_inline_data(struct inode *inode) +{ + return ext4_test_inode_flag(inode, EXT4_INODE_INLINE_DATA) && + EXT4_I(inode)->i_inline_off; +} +#endif +/* namei.c */ +extern const struct inode_operations ext4_dir_inode_operations; +extern const struct inode_operations ext4_special_inode_operations; +extern struct dentry *ext4_get_parent(struct dentry *child); +extern struct ext4_dir_entry_2 *ext4_init_dot_dotdot(struct inode *inode, + struct ext4_dir_entry_2 *de, + int blocksize, int csum_size, + unsigned int parent_ino, int dotdot_real_len); +extern void initialize_dirent_tail(struct ext4_dir_entry_tail *t, + unsigned int blocksize); +extern int ext4_handle_dirty_dirent_node(handle_t *handle, + struct inode *inode, + struct buffer_head *bh); +#define S_SHIFT 12 +#if 0 +static const unsigned char ext4_type_by_mode[(S_IFMT >> S_SHIFT) + 1] = { + [S_IFREG >> S_SHIFT] = EXT4_FT_REG_FILE, + [S_IFDIR >> S_SHIFT] = EXT4_FT_DIR, + [S_IFCHR >> S_SHIFT] = EXT4_FT_CHRDEV, + [S_IFBLK >> S_SHIFT] = EXT4_FT_BLKDEV, + [S_IFIFO >> S_SHIFT] = EXT4_FT_FIFO, + [S_IFSOCK >> S_SHIFT] = EXT4_FT_SOCK, + [S_IFLNK >> S_SHIFT] = EXT4_FT_SYMLINK, +}; + +static inline void ext4_set_de_type(struct super_block *sb, + struct ext4_dir_entry_2 *de, + umode_t mode) { + if (ext4_has_feature_filetype(sb)) + de->file_type = ext4_type_by_mode[(mode & S_IFMT)>>S_SHIFT]; +} +#endif +/* readpages.c */ +extern int ext4_mpage_readpages(struct address_space *mapping, + struct list_head *pages, struct page *page, + unsigned nr_pages, bool is_readahead); + +/* symlink.c */ +extern const struct inode_operations ext4_encrypted_symlink_inode_operations; +extern const struct inode_operations ext4_symlink_inode_operations; +extern const struct inode_operations ext4_fast_symlink_inode_operations; + +/* sysfs.c */ +extern int ext4_register_sysfs(struct super_block *sb); +extern void ext4_unregister_sysfs(struct super_block *sb); +extern int __init ext4_init_sysfs(void); +extern void ext4_exit_sysfs(void); + +/* block_validity */ +extern void ext4_release_system_zone(struct super_block *sb); +extern int ext4_setup_system_zone(struct super_block *sb); +extern int __init ext4_init_system_zone(void); +extern void ext4_exit_system_zone(void); +extern int ext4_data_block_valid(struct ext4_sb_info *sbi, + ext4_fsblk_t start_blk, + unsigned int count); +extern int ext4_check_blockref(const char *, unsigned int, + struct inode *, __le32 *, unsigned int); + +/* extents.c */ +struct ext4_ext_path; +struct ext4_extent; + +/* + * Maximum number of logical blocks in a file; ext4_extent's ee_block is + * __le32. + */ +#define EXT_MAX_BLOCKS 0xffffffff + +//extern int ext4_ext_tree_init(handle_t *handle, struct inode *); +extern int ext4_ext_writepage_trans_blocks(struct inode *, int); +extern int ext4_ext_index_trans_blocks(struct inode *inode, int extents); +extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, + struct ext4_map_blocks *map, int flags); +//extern int ext4_ext_truncate(handle_t *, struct inode *); +/*extern int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start, + ext4_lblk_t end);*/ +extern void ext4_ext_init(struct super_block *); +extern void ext4_ext_release(struct super_block *); +extern long ext4_fallocate(struct file *file, int mode, loff_t offset, + loff_t len); +extern int ext4_convert_unwritten_extents(handle_t *handle, struct inode *inode, + loff_t offset, ssize_t len); +extern int ext4_map_blocks(handle_t *handle, struct inode *inode, + struct ext4_map_blocks *map, int flags); +extern int ext4_ext_calc_metadata_amount(struct inode *inode, + ext4_lblk_t lblocks); +extern int ext4_ext_calc_credits_for_single_extent(struct inode *inode, + int num, + struct ext4_ext_path *path); +extern int ext4_can_extents_be_merged(struct inode *inode, + struct ext4_extent *ex1, + struct ext4_extent *ex2); +/*extern int ext4_ext_insert_extent(handle_t *, struct inode *, + struct ext4_ext_path **, + struct ext4_extent *, int);*/ +extern struct ext4_ext_path *ext4_find_extent(struct inode *, ext4_lblk_t, + struct ext4_ext_path **, + int flags); +extern void ext4_ext_drop_refs(struct ext4_ext_path *); +extern int ext4_ext_check_inode(struct inode *inode); +extern ext4_lblk_t ext4_ext_next_allocated_block(struct ext4_ext_path *path); +extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, + __u64 start, __u64 len); +extern int ext4_ext_precache(struct inode *inode); +extern int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len); +extern int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len); +extern int ext4_swap_extents(handle_t *handle, struct inode *inode1, + struct inode *inode2, ext4_lblk_t lblk1, + ext4_lblk_t lblk2, ext4_lblk_t count, + int mark_unwritten,int *err); +extern int ext4_clu_mapped(struct inode *inode, ext4_lblk_t lclu); + +/* move_extent.c */ +extern void ext4_double_down_write_data_sem(struct inode *first, + struct inode *second); +extern void ext4_double_up_write_data_sem(struct inode *orig_inode, + struct inode *donor_inode); +extern int ext4_move_extents(struct file *o_filp, struct file *d_filp, + __u64 start_orig, __u64 start_donor, + __u64 len, __u64 *moved_len); + +/* page-io.c */ +extern int __init ext4_init_pageio(void); +extern void ext4_exit_pageio(void); +extern ext4_io_end_t *ext4_init_io_end(struct inode *inode, gfp_t flags); +extern ext4_io_end_t *ext4_get_io_end(ext4_io_end_t *io_end); +extern int ext4_put_io_end(ext4_io_end_t *io_end); +extern void ext4_put_io_end_defer(ext4_io_end_t *io_end); +extern void ext4_io_submit_init(struct ext4_io_submit *io, + struct writeback_control *wbc); +extern void ext4_end_io_rsv_work(struct work_struct *work); +extern void ext4_io_submit(struct ext4_io_submit *io); +extern int ext4_bio_write_page(struct ext4_io_submit *io, + struct page *page, + int len, + struct writeback_control *wbc, + bool keep_towrite); + +/* mmp.c */ +extern int ext4_multi_mount_protect(struct super_block *, ext4_fsblk_t); +#if 0 +/* + * Add new method to test whether block and inode bitmaps are properly + * initialized. With uninit_bg reading the block from disk is not enough + * to mark the bitmap uptodate. We need to also zero-out the bitmap + */ +#define BH_BITMAP_UPTODATE BH_JBDPrivateStart + +static inline int bitmap_uptodate(struct buffer_head *bh) +{ + return (buffer_uptodate(bh) && + test_bit(BH_BITMAP_UPTODATE, &(bh)->b_state)); +} +static inline void set_bitmap_uptodate(struct buffer_head *bh) +{ + set_bit(BH_BITMAP_UPTODATE, &(bh)->b_state); +} +#endif +#define in_range(b, first, len) ((b) >= (first) && (b) <= (first) + (len) - 1) + +/* For ioend & aio unwritten conversion wait queues */ +#define EXT4_WQ_HASH_SZ 37 +#define ext4_ioend_wq(v) (&ext4__ioend_wq[((unsigned long)(v)) %\ + EXT4_WQ_HASH_SZ]) +extern wait_queue_head_t ext4__ioend_wq[EXT4_WQ_HASH_SZ]; + +extern int ext4_resize_begin(struct super_block *sb); +extern void ext4_resize_end(struct super_block *sb); +#if 0 +static inline void ext4_set_io_unwritten_flag(struct inode *inode, + struct ext4_io_end *io_end) +{ + if (!(io_end->flag & EXT4_IO_END_UNWRITTEN)) { + io_end->flag |= EXT4_IO_END_UNWRITTEN; + atomic_inc(&EXT4_I(inode)->i_unwritten); + } +} + +static inline void ext4_clear_io_unwritten_flag(ext4_io_end_t *io_end) +{ + struct inode *inode = io_end->inode; + + if (io_end->flag & EXT4_IO_END_UNWRITTEN) { + io_end->flag &= ~EXT4_IO_END_UNWRITTEN; + /* Wake up anyone waiting on unwritten extent conversion */ + if (atomic_dec_and_test(&EXT4_I(inode)->i_unwritten)) + wake_up_all(ext4_ioend_wq(inode)); + } +} +#endif +extern const struct iomap_ops ext4_iomap_ops; + +#endif /* __KERNEL__ */ + +#define EFSBADCRC EBADMSG /* Bad CRC detected */ +#define EFSCORRUPTED EUCLEAN /* Filesystem is corrupted */ + +/* + * End of Linux ext4.h, below are ext3 inline functions still used by the Windows driver. + */ + +/* + * Hash Tree Directory indexing + * (c) Daniel Phillips, 2001 + */ + +#undef is_dx +#ifdef EXT2_HTREE_INDEX +#define is_dx(dir) (EXT3_HAS_COMPAT_FEATURE(dir->i_sb, \ + EXT4_FEATURE_COMPAT_DIR_INDEX) && \ + (EXT3_I(dir)->i_flags & EXT3_INDEX_FL)) +#define EXT4_DIR_LINK_MAX(dir) (!is_dx(dir) && (dir)->i_nlink >= EXT3_LINK_MAX) +#define EXT4_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2 || (dir)->i_nlink == 1) +#else +#define is_dx(dir) 0 +#define EXT4_DIR_LINK_MAX(dir) ((dir)->i_nlink >= EXT3_LINK_MAX) +#define EXT4_DIR_LINK_EMPTY(dir) ((dir)->i_nlink == 2) +#endif + +struct fake_dirent +{ + __le32 inode; + __le16 rec_len; + __u8 name_len; + __u8 file_type; +}; + +struct dx_countlimit +{ + __le16 limit; + __le16 count; +}; + +struct dx_entry +{ + __le32 hash; + __le32 block; +}; + +/* + * dx_root_info is laid out so that if it should somehow get overlaid by a + * dirent the two low bits of the hash version will be zero. Therefore, the + * hash version mod 4 should never be 0. Sincerely, the paranoia department. + */ + +struct dx_root +{ + struct fake_dirent dot; + char dot_name[4]; + struct fake_dirent dotdot; + char dotdot_name[4]; + struct dx_root_info + { + __le32 reserved_zero; + __u8 hash_version; + __u8 info_length; /* 8 */ + __u8 indirect_levels; + __u8 unused_flags; + } + info; + struct dx_entry entries[0]; +}; + +struct dx_node +{ + struct fake_dirent fake; + struct dx_entry entries[0]; +}; + + +struct dx_frame +{ + struct buffer_head *bh; + struct dx_entry *entries; + struct dx_entry *at; +}; + +struct dx_map_entry +{ + __u32 hash; + __u16 offs; + __u16 size; +}; + +/* + * This goes at the end of each htree block. + */ +struct dx_tail { + u32 dt_reserved; + __le32 dt_checksum; /* crc32c(uuid+inum+dirblock) */ +}; + +static inline unsigned ext3_rec_len_from_disk(__le16 dlen) +{ + unsigned len = le16_to_cpu(dlen); + + if (len == EXT3_MAX_REC_LEN || len == 0) + return 1 << 16; + return len; +} + +static inline __le16 ext3_rec_len_to_disk(unsigned len) +{ + if (len == (1 << 16)) + return cpu_to_le16(EXT3_MAX_REC_LEN); + else if (len > (1 << 16)) + BUG(); + return cpu_to_le16(len); +} + +/* + * Compute the total directory entry data length. + * This includes the filename and an implicit NUL terminator (always present), + * and optional extensions. Each extension has a bit set in the high 4 bits of + * de->file_type, and the extension length is the first byte in each entry. + */ +static inline int ext3_get_dirent_data_len(struct ext3_dir_entry_2 *de) +{ + char *len = de->name + de->name_len + 1 /* NUL terminator */; + int dlen = 0; + __u8 extra_data_flags = (de->file_type & ~EXT3_FT_MASK) >> 4; + + while (extra_data_flags) { + if (extra_data_flags & 1) { + dlen += *len + (dlen == 0); + len += *len; + } + extra_data_flags >>= 1; + } + return dlen; +} + +#if _MSC_VER > 1900 +int _strnicmp(const char* str1, const char* str2, size_t count); +#endif + +/* + * NOTE! unlike strncmp, ext3_match returns 1 for success, 0 for failure. + * + * `len <= EXT3_NAME_LEN' is guaranteed by caller. + * `de != NULL' is guaranteed by caller. + */ +static inline int ext3_match (int len, const char * const name, + struct ext3_dir_entry_2 * de) +{ + if (len != de->name_len) + return 0; + if (!de->inode) + return 0; + return !_strnicmp(name, de->name, len); +} + +/* calculate the first block number of the group */ +static inline ext3_fsblk_t +ext3_group_first_block_no(struct super_block *sb, unsigned long group_no) +{ + return group_no * (ext3_fsblk_t)EXT3_BLOCKS_PER_GROUP(sb) + + le32_to_cpu(EXT3_SB(sb)->s_es->s_first_data_block); +} + +/* + * Functions for metadata checksums in ext4_csum.c. + */ + +//int ext4_has_feature_metadata_csum(struct super_block *sb); +__u32 ext4_chksum(struct ext4_sb_info *sbi, __u32 crc, + const void *buffer, unsigned int length); +int ext4_superblock_csum_verify(struct super_block *sb, + struct ext4_super_block *es); +void ext4_superblock_csum_set(struct super_block *sb); +__le16 ext4_group_desc_csum(struct super_block *sb, __u32 block_group, + struct ext4_group_desc *gdp); +int ext4_group_desc_csum_verify(struct super_block *sb, __u32 block_group, + struct ext4_group_desc *gdp); +void ext4_group_desc_csum_set(struct super_block *sb, __u32 block_group, + struct ext4_group_desc *gdp); +void ext4_inode_bitmap_csum_set(struct super_block *sb, ext4_group_t group, + struct ext4_group_desc *gdp, + struct buffer_head *bh, int sz); +void ext4_block_bitmap_csum_set(struct super_block *sb, ext4_group_t group, + struct ext4_group_desc *gdp, + struct buffer_head *bh); +int ext4_inode_csum_verify(struct inode *inode, struct ext4_inode *raw, + struct ext4_inode_info *ei); +void ext4_inode_csum_set(struct inode *inode, struct ext4_inode *raw, + struct ext4_inode_info *ei); +void initialize_dirent_tail(struct ext4_dir_entry_tail *t, + unsigned int blocksize); +int ext4_dirent_csum_verify(struct inode *inode, struct ext4_dir_entry *dirent); +void ext4_dirent_csum_set(struct inode *inode, + struct ext4_dir_entry *dirent); +int ext4_dx_csum_verify(struct inode *inode, + struct ext4_dir_entry *dirent); +void ext4_dx_csum_set(struct inode *inode, struct ext4_dir_entry *dirent); + +// +// Use default packing of structures +// +#include + +#endif /* _EXT4_H */ diff --git a/Ext4Fsd/include/linux/ext4_xattr.h b/Ext4Fsd/include/linux/ext4_xattr.h index d57949c..44d5f91 100644 --- a/Ext4Fsd/include/linux/ext4_xattr.h +++ b/Ext4Fsd/include/linux/ext4_xattr.h @@ -86,7 +86,7 @@ struct ext4_xattr_entry { #pragma pack(pop) -#define EXT4_GOOD_OLD_INODE_SIZE EXT2_GOOD_OLD_INODE_SIZE +//#define EXT4_GOOD_OLD_INODE_SIZE EXT2_GOOD_OLD_INODE_SIZE #define EXT4_XATTR_PAD_BITS 2 #define EXT4_XATTR_PAD (1< - * HOMEPAGE: http://www.ext2fsd.com - * UPDATE HISTORY: - */ - -/* INCLUDES *****************************************************************/ - -#include "ext2fs.h" - -/* GLOBALS ***************************************************************/ - -extern PEXT2_GLOBAL Ext2Global; - -/* DEFINITIONS *************************************************************/ - -#ifdef ALLOC_PRAGMA -#pragma alloc_text(PAGE, Ext2AllocateInode) -#pragma alloc_text(PAGE, Ext2DestroyInode) -#pragma alloc_text(PAGE, Ext2CheckBitmapConsistency) -#pragma alloc_text(PAGE, Ext2CheckSetBlock) -#pragma alloc_text(PAGE, Ext2InitializeVcb) -#pragma alloc_text(PAGE, Ext2TearDownStream) -#pragma alloc_text(PAGE, Ext2DestroyVcb) -#pragma alloc_text(PAGE, Ext2SyncUninitializeCacheMap) -#pragma alloc_text(PAGE, Ext2ReaperThread) -#pragma alloc_text(PAGE, Ext2StartReaper) -#pragma alloc_text(PAGE, Ext2StopReaper) -#endif - -PEXT2_IRP_CONTEXT -Ext2AllocateIrpContext (IN PDEVICE_OBJECT DeviceObject, - IN PIRP Irp ) -{ - PIO_STACK_LOCATION irpSp; - PEXT2_IRP_CONTEXT IrpContext; - - ASSERT(DeviceObject != NULL); - ASSERT(Irp != NULL); - - irpSp = IoGetCurrentIrpStackLocation(Irp); - - IrpContext = (PEXT2_IRP_CONTEXT) ( - ExAllocateFromNPagedLookasideList( - &(Ext2Global->Ext2IrpContextLookasideList))); - - if (IrpContext == NULL) { - return NULL; - } - - RtlZeroMemory(IrpContext, sizeof(EXT2_IRP_CONTEXT) ); - - IrpContext->Identifier.Type = EXT2ICX; - IrpContext->Identifier.Size = sizeof(EXT2_IRP_CONTEXT); - - IrpContext->Irp = Irp; - IrpContext->MajorFunction = irpSp->MajorFunction; - IrpContext->MinorFunction = irpSp->MinorFunction; - IrpContext->DeviceObject = DeviceObject; - IrpContext->FileObject = irpSp->FileObject; - if (NULL != IrpContext->FileObject) { - IrpContext->Fcb = (PEXT2_FCB)IrpContext->FileObject->FsContext; - IrpContext->Ccb = (PEXT2_CCB)IrpContext->FileObject->FsContext2; - } - - if (IrpContext->FileObject != NULL) { - IrpContext->RealDevice = IrpContext->FileObject->DeviceObject; - } else if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) { - if (irpSp->Parameters.MountVolume.Vpb) { - IrpContext->RealDevice = irpSp->Parameters.MountVolume.Vpb->RealDevice; - } - } - - if (IsFlagOn(irpSp->Flags, SL_WRITE_THROUGH)) { - SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); - } - - if (IsFlagOn(irpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME)) { - SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_VERIFY_READ); - } - - if (IrpContext->MajorFunction == IRP_MJ_CLEANUP || - IrpContext->MajorFunction == IRP_MJ_CLOSE || - IrpContext->MajorFunction == IRP_MJ_SHUTDOWN || - IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL || - IrpContext->MajorFunction == IRP_MJ_PNP ) { - - if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL || - IrpContext->MajorFunction == IRP_MJ_PNP) { - if (IoGetCurrentIrpStackLocation(Irp)->FileObject == NULL || - IoIsOperationSynchronous(Irp)) { - SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); - } - } else { - SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); - } - - } else if (IoIsOperationSynchronous(Irp)) { - - SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); - } - - IrpContext->IsTopLevel = (IoGetTopLevelIrp() == Irp); - IrpContext->ExceptionInProgress = FALSE; - INC_IRP_COUNT(IrpContext); - - return IrpContext; -} - -VOID -Ext2FreeIrpContext (IN PEXT2_IRP_CONTEXT IrpContext) -{ - ASSERT(IrpContext != NULL); - - ASSERT((IrpContext->Identifier.Type == EXT2ICX) && - (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); - - /* free the IrpContext to NonPagedList */ - IrpContext->Identifier.Type = 0; - IrpContext->Identifier.Size = 0; - - DEC_IRP_COUNT(IrpContext); - ExFreeToNPagedLookasideList(&(Ext2Global->Ext2IrpContextLookasideList), IrpContext); -} - - -PEXT2_FCB -Ext2AllocateFcb ( - IN PEXT2_VCB Vcb, - IN PEXT2_MCB Mcb -) -{ - PEXT2_FCB Fcb; - - ASSERT(ExIsResourceAcquiredExclusiveLite(&Vcb->FcbLock)); - - Fcb = (PEXT2_FCB) ExAllocateFromNPagedLookasideList( - &(Ext2Global->Ext2FcbLookasideList)); - - if (!Fcb) { - return NULL; - } - - RtlZeroMemory(Fcb, sizeof(EXT2_FCB)); - Fcb->Identifier.Type = EXT2FCB; - Fcb->Identifier.Size = sizeof(EXT2_FCB); - -#ifndef _WIN2K_TARGET_ - ExInitializeFastMutex(&Fcb->Mutex); - FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->Mutex); -#endif - - FsRtlInitializeOplock(&Fcb->Oplock); - FsRtlInitializeFileLock ( - &Fcb->FileLockAnchor, - NULL, - NULL ); - - Fcb->OpenHandleCount = 0; - Fcb->ReferenceCount = 0; - Fcb->Vcb = Vcb; - Fcb->Inode = &Mcb->Inode; - - ASSERT(Mcb->Fcb == NULL); - Ext2ReferMcb(Mcb); - Fcb->Mcb = Mcb; - Mcb->Fcb = Fcb; - - DEBUG(DL_RES, ("Ext2AllocateFcb: Fcb %p created: %wZ.\n", - Fcb, &Fcb->Mcb->FullName)); - - RtlZeroMemory(&Fcb->Header, sizeof(FSRTL_COMMON_FCB_HEADER)); - Fcb->Header.NodeTypeCode = (USHORT) EXT2FCB; - Fcb->Header.NodeByteSize = sizeof(EXT2_FCB); - Fcb->Header.IsFastIoPossible = FastIoIsNotPossible; - Fcb->Header.Resource = &(Fcb->MainResource); - Fcb->Header.PagingIoResource = &(Fcb->PagingIoResource); - - Fcb->Header.FileSize.QuadPart = Mcb->Inode.i_size; - Fcb->Header.ValidDataLength.QuadPart = Mcb->Inode.i_size; - Fcb->Header.AllocationSize.QuadPart = CEILING_ALIGNED(ULONGLONG, - Fcb->Header.FileSize.QuadPart, (ULONGLONG)Vcb->BlockSize); - - Fcb->SectionObject.DataSectionObject = NULL; - Fcb->SectionObject.SharedCacheMap = NULL; - Fcb->SectionObject.ImageSectionObject = NULL; - - ExInitializeResourceLite(&(Fcb->MainResource)); - ExInitializeResourceLite(&(Fcb->PagingIoResource)); - - Ext2InsertFcb(Vcb, Fcb); - - INC_MEM_COUNT(PS_FCB, Fcb, sizeof(EXT2_FCB)); - - return Fcb; -} - -VOID -Ext2UnlinkFcb(IN PEXT2_FCB Fcb) -{ - PEXT2_VCB Vcb = Fcb->Vcb; - PEXT2_MCB Mcb; - - ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); - Mcb = Fcb->Mcb; - - DEBUG(DL_INF, ("Ext2FreeFcb: Fcb (%p) to be unlinked: %wZ.\n", - Fcb, Mcb ? &Mcb->FullName : NULL)); - - if ((Mcb != NULL) && - (Mcb->Identifier.Type == EXT2MCB) && - (Mcb->Identifier.Size == sizeof(EXT2_MCB))) { - - ASSERT (Mcb->Fcb == Fcb); - if (IsMcbSpecialFile(Mcb) || - IsFileDeleted(Mcb)) { - - ASSERT(!IsRoot(Fcb)); - Ext2RemoveMcb(Vcb, Mcb); - Mcb->Fcb = NULL; - - Ext2UnlinkMcb(Vcb, Mcb); - Ext2DerefMcb(Mcb); - Ext2LinkHeadMcb(Vcb, Mcb); - - } else { - Mcb->Fcb = NULL; - Ext2DerefMcb(Mcb); - } - Fcb->Mcb = NULL; - } - - ExReleaseResourceLite(&Vcb->McbLock); -} - -VOID -Ext2FreeFcb (IN PEXT2_FCB Fcb) -{ - PEXT2_VCB Vcb = Fcb->Vcb; - - __try { - - ASSERT((Fcb != NULL) && (Fcb->Identifier.Type == EXT2FCB) && - (Fcb->Identifier.Size == sizeof(EXT2_FCB))); - ASSERT(0 == Fcb->ReferenceCount); - -#ifndef _WIN2K_TARGET_ - FsRtlTeardownPerStreamContexts(&Fcb->Header); -#endif - - FsRtlUninitializeFileLock(&Fcb->FileLockAnchor); - FsRtlUninitializeOplock(&Fcb->Oplock); - ExDeleteResourceLite(&Fcb->MainResource); - ExDeleteResourceLite(&Fcb->PagingIoResource); - - Fcb->Identifier.Type = 0; - Fcb->Identifier.Size = 0; - - ExFreeToNPagedLookasideList(&(Ext2Global->Ext2FcbLookasideList), Fcb); - DEC_MEM_COUNT(PS_FCB, Fcb, sizeof(EXT2_FCB)); - - if (0 == Ext2DerefXcb(&Vcb->ReferenceCount)) { - if (!IsMounted(Vcb) || IsDispending(Vcb)) { - Ext2CheckDismount(NULL, Vcb, FALSE); - } - } - - } __finally { - } -} - -VOID -Ext2ReleaseFcb (IN PEXT2_FCB Fcb) -{ - PEXT2_VCB Vcb = Fcb->Vcb; - PEXT2_MCB Mcb; - - if (0 != Ext2DerefXcb(&Fcb->ReferenceCount)) - return; - - ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); - ExAcquireResourceExclusiveLite(&Fcb->MainResource, TRUE); - - Mcb = Fcb->Mcb; - RemoveEntryList(&Fcb->Next); - - if (IsFlagOn(Fcb->Flags, FCB_DELETE_PENDING) || - NULL == Mcb || IsFileDeleted(Mcb)) { - InsertHeadList(&Vcb->FcbList, &Fcb->Next); - Fcb->TsDrop.QuadPart = 0; - } else { - InsertTailList(&Vcb->FcbList, &Fcb->Next); - KeQuerySystemTime(&Fcb->TsDrop); - } - ExReleaseResourceLite(&Fcb->MainResource); - ExReleaseResourceLite(&Vcb->FcbLock); - - if ((Vcb->FcbCount >> 6) > (ULONG)(Ext2Global->MaxDepth)) { - KeSetEvent(&Ext2Global->FcbReaper.Wait, 0, FALSE); - } -} - -/* Insert Fcb to Vcb->FcbList queue, with Vcb->FcbLock Acquired. */ - -VOID -Ext2InsertFcb(PEXT2_VCB Vcb, PEXT2_FCB Fcb) -{ - ASSERT(ExIsResourceAcquiredExclusiveLite(&Vcb->FcbLock)); - - KeQuerySystemTime(&Fcb->TsDrop); - Ext2ReferXcb(&Vcb->FcbCount); - Ext2ReferXcb(&Vcb->ReferenceCount); - InsertTailList(&Vcb->FcbList, &Fcb->Next); -} - -PEXT2_CCB -Ext2AllocateCcb (ULONG Flags, PEXT2_MCB SymLink) -{ - PEXT2_CCB Ccb; - - Ccb = (PEXT2_CCB) (ExAllocateFromNPagedLookasideList( - &(Ext2Global->Ext2CcbLookasideList))); - if (!Ccb) { - return NULL; - } - - DEBUG(DL_RES, ( "ExtAllocateCcb: Ccb created: %ph.\n", Ccb)); - - RtlZeroMemory(Ccb, sizeof(EXT2_CCB)); - - Ccb->Identifier.Type = EXT2CCB; - Ccb->Identifier.Size = sizeof(EXT2_CCB); - Ccb->Flags = Flags; - - Ccb->SymLink = SymLink; - if (SymLink) { - ASSERT(SymLink->Refercount > 0); - Ext2ReferMcb(SymLink); - DEBUG(DL_INF, ( "ExtAllocateCcb: Ccb SymLink: %wZ.\n", - &Ccb->SymLink->FullName)); - } - - Ccb->DirectorySearchPattern.Length = 0; - Ccb->DirectorySearchPattern.MaximumLength = 0; - Ccb->DirectorySearchPattern.Buffer = 0; - - INC_MEM_COUNT(PS_CCB, Ccb, sizeof(EXT2_CCB)); - - return Ccb; -} - -VOID -Ext2FreeCcb (IN PEXT2_VCB Vcb, IN PEXT2_CCB Ccb) -{ - ASSERT(Ccb != NULL); - ASSERT((Ccb->Identifier.Type == EXT2CCB) && - (Ccb->Identifier.Size == sizeof(EXT2_CCB))); - - DEBUG(DL_RES, ( "Ext2FreeCcb: Ccb = %ph.\n", Ccb)); - - if (Ccb->SymLink) { - DEBUG(DL_INF, ( "Ext2FreeCcb: Ccb SymLink: %wZ.\n", - &Ccb->SymLink->FullName)); - if (IsFileDeleted(Ccb->SymLink->Target)) { - Ext2UnlinkMcb(Vcb, Ccb->SymLink); - Ext2DerefMcb(Ccb->SymLink); - Ext2LinkHeadMcb(Vcb, Ccb->SymLink); - } else { - Ext2DerefMcb(Ccb->SymLink); - } - } - - if (Ccb->DirectorySearchPattern.Buffer != NULL) { - DEC_MEM_COUNT(PS_DIR_PATTERN, Ccb->DirectorySearchPattern.Buffer, - Ccb->DirectorySearchPattern.MaximumLength ); - Ext2FreePool(Ccb->DirectorySearchPattern.Buffer, EXT2_DIRSP_MAGIC); - } - - ExFreeToNPagedLookasideList(&(Ext2Global->Ext2CcbLookasideList), Ccb); - DEC_MEM_COUNT(PS_CCB, Ccb, sizeof(EXT2_CCB)); -} - -PEXT2_INODE -Ext2AllocateInode (PEXT2_VCB Vcb) -{ - PVOID inode = NULL; - - inode = ExAllocateFromNPagedLookasideList( - &(Vcb->InodeLookasideList)); - if (!inode) { - return NULL; - } - - RtlZeroMemory(inode, INODE_SIZE); - - DEBUG(DL_INF, ("ExtAllocateInode: Inode created: %ph.\n", inode)); - INC_MEM_COUNT(PS_EXT2_INODE, inode, INODE_SIZE); - - return inode; -} - -VOID -Ext2DestroyInode (IN PEXT2_VCB Vcb, IN PEXT2_INODE inode) -{ - ASSERT(inode != NULL); - - DEBUG(DL_INF, ("Ext2FreeInode: Inode = %ph.\n", inode)); - - ExFreeToNPagedLookasideList(&(Vcb->InodeLookasideList), inode); - DEC_MEM_COUNT(PS_EXT2_INODE, inode, INODE_SIZE); -} - -struct dentry * Ext2AllocateEntry() -{ - struct dentry *de; - - de = (struct dentry *)ExAllocateFromNPagedLookasideList( - &(Ext2Global->Ext2DentryLookasideList)); - if (!de) { - return NULL; - } - - RtlZeroMemory(de, sizeof(struct dentry)); - INC_MEM_COUNT(PS_DENTRY, de, sizeof(struct dentry)); - - return de; -} - -VOID Ext2FreeEntry (IN struct dentry *de) -{ - ASSERT(de != NULL); - - if (de->d_name.name) - ExFreePool(de->d_name.name); - - ExFreeToNPagedLookasideList(&(Ext2Global->Ext2DentryLookasideList), de); - DEC_MEM_COUNT(PS_DENTRY, de, sizeof(struct dentry)); -} - - -struct dentry *Ext2BuildEntry(PEXT2_VCB Vcb, PEXT2_MCB Dcb, PUNICODE_STRING FileName) -{ - OEM_STRING Oem = { 0 }; - struct dentry *de = NULL; - NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES; - - __try { - - de = Ext2AllocateEntry(); - if (!de) { - DEBUG(DL_ERR, ("Ext2BuildEntry: failed to allocate dentry.\n")); - __leave; - } - de->d_sb = &Vcb->sb; - if (Dcb) - de->d_parent = Dcb->de; - - Oem.MaximumLength = (USHORT)Ext2UnicodeToOEMSize(Vcb, FileName) + 1; - Oem.Buffer = ExAllocatePool(PagedPool, Oem.MaximumLength); - if (!Oem.Buffer) { - DEBUG(DL_ERR, ( "Ex2BuildEntry: failed to allocate OEM name.\n")); - __leave; - } - de->d_name.name = Oem.Buffer; - RtlZeroMemory(Oem.Buffer, Oem.MaximumLength); - Status = Ext2UnicodeToOEM(Vcb, &Oem, FileName); - if (!NT_SUCCESS(Status)) { - DEBUG(DL_CP, ("Ext2BuildEntry: failed to convert %S to OEM.\n", FileName->Buffer)); - __leave; - } - de->d_name.len = Oem.Length; - - } __finally { - - if (!NT_SUCCESS(Status)) { - if (de) - Ext2FreeEntry(de); - } - } - - return de; -} - -PEXT2_EXTENT -Ext2AllocateExtent () -{ - PEXT2_EXTENT Extent; - - Extent = (PEXT2_EXTENT)ExAllocateFromNPagedLookasideList( - &(Ext2Global->Ext2ExtLookasideList)); - if (!Extent) { - return NULL; - } - - RtlZeroMemory(Extent, sizeof(EXT2_EXTENT)); - INC_MEM_COUNT(PS_EXTENT, Extent, sizeof(EXT2_EXTENT)); - - return Extent; -} - -VOID -Ext2FreeExtent (IN PEXT2_EXTENT Extent) -{ - ASSERT(Extent != NULL); - ExFreeToNPagedLookasideList(&(Ext2Global->Ext2ExtLookasideList), Extent); - DEC_MEM_COUNT(PS_EXTENT, Extent, sizeof(EXT2_EXTENT)); -} - -ULONG -Ext2CountExtents(IN PEXT2_EXTENT Chain) -{ - ULONG count = 0; - PEXT2_EXTENT List = Chain; - - while (List) { - count += 1; - List = List->Next; - } - - return count; -} - -VOID -Ext2JointExtents( - IN PEXT2_EXTENT Chain, - IN PEXT2_EXTENT Extent -) -{ - ULONG count = 0; - PEXT2_EXTENT List = Chain; - - while (List->Next) { - List = List->Next; - } - - List->Next = Extent; -} - - -VOID -Ext2DestroyExtentChain(IN PEXT2_EXTENT Chain) -{ - PEXT2_EXTENT Extent = NULL, List = Chain; - - while (List) { - Extent = List->Next; - Ext2FreeExtent(List); - List = Extent; - } -} - -BOOLEAN -Ext2ListExtents(PLARGE_MCB Extents) -{ - if (FsRtlNumberOfRunsInLargeMcb(Extents) != 0) { - - LONGLONG DirtyVba; - LONGLONG DirtyLba; - LONGLONG DirtyLength; - int i, n = 0; - - for (i = 0; FsRtlGetNextLargeMcbEntry( - Extents, i, &DirtyVba, - &DirtyLba, &DirtyLength); i++) { - if (DirtyVba > 0 && DirtyLba != -1) { - DEBUG(DL_EXT, ("Vba:%I64xh Lba:%I64xh Len:%I64xh.\n", DirtyVba, DirtyLba, DirtyLength)); - n++; - } - } - - return n ? TRUE : FALSE; - } - - return FALSE; -} - -VOID -Ext2CheckExtent( - PLARGE_MCB Zone, - LONGLONG Vbn, - LONGLONG Lbn, - LONGLONG Length, - BOOLEAN bAdded -) -{ -#if EXT2_DEBUG - LONGLONG DirtyLbn; - LONGLONG DirtyLen; - LONGLONG RunStart; - LONGLONG RunLength; - ULONG Index; - BOOLEAN bFound = FALSE; - - bFound = FsRtlLookupLargeMcbEntry( - Zone, - Vbn, - &DirtyLbn, - &DirtyLen, - &RunStart, - &RunLength, - &Index ); - - if (!bAdded && (!bFound || DirtyLbn == -1)) { - return; - } - - if ( !bFound || (DirtyLbn == -1) || - (DirtyLbn != Lbn) || - (DirtyLen < Length)) { - - DbgBreak(); - - for (Index = 0; TRUE; Index++) { - - if (!FsRtlGetNextLargeMcbEntry( - Zone, - Index, - &Vbn, - &Lbn, - &Length)) { - break; - } - - DEBUG(DL_EXT, ("Index = %xh Vbn = %I64xh Lbn = %I64xh Len = %I64xh\n", - Index, Vbn, Lbn, Length )); - } - } -#endif -} - -VOID -Ext2ClearAllExtents(PLARGE_MCB Zone) -{ - __try { - FsRtlTruncateLargeMcb(Zone, (LONGLONG)0); - } __except (EXCEPTION_EXECUTE_HANDLER) { - DbgBreak(); - } -} - - -BOOLEAN -Ext2AddVcbExtent ( - IN PEXT2_VCB Vcb, - IN LONGLONG Vbn, - IN LONGLONG Length -) -{ - ULONG TriedTimes = 0; - - LONGLONG Offset = 0; - BOOLEAN rc = FALSE; - - Offset = Vbn & (~(Vcb->IoUnitSize - 1)); - Length = (Vbn - Offset + Length + Vcb->IoUnitSize - 1) & - ~(Vcb->IoUnitSize - 1); - - ASSERT ((Offset & (Vcb->IoUnitSize - 1)) == 0); - ASSERT ((Length & (Vcb->IoUnitSize - 1)) == 0); - - Offset = (Offset >> Vcb->IoUnitBits) + 1; - Length = (Length >> Vcb->IoUnitBits); - -Again: - - __try { - rc = FsRtlAddLargeMcbEntry( - &Vcb->Extents, - Offset, - Offset, - Length - ); - } __except (EXCEPTION_EXECUTE_HANDLER) { - DbgBreak(); - rc = FALSE; - } - - if (!rc && ++TriedTimes < 10) { - Ext2Sleep(TriedTimes * 100); - goto Again; - } - - DEBUG(DL_EXT, ("Ext2AddVcbExtent: Vbn=%I64xh Length=%I64xh," - " rc=%d Runs=%u\n", Offset, Length, rc, - FsRtlNumberOfRunsInLargeMcb(&Vcb->Extents))); - - if (rc) { - Ext2CheckExtent(&Vcb->Extents, Offset, Offset, Length, TRUE); - } - - return rc; -} - -BOOLEAN -Ext2RemoveVcbExtent ( - IN PEXT2_VCB Vcb, - IN LONGLONG Vbn, - IN LONGLONG Length -) -{ - ULONG TriedTimes = 0; - LONGLONG Offset = 0; - BOOLEAN rc = TRUE; - - Offset = Vbn & (~(Vcb->IoUnitSize - 1)); - Length = (Length + Vbn - Offset + Vcb->IoUnitSize - 1) & (~(Vcb->IoUnitSize - 1)); - - ASSERT ((Offset & (Vcb->IoUnitSize - 1)) == 0); - ASSERT ((Length & (Vcb->IoUnitSize - 1)) == 0); - - Offset = (Offset >> Vcb->IoUnitBits) + 1; - Length = (Length >> Vcb->IoUnitBits); - -Again: - - __try { - FsRtlRemoveLargeMcbEntry( - &Vcb->Extents, - Offset, - Length - ); - } __except (EXCEPTION_EXECUTE_HANDLER) { - DbgBreak(); - rc = FALSE; - } - - if (!rc && ++TriedTimes < 10) { - Ext2Sleep(TriedTimes * 100); - goto Again; - } - - DEBUG(DL_EXT, ("Ext2RemoveVcbExtent: Vbn=%I64xh Length=%I64xh Runs=%u\n", - Offset, Length, FsRtlNumberOfRunsInLargeMcb(&Vcb->Extents))); - if (rc) { - Ext2CheckExtent(&Vcb->Extents, Offset, 0, Length, FALSE); - } - - return rc; -} - -BOOLEAN -Ext2LookupVcbExtent ( - IN PEXT2_VCB Vcb, - IN LONGLONG Vbn, - OUT PLONGLONG Lbn, - OUT PLONGLONG Length -) -{ - LONGLONG offset; - BOOLEAN rc; - - offset = Vbn & (~(Vcb->IoUnitSize - 1)); - ASSERT ((offset & (Vcb->IoUnitSize - 1)) == 0); - offset = (offset >> Vcb->IoUnitBits) + 1; - - rc = FsRtlLookupLargeMcbEntry( - &(Vcb->Extents), - offset, - Lbn, - Length, - NULL, - NULL, - NULL - ); - - if (rc) { - - if (Lbn && ((*Lbn) != -1)) { - ASSERT((*Lbn) > 0); - (*Lbn) = (((*Lbn) - 1) << Vcb->IoUnitBits); - (*Lbn) += ((Vbn) & (Vcb->IoUnitSize - 1)); - } - - if (Length && *Length) { - (*Length) <<= Vcb->IoUnitBits; - (*Length) -= ((Vbn) & (Vcb->IoUnitSize - 1)); - } - } - - return rc; -} - - -BOOLEAN -Ext2AddMcbExtent ( - IN PEXT2_VCB Vcb, - IN PEXT2_MCB Mcb, - IN LONGLONG Vbn, - IN LONGLONG Lbn, - IN LONGLONG Length -) -{ - ULONG TriedTimes = 0; - LONGLONG Base = 0; - UCHAR Bits = 0; - BOOLEAN rc = FALSE; - - Base = (LONGLONG)BLOCK_SIZE; - Bits = (UCHAR)BLOCK_BITS; - - ASSERT ((Vbn & (Base - 1)) == 0); - ASSERT ((Lbn & (Base - 1)) == 0); - ASSERT ((Length & (Base - 1)) == 0); - - Vbn = (Vbn >> Bits) + 1; - Lbn = (Lbn >> Bits) + 1; - Length = (Length >> Bits); - -Again: - - __try { - - rc = FsRtlAddLargeMcbEntry( - &Mcb->Extents, - Vbn, - Lbn, - Length - ); - - } __except (EXCEPTION_EXECUTE_HANDLER) { - - DbgBreak(); - rc = FALSE; - } - - if (!rc && ++TriedTimes < 10) { - Ext2Sleep(TriedTimes * 100); - goto Again; - } - - DEBUG(DL_EXT, ("Ext2AddMcbExtent: Vbn=%I64xh Lbn=%I64xh Length=%I64xh," - " rc=%d Runs=%u\n", Vbn, Lbn, Length, rc, - FsRtlNumberOfRunsInLargeMcb(&Mcb->Extents))); - - if (rc) { - Ext2CheckExtent(&Mcb->Extents, Vbn, Lbn, Length, TRUE); - } - - return rc; -} - -BOOLEAN -Ext2RemoveMcbExtent ( - IN PEXT2_VCB Vcb, - IN PEXT2_MCB Mcb, - IN LONGLONG Vbn, - IN LONGLONG Length -) -{ - ULONG TriedTimes = 0; - LONGLONG Base = 0; - UCHAR Bits = 0; - BOOLEAN rc = TRUE; - - Base = (LONGLONG)BLOCK_SIZE; - Bits = (UCHAR)BLOCK_BITS; - - ASSERT ((Vbn & (Base - 1)) == 0); - ASSERT ((Length & (Base - 1)) == 0); - - Vbn = (Vbn >> Bits) + 1; - Length = (Length >> Bits); - -Again: - - __try { - FsRtlRemoveLargeMcbEntry( - &Mcb->Extents, - Vbn, - Length - ); - } __except (EXCEPTION_EXECUTE_HANDLER) { - DbgBreak(); - rc = FALSE; - } - - if (!rc && ++TriedTimes < 10) { - Ext2Sleep(TriedTimes * 100); - goto Again; - } - - DEBUG(DL_EXT, ("Ext2RemoveMcbExtent: Vbn=%I64xh Length=%I64xh Runs=%u\n", - Vbn, Length, FsRtlNumberOfRunsInLargeMcb(&Mcb->Extents))); - if (rc) { - Ext2CheckExtent(&Mcb->Extents, Vbn, 0, Length, FALSE); - } - - return rc; -} - -BOOLEAN -Ext2LookupMcbExtent ( - IN PEXT2_VCB Vcb, - IN PEXT2_MCB Mcb, - IN LONGLONG Vbn, - OUT PLONGLONG Lbn, - OUT PLONGLONG Length -) -{ - LONGLONG offset; - BOOLEAN rc; - - offset = Vbn & (~((LONGLONG)BLOCK_SIZE - 1)); - ASSERT ((offset & (BLOCK_SIZE - 1)) == 0); - offset = (offset >> BLOCK_BITS) + 1; - - rc = FsRtlLookupLargeMcbEntry( - &(Mcb->Extents), - offset, - Lbn, - Length, - NULL, - NULL, - NULL - ); - - if (rc) { - - if (Lbn && ((*Lbn) != -1)) { - ASSERT((*Lbn) > 0); - (*Lbn) = (((*Lbn) - 1) << BLOCK_BITS); - (*Lbn) += ((Vbn) & ((LONGLONG)BLOCK_SIZE - 1)); - } - - if (Length && *Length) { - (*Length) <<= BLOCK_BITS; - (*Length) -= ((Vbn) & ((LONGLONG)BLOCK_SIZE - 1)); - } - } - - return rc; -} - - -BOOLEAN -Ext2AddMcbMetaExts ( - IN PEXT2_VCB Vcb, - IN PEXT2_MCB Mcb, - IN ULONG Block, - IN ULONG Length -) -{ - ULONG TriedTimes = 0; - LONGLONG Lbn = Block + 1; - BOOLEAN rc = TRUE; - -Again: - - __try { - - rc = FsRtlAddLargeMcbEntry( - &Mcb->MetaExts, - Lbn, - Lbn, - Length - ); - - } __except (EXCEPTION_EXECUTE_HANDLER) { - - DbgBreak(); - rc = FALSE; - } - - if (!rc && ++TriedTimes < 10) { - Ext2Sleep(TriedTimes * 100); - goto Again; - } - - DEBUG(DL_EXT, ("Ext2AddMcbMetaExts: Block: %xh-%xh rc=%d Runs=%u\n", Block, - Length, rc, FsRtlNumberOfRunsInLargeMcb(&Mcb->MetaExts))); - - if (rc) { - Ext2CheckExtent(&Mcb->MetaExts, Lbn, Lbn, Length, TRUE); - } - - return rc; -} - -BOOLEAN -Ext2RemoveMcbMetaExts ( - IN PEXT2_VCB Vcb, - IN PEXT2_MCB Mcb, - IN ULONG Block, - IN ULONG Length -) -{ - ULONG TriedTimes = 0; - LONGLONG Lbn = Block + 1; - BOOLEAN rc = TRUE; - -Again: - - __try { - - FsRtlRemoveLargeMcbEntry( - &Mcb->MetaExts, - Lbn, - Length - ); - - } __except (EXCEPTION_EXECUTE_HANDLER) { - DbgBreak(); - rc = FALSE; - } - - if (!rc && ++TriedTimes < 10) { - Ext2Sleep(TriedTimes * 100); - goto Again; - } - - DEBUG(DL_EXT, ("Ext2RemoveMcbMetaExts: Block: %xh-%xhh Runs=%u\n", Block, - Length, FsRtlNumberOfRunsInLargeMcb(&Mcb->MetaExts))); - if (rc) { - Ext2CheckExtent(&Mcb->MetaExts, Lbn, 0, Length, FALSE); - } - - return rc; -} - - -BOOLEAN -Ext2AddBlockExtent( - IN PEXT2_VCB Vcb, - IN PEXT2_MCB Mcb, - IN ULONG Start, - IN ULONG Block, - IN ULONG Number -) -{ - LONGLONG Vbn = 0; - LONGLONG Lbn = 0; - LONGLONG Length = 0; - - Vbn = ((LONGLONG) Start) << BLOCK_BITS; - Lbn = ((LONGLONG) Block) << BLOCK_BITS; - Length = ((LONGLONG)Number << BLOCK_BITS); - - if (Mcb) { -#if EXT2_DEBUG - ULONG _block = 0, _mapped = 0; - BOOLEAN _rc = Ext2LookupBlockExtent(Vcb, Mcb, Start, &_block, &_mapped); - if (_rc && _block != 0 && (_block != Block)) { - DbgBreak(); - } -#endif - return Ext2AddMcbExtent(Vcb, Mcb, Vbn, Lbn, Length); - - } - - ASSERT(Start == Block); - return Ext2AddVcbExtent(Vcb, Vbn, Length); -} - - -BOOLEAN -Ext2LookupBlockExtent( - IN PEXT2_VCB Vcb, - IN PEXT2_MCB Mcb, - IN ULONG Start, - IN PULONG Block, - IN PULONG Mapped -) -{ - LONGLONG Vbn = 0; - LONGLONG Lbn = 0; - LONGLONG Length = 0; - - BOOLEAN rc = FALSE; - - Vbn = ((LONGLONG) Start) << BLOCK_BITS; - - if (Mcb) { - rc = Ext2LookupMcbExtent(Vcb, Mcb, Vbn, &Lbn, &Length); - } else { - rc = Ext2LookupVcbExtent(Vcb, Vbn, &Lbn, &Length); - } - - if (rc) { - *Mapped = (ULONG)(Length >> BLOCK_BITS); - if (Lbn != -1 && Length > 0) { - *Block = (ULONG)(Lbn >> BLOCK_BITS); - } else { - *Block = 0; - } - } - - return rc; -} - - -BOOLEAN -Ext2RemoveBlockExtent( - IN PEXT2_VCB Vcb, - IN PEXT2_MCB Mcb, - IN ULONG Start, - IN ULONG Number -) -{ - LONGLONG Vbn = 0; - LONGLONG Length = 0; - BOOLEAN rc; - - Vbn = ((LONGLONG) Start) << BLOCK_BITS; - Length = ((LONGLONG)Number << BLOCK_BITS); - - if (Mcb) { - rc = Ext2RemoveMcbExtent(Vcb, Mcb, Vbn, Length); - } else { - rc = Ext2RemoveVcbExtent(Vcb, Vbn, Length); - } - - return rc; -} - -NTSTATUS -Ext2InitializeZone( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN PEXT2_MCB Mcb -) -{ - NTSTATUS Status = STATUS_SUCCESS; - - ULONG Start = 0; - ULONG End; - ULONG Block; - ULONG Mapped; - - Ext2ClearAllExtents(&Mcb->Extents); - Ext2ClearAllExtents(&Mcb->MetaExts); - - ASSERT(Mcb != NULL); - End = (ULONG)((Mcb->Inode.i_size + BLOCK_SIZE - 1) >> BLOCK_BITS); - - while (Start < End) { - - Block = Mapped = 0; - - /* mapping file offset to ext2 block */ - if (INODE_HAS_EXTENT(&Mcb->Inode)) { - Status = Ext2MapExtent( - IrpContext, - Vcb, - Mcb, - Start, - FALSE, - &Block, - &Mapped - ); - } else { - Status = Ext2MapIndirect( - IrpContext, - Vcb, - Mcb, - Start, - FALSE, - &Block, - &Mapped - ); - } - - if (!NT_SUCCESS(Status)) { - goto errorout; - } - - /* skip wrong blocks, in case wrongly treating symlink - target names as blocks, silly */ - if (Block >= TOTAL_BLOCKS) { - Block = 0; - } - - if (Block) { - if (!Ext2AddBlockExtent(Vcb, Mcb, Start, Block, Mapped)) { - DbgBreak(); - ClearFlag(Mcb->Flags, MCB_ZONE_INITED); - Ext2ClearAllExtents(&Mcb->Extents); - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - DEBUG(DL_MAP, ("Ext2InitializeZone %wZ: Block = %xh Mapped = %xh\n", - &Mcb->FullName, Block, Mapped)); - } - - /* Mapped is total number of continous blocks or NULL blocks */ - Start += Mapped; - } - - /* set mcb zone as initialized */ - SetLongFlag(Mcb->Flags, MCB_ZONE_INITED); - -errorout: - - if (!IsZoneInited(Mcb)) { - Ext2ClearAllExtents(&Mcb->Extents); - Ext2ClearAllExtents(&Mcb->MetaExts); - } - - return Status; -} - -NTSTATUS -Ext2BuildExtents( - IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN PEXT2_MCB Mcb, - IN ULONGLONG Offset, - IN ULONG Size, - IN BOOLEAN bAlloc, - OUT PEXT2_EXTENT * Chain -) -{ - ULONG Start, End; - ULONG Total = 0; - - LONGLONG Lba = 0; - NTSTATUS Status = STATUS_SUCCESS; - - PEXT2_EXTENT Extent = NULL; - PEXT2_EXTENT List = *Chain = NULL; - - if (!IsZoneInited(Mcb)) { - Status = Ext2InitializeZone(IrpContext, Vcb, Mcb); - if (!NT_SUCCESS(Status)) { - DbgBreak(); - } - } - - if ((IrpContext && IrpContext->Irp) && - ((IrpContext->Irp->Flags & IRP_NOCACHE) || - (IrpContext->Irp->Flags & IRP_PAGING_IO))) { - Size = (Size + SECTOR_SIZE - 1) & (~(SECTOR_SIZE - 1)); - } - - Start = (ULONG)(Offset >> BLOCK_BITS); - End = (ULONG)((Size + Offset + BLOCK_SIZE - 1) >> BLOCK_BITS); - - if (End > (ULONG)((Mcb->Inode.i_size + BLOCK_SIZE - 1) >> BLOCK_BITS) ) { - End = (ULONG)((Mcb->Inode.i_size + BLOCK_SIZE - 1) >> BLOCK_BITS); - } - - while (Size > 0 && Start < End) { - - ULONG Mapped = 0; - ULONG Length = 0; - ULONG Block = 0; - - BOOLEAN rc = FALSE; - - /* try to map file offset to ext2 block upon Extents cache */ - if (IsZoneInited(Mcb)) { - rc = Ext2LookupBlockExtent( - Vcb, - Mcb, - Start, - &Block, - &Mapped); - - if (!rc) { - /* we likely get a sparse file here */ - Mapped = 1; - Block = 0; - } - } - - /* try to BlockMap in case failed to access Extents cache */ - if (!IsZoneInited(Mcb) || (bAlloc && Block == 0)) { - - Status = Ext2BlockMap( - IrpContext, - Vcb, - Mcb, - Start, - bAlloc, - &Block, - &Mapped - ); - if (!NT_SUCCESS(Status)) { - break; - } - - /* skip wrong blocks, in case wrongly treating symlink - target names as blocks, silly */ - if (Block >= TOTAL_BLOCKS) { - Block = 0; - } - - /* add new allocated blocks to Mcb zone */ - if (IsZoneInited(Mcb) && Block) { - if (!Ext2AddBlockExtent(Vcb, Mcb, Start, Block, Mapped)) { - DbgBreak(); - ClearFlag(Mcb->Flags, MCB_ZONE_INITED); - Ext2ClearAllExtents(&Mcb->Extents); - } - } - } - - /* calculate i/o extent */ - Lba = ((LONGLONG)Block << BLOCK_BITS) + Offset - ((LONGLONG)Start << BLOCK_BITS); - Length = (ULONG)(((LONGLONG)(Start + Mapped) << BLOCK_BITS) - Offset); - if (Length > Size) { - Length = Size; - } - - if (0 == Length) { - DbgBreak(); - break; - } - - Start += Mapped; - Offset = (ULONGLONG)Start << BLOCK_BITS; - - if (Block != 0) { - - if (List && List->Lba + List->Length == Lba) { - - /* it's continuous upon previous Extent */ - List->Length += Length; - - } else { - - /* have to allocate a new Extent */ - Extent = Ext2AllocateExtent(); - if (!Extent) { - Status = STATUS_INSUFFICIENT_RESOURCES; - DbgBreak(); - break; - } - - Extent->Lba = Lba; - Extent->Length = Length; - Extent->Offset = Total; - - /* insert new Extent to chain */ - if (List) { - List->Next = Extent; - List = Extent; - } else { - *Chain = List = Extent; - } - } - } else { - if (bAlloc) { - DbgBreak(); - } - } - - Total += Length; - Size -= Length; - } - - return Status; -} - - -BOOLEAN -Ext2BuildName( - IN OUT PUNICODE_STRING Target, - IN PUNICODE_STRING File, - IN PUNICODE_STRING Parent -) -{ - USHORT Length = 0; - USHORT ParentLen = 0; - BOOLEAN bBackslash = TRUE; - - /* free the original buffer */ - if (Target->Buffer) { - DEC_MEM_COUNT(PS_MCB_NAME, Target->Buffer, Target->MaximumLength); - Ext2FreePool(Target->Buffer, EXT2_FNAME_MAGIC); - Target->Length = Target->MaximumLength = 0; - } - - /* check the parent directory's name and backslash */ - if (Parent && Parent->Buffer && Parent->Length > 0) { - ParentLen = Parent->Length / sizeof(WCHAR); - if (Parent->Buffer[ParentLen - 1] == L'\\') { - bBackslash = FALSE; - } - } - - if (Parent == NULL || File->Buffer[0] == L'\\') { - /* must be root inode */ - ASSERT(ParentLen == 0); - bBackslash = FALSE; - } - - /* allocate and initialize new name buffer */ - Length = File->Length; - Length += (ParentLen + (bBackslash ? 1 : 0)) * sizeof(WCHAR); - - Target->Buffer = Ext2AllocatePool( - PagedPool, - Length + 2, - EXT2_FNAME_MAGIC - ); - - if (!Target->Buffer) { - DEBUG(DL_ERR, ( "Ex2BuildName: failed to allocate name bufer.\n")); - return FALSE; - } - RtlZeroMemory(Target->Buffer, Length + 2); - - if (ParentLen) { - RtlCopyMemory(&Target->Buffer[0], - Parent->Buffer, - ParentLen * sizeof(WCHAR)); - } - - if (bBackslash) { - Target->Buffer[ParentLen++] = L'\\'; - } - - RtlCopyMemory( &Target->Buffer[ParentLen], - File->Buffer, - File->Length); - - INC_MEM_COUNT(PS_MCB_NAME, Target->Buffer, Length + 2); - Target->Length = Length; - Target->MaximumLength = Length + 2; - - return TRUE; -} - -PEXT2_MCB -Ext2AllocateMcb ( - IN PEXT2_VCB Vcb, - IN PUNICODE_STRING FileName, - IN PUNICODE_STRING Parent, - IN ULONG FileAttr -) -{ - PEXT2_MCB Mcb = NULL; - NTSTATUS Status = STATUS_SUCCESS; - - /* need wake the reaper thread if there are many Mcb allocated */ - if (Ext2Global->PerfStat.Current.Mcb > (((ULONG)Ext2Global->MaxDepth) * 4)) { - KeSetEvent(&Ext2Global->McbReaper.Wait, 0, FALSE); - } - - /* allocate Mcb from LookasideList */ - Mcb = (PEXT2_MCB) (ExAllocateFromNPagedLookasideList( - &(Ext2Global->Ext2McbLookasideList))); - - if (Mcb == NULL) { - return NULL; - } - - /* initialize Mcb header */ - RtlZeroMemory(Mcb, sizeof(EXT2_MCB)); - Mcb->Identifier.Type = EXT2MCB; - Mcb->Identifier.Size = sizeof(EXT2_MCB); - Mcb->FileAttr = FileAttr; - - Mcb->Inode.i_priv = (PVOID)Mcb; - Mcb->Inode.i_sb = &Vcb->sb; - - /* initialize Mcb names */ - if (FileName) { - -#if EXT2_DEBUG - if ( FileName->Length == 2 && - FileName->Buffer[0] == L'\\') { - DEBUG(DL_RES, ( "Ext2AllocateMcb: Root Mcb is to be created !\n")); - } - - if ( FileName->Length == 2 && - FileName->Buffer[0] == L'.') { - DbgBreak(); - } - - if ( FileName->Length == 4 && - FileName->Buffer[0] == L'.' && - FileName->Buffer[1] == L'.' ) { - DbgBreak(); - } -#endif - - if (( FileName->Length >= 4 && FileName->Buffer[0] == L'.') && - ((FileName->Length == 4 && FileName->Buffer[1] != L'.') || - FileName->Length >= 6 )) { - SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_HIDDEN); - } - - if (!Ext2BuildName(&Mcb->ShortName, FileName, NULL)) { - goto errorout; - } - if (!Ext2BuildName(&Mcb->FullName, FileName, Parent)) { - goto errorout; - } - } - - /* initialize Mcb Extents, it will raise an expcetion if failed */ - __try { - FsRtlInitializeLargeMcb(&(Mcb->Extents), NonPagedPool); - FsRtlInitializeLargeMcb(&(Mcb->MetaExts), NonPagedPool); - } __except (EXCEPTION_EXECUTE_HANDLER) { - Status = STATUS_INSUFFICIENT_RESOURCES; - DbgBreak(); - } - - if (!NT_SUCCESS(Status)) { - goto errorout; - } - - INC_MEM_COUNT(PS_MCB, Mcb, sizeof(EXT2_MCB)); - DEBUG(DL_INF, ( "Ext2AllocateMcb: Mcb %wZ created.\n", &Mcb->FullName)); - - return Mcb; - -errorout: - - if (Mcb) { - - if (Mcb->ShortName.Buffer) { - DEC_MEM_COUNT(PS_MCB_NAME, Mcb->ShortName.Buffer, - Mcb->ShortName.MaximumLength); - Ext2FreePool(Mcb->ShortName.Buffer, EXT2_FNAME_MAGIC); - } - - if (Mcb->FullName.Buffer) { - DEC_MEM_COUNT(PS_MCB_NAME, Mcb->FullName.Buffer, - Mcb->FullName.MaximumLength); - Ext2FreePool(Mcb->FullName.Buffer, EXT2_FNAME_MAGIC); - } - - ExFreeToNPagedLookasideList(&(Ext2Global->Ext2McbLookasideList), Mcb); - } - - return NULL; -} - -VOID -Ext2FreeMcb (IN PEXT2_VCB Vcb, IN PEXT2_MCB Mcb) -{ - PEXT2_MCB Parent = Mcb->Parent; - - ASSERT(Mcb != NULL); - - ASSERT((Mcb->Identifier.Type == EXT2MCB) && - (Mcb->Identifier.Size == sizeof(EXT2_MCB))); - - if ((Mcb->Identifier.Type != EXT2MCB) || - (Mcb->Identifier.Size != sizeof(EXT2_MCB))) { - return; - } - - DEBUG(DL_INF, ( "Ext2FreeMcb: Mcb %wZ will be freed.\n", &Mcb->FullName)); - - if (IsMcbSymLink(Mcb) && Mcb->Target) { - Ext2DerefMcb(Mcb->Target); - } - - if (FsRtlNumberOfRunsInLargeMcb(&Mcb->Extents)) { - DEBUG(DL_EXT, ("List data extents for: %wZ\n", &Mcb->FullName)); - Ext2ListExtents(&Mcb->Extents); - } - FsRtlUninitializeLargeMcb(&(Mcb->Extents)); - if (FsRtlNumberOfRunsInLargeMcb(&Mcb->MetaExts)) { - DEBUG(DL_EXT, ("List meta extents for: %wZ\n", &Mcb->FullName)); - Ext2ListExtents(&Mcb->MetaExts); - } - FsRtlUninitializeLargeMcb(&(Mcb->MetaExts)); - ClearLongFlag(Mcb->Flags, MCB_ZONE_INITED); - - if (Mcb->ShortName.Buffer) { - DEC_MEM_COUNT(PS_MCB_NAME, Mcb->ShortName.Buffer, - Mcb->ShortName.MaximumLength); - Ext2FreePool(Mcb->ShortName.Buffer, EXT2_FNAME_MAGIC); - } - - if (Mcb->FullName.Buffer) { - DEC_MEM_COUNT(PS_MCB_NAME, Mcb->FullName.Buffer, - Mcb->FullName.MaximumLength); - Ext2FreePool(Mcb->FullName.Buffer, EXT2_FNAME_MAGIC); - } - - /* free dentry */ - if (Mcb->de) { - Ext2FreeEntry(Mcb->de); - } - - Mcb->Identifier.Type = 0; - Mcb->Identifier.Size = 0; - - ExFreeToNPagedLookasideList(&(Ext2Global->Ext2McbLookasideList), Mcb); - DEC_MEM_COUNT(PS_MCB, Mcb, sizeof(EXT2_MCB)); -} - - -PEXT2_MCB -Ext2SearchMcb( - PEXT2_VCB Vcb, - PEXT2_MCB Parent, - PUNICODE_STRING FileName -) -{ - BOOLEAN LockAcquired = FALSE; - PEXT2_MCB Mcb = NULL; - - __try { - ExAcquireResourceSharedLite(&Vcb->McbLock, TRUE); - LockAcquired = TRUE; - Mcb = Ext2SearchMcbWithoutLock(Parent, FileName); - } __finally { - if (LockAcquired) { - ExReleaseResourceLite(&Vcb->McbLock); - } - } - - return Mcb; -} - - -PEXT2_MCB -Ext2SearchMcbWithoutLock( - PEXT2_MCB Parent, - PUNICODE_STRING FileName -) -{ - PEXT2_MCB TmpMcb = NULL; - - DEBUG(DL_RES, ("Ext2SearchMcb: %wZ\n", FileName)); - - __try { - - Ext2ReferMcb(Parent); - - if (Ext2IsDot(FileName)) { - TmpMcb = Parent; - Ext2ReferMcb(Parent); - __leave; - } - - if (Ext2IsDotDot(FileName)) { - if (IsMcbRoot(Parent)) { - TmpMcb = Parent; - } else { - TmpMcb = Parent->Parent; - } - if (TmpMcb) { - Ext2ReferMcb(TmpMcb); - } - __leave; - } - - if (IsMcbSymLink(Parent)) { - if (Parent->Target) { - TmpMcb = Parent->Target->Child; - ASSERT(!IsMcbSymLink(Parent->Target)); - } else { - TmpMcb = NULL; - __leave; - } - } else { - TmpMcb = Parent->Child; - } - - while (TmpMcb) { - - if (!RtlCompareUnicodeString( - &(TmpMcb->ShortName), - FileName, TRUE )) { - Ext2ReferMcb(TmpMcb); - break; - } - - TmpMcb = TmpMcb->Next; - } - - } __finally { - - Ext2DerefMcb(Parent); - } - - return TmpMcb; -} - -VOID -Ext2InsertMcb ( - PEXT2_VCB Vcb, - PEXT2_MCB Parent, - PEXT2_MCB Child -) -{ - BOOLEAN LockAcquired = FALSE; - PEXT2_MCB Mcb = NULL; - - __try { - - ExAcquireResourceExclusiveLite( - &Vcb->McbLock, - TRUE ); - LockAcquired = TRUE; - - /* use it's target if it's a symlink */ - if (IsMcbSymLink(Parent)) { - Parent = Parent->Target; - ASSERT(!IsMcbSymLink(Parent)); - } - - Mcb = Parent->Child; - while (Mcb) { - if (Mcb == Child) { - break; - } - Mcb = Mcb->Next; - } - - if (Mcb) { - /* already attached in the list */ - DEBUG(DL_ERR, ( "Ext2InsertMcb: Child Mcb is alreay attached.\n")); - if (!IsFlagOn(Mcb->Flags, MCB_ENTRY_TREE)) { - SetLongFlag(Child->Flags, MCB_ENTRY_TREE); - DEBUG(DL_ERR, ( "Ext2InsertMcb: Child Mcb's flag isn't set.\n")); - } - - DbgBreak(); - - } else { - - /* insert this Mcb into the head */ - Child->Next = Parent->Child; - Parent->Child = Child; - Child->Parent = Parent; - Child->de->d_parent = Parent->de; - Ext2ReferMcb(Parent); - SetLongFlag(Child->Flags, MCB_ENTRY_TREE); - } - - } __finally { - - if (LockAcquired) { - ExReleaseResourceLite(&Vcb->McbLock); - } - } -} - -BOOLEAN -Ext2RemoveMcb ( - PEXT2_VCB Vcb, - PEXT2_MCB Mcb -) -{ - PEXT2_MCB TmpMcb = NULL; - BOOLEAN LockAcquired = FALSE; - BOOLEAN bLinked = FALSE; - - __try { - - ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); - LockAcquired = TRUE; - - if (Mcb->Parent) { - - if (Mcb->Parent->Child == Mcb) { - Mcb->Parent->Child = Mcb->Next; - bLinked = TRUE; - } else { - TmpMcb = Mcb->Parent->Child; - - while (TmpMcb && TmpMcb->Next != Mcb) { - TmpMcb = TmpMcb->Next; - } - - if (TmpMcb) { - TmpMcb->Next = Mcb->Next; - bLinked = TRUE; - } else { - /* we got errors: link broken */ - } - } - - if (bLinked) { - if (IsFlagOn(Mcb->Flags, MCB_ENTRY_TREE)) { - DEBUG(DL_RES, ("Mcb %p %wZ removed from Mcb %p %wZ\n", Mcb, - &Mcb->FullName, Mcb->Parent, &Mcb->Parent->FullName)); - Ext2DerefMcb(Mcb->Parent); - ClearLongFlag(Mcb->Flags, MCB_ENTRY_TREE); - } else { - DbgBreak(); - } - } else { - if (IsFlagOn(Mcb->Flags, MCB_ENTRY_TREE)) { - ClearLongFlag(Mcb->Flags, MCB_ENTRY_TREE); - } - DbgBreak(); - } - Mcb->Parent = NULL; - Mcb->de->d_parent = NULL; - } - - } __finally { - - if (LockAcquired) { - ExReleaseResourceLite(&Vcb->McbLock); - } - } - - return TRUE; -} - -VOID -Ext2CleanupAllMcbs(PEXT2_VCB Vcb) -{ - BOOLEAN LockAcquired = FALSE; - PEXT2_MCB Mcb = NULL; - - __try { - - ExAcquireResourceExclusiveLite( - &Vcb->McbLock, - TRUE ); - LockAcquired = TRUE; - - while (Mcb = Ext2FirstUnusedMcb(Vcb, TRUE, Vcb->NumOfMcb)) { - while (Mcb) { - PEXT2_MCB Next = Mcb->Next; - if (IsMcbSymLink(Mcb)) { - Mcb->Target = NULL; - } - Ext2FreeMcb(Vcb, Mcb); - Mcb = Next; - } - } - Ext2FreeMcb(Vcb, Vcb->McbTree); - Vcb->McbTree = NULL; - - } __finally { - - if (LockAcquired) { - ExReleaseResourceLite(&Vcb->McbLock); - } - } -} - -BOOLEAN -Ext2CheckSetBlock(PEXT2_IRP_CONTEXT IrpContext, PEXT2_VCB Vcb, LONGLONG Block) -{ - PEXT2_GROUP_DESC gd; - struct buffer_head *gb = NULL; - struct buffer_head *bh = NULL; - ULONG group, dwBlk, Length; - RTL_BITMAP bitmap; - BOOLEAN bModified = FALSE; - - group = (ULONG)(Block - EXT2_FIRST_DATA_BLOCK) / BLOCKS_PER_GROUP; - dwBlk = (ULONG)(Block - EXT2_FIRST_DATA_BLOCK) % BLOCKS_PER_GROUP; - - gd = ext4_get_group_desc(&Vcb->sb, group, &gb); - if (!gd) { - return FALSE; - } - bh = sb_getblk(&Vcb->sb, ext4_block_bitmap(&Vcb->sb, gd)); - - if (group == Vcb->sbi.s_groups_count - 1) { - Length = (ULONG)(TOTAL_BLOCKS % BLOCKS_PER_GROUP); - - /* s_blocks_count is integer multiple of s_blocks_per_group */ - if (Length == 0) - Length = BLOCKS_PER_GROUP; - } else { - Length = BLOCKS_PER_GROUP; - } - - if (dwBlk >= Length) { - fini_bh(&gb); - fini_bh(&bh); - return FALSE; - } - - - RtlInitializeBitMap(&bitmap, (PULONG)bh->b_data, Length); - - if (RtlCheckBit(&bitmap, dwBlk) == 0) { - DbgBreak(); - RtlSetBits(&bitmap, dwBlk, 1); - bModified = TRUE; - mark_buffer_dirty(bh); - } - - fini_bh(&gb); - fini_bh(&bh); - - return (!bModified); -} - -BOOLEAN -Ext2CheckBitmapConsistency(PEXT2_IRP_CONTEXT IrpContext, PEXT2_VCB Vcb) -{ - ULONG i, j, InodeBlocks; - - for (i = 0; i < Vcb->sbi.s_groups_count; i++) { - - PEXT2_GROUP_DESC gd; - struct buffer_head *bh = NULL; - - gd = ext4_get_group_desc(&Vcb->sb, i, &bh); - if (!gd) - continue; - Ext2CheckSetBlock(IrpContext, Vcb, ext4_block_bitmap(&Vcb->sb, gd)); - Ext2CheckSetBlock(IrpContext, Vcb, ext4_inode_bitmap(&Vcb->sb, gd)); - - - if (i == Vcb->sbi.s_groups_count - 1) { - InodeBlocks = ((INODES_COUNT % INODES_PER_GROUP) * - Vcb->InodeSize + Vcb->BlockSize - 1) / - (Vcb->BlockSize); - } else { - InodeBlocks = (INODES_PER_GROUP * Vcb->InodeSize + - Vcb->BlockSize - 1) / (Vcb->BlockSize); - } - - for (j = 0; j < InodeBlocks; j++ ) - Ext2CheckSetBlock(IrpContext, Vcb, ext4_inode_table(&Vcb->sb, gd) + j); - - fini_bh(&bh); - } - - return TRUE; -} - -/* Ext2Global->Resource should be already acquired */ -VOID -Ext2InsertVcb(PEXT2_VCB Vcb) -{ - InsertTailList(&(Ext2Global->VcbList), &Vcb->Next); -} - - -/* Ext2Global->Resource should be already acquired */ -VOID -Ext2RemoveVcb(PEXT2_VCB Vcb) -{ - RemoveEntryList(&Vcb->Next); - InitializeListHead(&Vcb->Next); -} - -NTSTATUS -Ext2QueryVolumeParams(IN PEXT2_VCB Vcb, IN PUNICODE_STRING Params) -{ - NTSTATUS Status; - RTL_QUERY_REGISTRY_TABLE QueryTable[2]; - - UNICODE_STRING UniName; - PUSHORT UniBuffer = NULL; - - USHORT UUID[50]; - - int i; - int len = 0; - - /* zero params */ - RtlZeroMemory(Params, sizeof(UNICODE_STRING)); - - /* constructing volume UUID name */ - memset(UUID, 0, sizeof(USHORT) * 50); - for (i=0; i < 16; i++) { - if (i == 0) { - swprintf((wchar_t *)&UUID[len], L"{%2.2X",Vcb->SuperBlock->s_uuid[i]); - len += 3; - } else if (i == 15) { - swprintf((wchar_t *)&UUID[len], L"-%2.2X}", Vcb->SuperBlock->s_uuid[i]); - len +=4; - } else { - swprintf((wchar_t *)&UUID[len], L"-%2.2X", Vcb->SuperBlock->s_uuid[i]); - len += 3; - } - } - - /* allocating memory for UniBuffer */ - UniBuffer = Ext2AllocatePool(PagedPool, 1024, EXT2_PARAM_MAGIC); - if (NULL == UniBuffer) { - Status = STATUS_INSUFFICIENT_RESOURCES; - goto errorout; - } - RtlZeroMemory(UniBuffer, 1024); - - /* querying volume parameter string */ - RtlZeroMemory(&QueryTable[0], sizeof(RTL_QUERY_REGISTRY_TABLE) * 2); - QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; - QueryTable[0].Name = UUID; - QueryTable[0].EntryContext = &(UniName); - UniName.MaximumLength = 1024; - UniName.Length = 0; - UniName.Buffer = UniBuffer; - - Status = RtlQueryRegistryValues( - RTL_REGISTRY_ABSOLUTE, - Ext2Global->RegistryPath.Buffer, - &QueryTable[0], - NULL, - NULL - ); - - if (!NT_SUCCESS(Status)) { - goto errorout; - } - -errorout: - - if (NT_SUCCESS(Status)) { - *Params = UniName; - } else { - if (UniBuffer) { - Ext2FreePool(UniBuffer, EXT2_PARAM_MAGIC); - } - } - - return Status; -} - -VOID -Ext2ParseRegistryVolumeParams( - IN PUNICODE_STRING Params, - OUT PEXT2_VOLUME_PROPERTY3 Property -) -{ - WCHAR Codepage[CODEPAGE_MAXLEN]; - WCHAR Prefix[HIDINGPAT_LEN]; - WCHAR Suffix[HIDINGPAT_LEN]; - USHORT MountPoint[4]; - UCHAR DrvLetter[4]; - WCHAR wUID[8], wGID[8], wEUID[8], wEGID[8]; - CHAR sUID[8], sGID[8], sEUID[8], sEGID[8]; - - BOOLEAN bWriteSupport = FALSE, - bCheckBitmap = FALSE, - bCodeName = FALSE, - bMountPoint = FALSE; - BOOLEAN bUID = 0, bGID = 0, bEUID = 0, bEGID = 0; - - struct { - PWCHAR Name; /* parameters name */ - PBOOLEAN bExist; /* is it contained in params */ - USHORT Length; /* parameter value length */ - PWCHAR uValue; /* value buffer in unicode */ - PCHAR aValue; /* value buffer in ansi */ - } ParamPattern[] = { - /* writing support */ - {READING_ONLY, &Property->bReadonly, 0, NULL, NULL}, - {WRITING_SUPPORT, &bWriteSupport, 0, NULL, NULL}, - {EXT3_FORCEWRITING, &Property->bExt3Writable, 0, NULL, NULL}, - - /* need check bitmap */ - {CHECKING_BITMAP, &bCheckBitmap, 0, NULL, NULL}, - /* codepage */ - {CODEPAGE_NAME, &bCodeName, CODEPAGE_MAXLEN, - &Codepage[0], Property->Codepage}, - /* filter prefix and suffix */ - {HIDING_PREFIX, &Property->bHidingPrefix, HIDINGPAT_LEN, - &Prefix[0], Property->sHidingPrefix}, - {HIDING_SUFFIX, &Property->bHidingSuffix, HIDINGPAT_LEN, - &Suffix[0], Property->sHidingSuffix}, - {MOUNT_POINT, &bMountPoint, 4, - &MountPoint[0], &DrvLetter[0]}, - - {UID, &bUID, 8, &wUID[0], &sUID[0],}, - {GID, &bGID, 8, &wGID[0], &sGID[0]}, - {EUID, &bEUID, 8, &wEUID[0], &sEUID[0]}, - {EGID, &bEGID, 8, &wEGID[0], &sEGID[0]}, - - /* end */ - {NULL, NULL, 0, NULL} - }; - - USHORT i, j, k; - - RtlZeroMemory(Codepage, CODEPAGE_MAXLEN); - RtlZeroMemory(Prefix, HIDINGPAT_LEN); - RtlZeroMemory(Suffix, HIDINGPAT_LEN); - RtlZeroMemory(MountPoint, sizeof(USHORT) * 4); - RtlZeroMemory(DrvLetter, sizeof(CHAR) * 4); - - RtlZeroMemory(Property, sizeof(EXT2_VOLUME_PROPERTY3)); - Property->Magic = EXT2_VOLUME_PROPERTY_MAGIC; - Property->Command = APP_CMD_SET_PROPERTY3; - - for (i=0; ParamPattern[i].Name != NULL; i++) { - - UNICODE_STRING Name1=*Params, Name2; - RtlInitUnicodeString(&Name2, ParamPattern[i].Name); - *ParamPattern[i].bExist = FALSE; - - for (j=0; j * sizeof(WCHAR) + Name2.Length <= Params->Length ; j++) { - - Name1.MaximumLength = Params->Length - j * sizeof(WCHAR); - Name1.Length = Name2.Length; - Name1.Buffer = &Params->Buffer[j]; - - if (!RtlCompareUnicodeString(&Name1, &Name2, TRUE)) { - if (j * sizeof(WCHAR) + Name2.Length == Params->Length || - Name1.Buffer[Name2.Length/sizeof(WCHAR)] == L';' || - Name1.Buffer[Name2.Length/sizeof(WCHAR)] == L',' ) { - *(ParamPattern[i].bExist) = TRUE; - } else if ((j * 2 + Name2.Length < Params->Length + 2) || - (Name1.Buffer[Name2.Length/sizeof(WCHAR)] == L'=' )) { - j += Name2.Length/sizeof(WCHAR) + 1; - k = 0; - while ( j + k < Params->Length/2 && - k < ParamPattern[i].Length && - Params->Buffer[j+k] != L';' && - Params->Buffer[j+k] != L',' ) { - ParamPattern[i].uValue[k] = Params->Buffer[j + k++]; - } - if (k) { - NTSTATUS status; - ANSI_STRING AnsiName; - AnsiName.Length = 0; - AnsiName.MaximumLength =ParamPattern[i].Length; - AnsiName.Buffer = ParamPattern[i].aValue; - - Name2.Buffer = ParamPattern[i].uValue; - Name2.MaximumLength = Name2.Length = k * sizeof(WCHAR); - status = RtlUnicodeStringToAnsiString( - &AnsiName, &Name2, FALSE); - if (NT_SUCCESS(status)) { - *(ParamPattern[i].bExist) = TRUE; - } else { - *ParamPattern[i].bExist = FALSE; - } - } - } - break; - } - } - } - - if (bMountPoint) { - Property->DrvLetter = DrvLetter[0]; - Property->DrvLetter |= 0x80; - } - - if (bUID && bGID) { - SetFlag(Property->Flags2, EXT2_VPROP3_USERIDS); - sUID[7] = sGID[7] = sEUID[7] = sEGID[7] = 0; - Property->uid = (USHORT)atoi(sUID); - Property->gid = (USHORT)atoi(sGID); - if (bEUID) { - Property->euid = (USHORT)atoi(sEUID); - Property->egid = (USHORT)atoi(sEGID); - Property->EIDS = TRUE; - } else { - Property->EIDS = FALSE; - } - } else { - ClearFlag(Property->Flags2, EXT2_VPROP3_USERIDS); - } -} - -NTSTATUS -Ext2PerformRegistryVolumeParams(IN PEXT2_VCB Vcb) -{ - NTSTATUS Status; - UNICODE_STRING VolumeParams; - - Status = Ext2QueryVolumeParams(Vcb, &VolumeParams); - if (NT_SUCCESS(Status)) { - - /* set Vcb settings from registery */ - EXT2_VOLUME_PROPERTY3 Property; - Ext2ParseRegistryVolumeParams(&VolumeParams, &Property); - Ext2ProcessVolumeProperty(Vcb, &Property, sizeof(Property)); - - } else { - - /* don't support auto mount */ - if (IsFlagOn(Ext2Global->Flags, EXT2_AUTO_MOUNT)) { - Status = STATUS_SUCCESS; - } else { - Status = STATUS_UNSUCCESSFUL; - goto errorout; - } - - /* set Vcb settings from Ext2Global */ - if (IsFlagOn(Ext2Global->Flags, EXT2_SUPPORT_WRITING)) { - if (Vcb->IsExt3fs) { - if (IsFlagOn(Ext2Global->Flags, EXT3_FORCE_WRITING)) { - ClearLongFlag(Vcb->Flags, VCB_READ_ONLY); - } else { - SetLongFlag(Vcb->Flags, VCB_READ_ONLY); - } - } else { - ClearLongFlag(Vcb->Flags, VCB_READ_ONLY); - } - } else { - SetLongFlag(Vcb->Flags, VCB_READ_ONLY); - } - - /* set the default codepage */ - Vcb->Codepage.PageTable = Ext2Global->Codepage.PageTable; - memcpy(Vcb->Codepage.AnsiName, Ext2Global->Codepage.AnsiName, CODEPAGE_MAXLEN); - Vcb->Codepage.PageTable = Ext2Global->Codepage.PageTable; - - if (Vcb->bHidingPrefix = Ext2Global->bHidingPrefix) { - RtlCopyMemory( Vcb->sHidingPrefix, - Ext2Global->sHidingPrefix, - HIDINGPAT_LEN); - } else { - RtlZeroMemory( Vcb->sHidingPrefix, - HIDINGPAT_LEN); - } - - if (Vcb->bHidingSuffix = Ext2Global->bHidingSuffix) { - RtlCopyMemory( Vcb->sHidingSuffix, - Ext2Global->sHidingSuffix, - HIDINGPAT_LEN); - } else { - RtlZeroMemory( Vcb->sHidingSuffix, - HIDINGPAT_LEN); - } - } - -errorout: - - if (VolumeParams.Buffer) { - Ext2FreePool(VolumeParams.Buffer, EXT2_PARAM_MAGIC); - } - - return Status; -} - -NTSTATUS -Ext2InitializeLabel( - IN PEXT2_VCB Vcb, - IN PEXT2_SUPER_BLOCK Sb -) -{ - NTSTATUS status; - - USHORT Length; - UNICODE_STRING Label; - OEM_STRING OemName; - - Label.MaximumLength = 16 * sizeof(WCHAR); - Label.Length = 0; - Label.Buffer = Vcb->Vpb->VolumeLabel; - Vcb->Vpb->VolumeLabelLength = 0; - RtlZeroMemory(Label.Buffer, Label.MaximumLength); - - Length = 16; - while ( (Length > 0) && - ((Sb->s_volume_name[Length -1] == 0x00) || - (Sb->s_volume_name[Length - 1] == 0x20) ) - ) { - Length--; - } - - if (Length == 0) { - return STATUS_SUCCESS; - } - - OemName.Buffer = Sb->s_volume_name; - OemName.MaximumLength = 16; - OemName.Length = Length; - - status = Ext2OEMToUnicode(Vcb, &Label, &OemName); - if (NT_SUCCESS(status)) { - Vcb->Vpb->VolumeLabelLength = Label.Length; - } - - return status; -} - -static __inline BOOLEAN Ext2IsNullUuid (__u8 * uuid) -{ - int i; - for (i = 0; i < 16; i++) { - if (uuid[i]) { - break; - } - } - - return (i >= 16); -} - -#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0)) - -NTSTATUS -Ext2InitializeVcb( IN PEXT2_IRP_CONTEXT IrpContext, - IN PEXT2_VCB Vcb, - IN PEXT2_SUPER_BLOCK sb, - IN PDEVICE_OBJECT TargetDevice, - IN PDEVICE_OBJECT VolumeDevice, - IN PVPB Vpb ) -{ - NTSTATUS Status = STATUS_UNRECOGNIZED_VOLUME; - ULONG IoctlSize; - LONGLONG DiskSize; - LONGLONG PartSize; - UNICODE_STRING RootNode; - USHORT Buffer[2]; - ULONG ChangeCount = 0, features; - CC_FILE_SIZES FileSizes; - int i, has_huge_files; - - BOOLEAN VcbResourceInitialized = FALSE; - BOOLEAN NotifySyncInitialized = FALSE; - BOOLEAN ExtentsInitialized = FALSE; - BOOLEAN InodeLookasideInitialized = FALSE; - BOOLEAN GroupLoaded = FALSE; - - __try { - - if (Vpb == NULL) { - Status = STATUS_DEVICE_NOT_READY; - __leave; - } - - /* Reject mounting volume if we encounter unsupported incompat features */ - if (FlagOn(sb->s_feature_incompat, ~EXT4_FEATURE_INCOMPAT_SUPP)) { - Status = STATUS_UNRECOGNIZED_VOLUME; - __leave; - } - - /* Mount the volume RO if we encounter unsupported ro_compat features */ - if (FlagOn(sb->s_feature_ro_compat, ~EXT4_FEATURE_RO_COMPAT_SUPP)) { - SetLongFlag(Vcb->Flags, VCB_RO_COMPAT_READ_ONLY); - } - - /* Recognize the filesystem as Ext3fs if it supports journalling */ - if (IsFlagOn(sb->s_feature_compat, EXT4_FEATURE_COMPAT_HAS_JOURNAL)) { - Vcb->IsExt3fs = TRUE; - } - - /* check block size */ - Vcb->BlockSize = (EXT2_MIN_BLOCK_SIZE << sb->s_log_block_size); - /* we cannot handle volume with block size bigger than 64k */ - if (Vcb->BlockSize > EXT2_MAX_USER_BLKSIZE) { - Status = STATUS_UNRECOGNIZED_VOLUME; - __leave; - } - - if (Vcb->BlockSize >= PAGE_SIZE) { - Vcb->IoUnitBits = PAGE_SHIFT; - Vcb->IoUnitSize = PAGE_SIZE; - } else { - Vcb->IoUnitSize = Vcb->BlockSize; - Vcb->IoUnitBits = Ext2Log2(Vcb->BlockSize); - } - - /* initialize vcb header members ... */ - Vcb->Header.IsFastIoPossible = FastIoIsNotPossible; - Vcb->Header.Resource = &(Vcb->MainResource); - Vcb->Header.PagingIoResource = &(Vcb->PagingIoResource); - Vcb->OpenVolumeCount = 0; - Vcb->OpenHandleCount = 0; - Vcb->ReferenceCount = 0; - - /* initialize eresources */ - ExInitializeResourceLite(&Vcb->MainResource); - ExInitializeResourceLite(&Vcb->PagingIoResource); - ExInitializeResourceLite(&Vcb->MetaInode); - ExInitializeResourceLite(&Vcb->MetaBlock); - ExInitializeResourceLite(&Vcb->McbLock); - ExInitializeResourceLite(&Vcb->FcbLock); - ExInitializeResourceLite(&Vcb->sbi.s_gd_lock); -#ifndef _WIN2K_TARGET_ - ExInitializeFastMutex(&Vcb->Mutex); - FsRtlSetupAdvancedHeader(&Vcb->Header, &Vcb->Mutex); -#endif - VcbResourceInitialized = TRUE; - - /* initialize Fcb list head */ - InitializeListHead(&Vcb->FcbList); - - /* initialize Mcb list head */ - InitializeListHead(&(Vcb->McbList)); - - /* initialize directory notify list */ - InitializeListHead(&Vcb->NotifyList); - FsRtlNotifyInitializeSync(&Vcb->NotifySync); - NotifySyncInitialized = TRUE; - - /* superblock checking */ - Vcb->SuperBlock = sb; - - /* initialize Vpb and Label */ - Vcb->DeviceObject = VolumeDevice; - Vcb->TargetDeviceObject = TargetDevice; - Vcb->Vpb = Vpb; - Vcb->RealDevice = Vpb->RealDevice; - Vpb->DeviceObject = VolumeDevice; - - /* set inode size */ - Vcb->InodeSize = (ULONG)sb->s_inode_size; - if (Vcb->InodeSize == 0) { - Vcb->InodeSize = EXT2_GOOD_OLD_INODE_SIZE; - } - - /* initialize inode lookaside list */ - ExInitializeNPagedLookasideList(&(Vcb->InodeLookasideList), - NULL, NULL, 0, Vcb->InodeSize, - 'SNIE', 0); - - InodeLookasideInitialized = TRUE; - - /* initialize label in Vpb */ - Status = Ext2InitializeLabel(Vcb, sb); - if (!NT_SUCCESS(Status)) { - DbgBreak(); - } - - /* check device characteristics flags */ - if (IsFlagOn(Vpb->RealDevice->Characteristics, FILE_REMOVABLE_MEDIA)) { - SetLongFlag(Vcb->Flags, VCB_REMOVABLE_MEDIA); - } - - if (IsFlagOn(Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) { - SetLongFlag(Vcb->Flags, VCB_FLOPPY_DISK); - } - - if (IsFlagOn(Vpb->RealDevice->Characteristics, FILE_READ_ONLY_DEVICE)) { - SetLongFlag(Vcb->Flags, VCB_WRITE_PROTECTED); - } - - if (IsFlagOn(TargetDevice->Characteristics, FILE_READ_ONLY_DEVICE)) { - SetLongFlag(Vcb->Flags, VCB_WRITE_PROTECTED); - } - - /* verify device is writable ? */ - if (Ext2IsMediaWriteProtected(IrpContext, TargetDevice)) { - SetFlag(Vcb->Flags, VCB_WRITE_PROTECTED); - } - - /* initialize UUID and serial number */ - if (Ext2IsNullUuid(sb->s_uuid)) { - ExUuidCreate((UUID *)sb->s_uuid); - } - Vpb->SerialNumber = ((ULONG*)sb->s_uuid)[0] + - ((ULONG*)sb->s_uuid)[1] + - ((ULONG*)sb->s_uuid)[2] + - ((ULONG*)sb->s_uuid)[3]; - - /* query partition size and disk geometry parameters */ - DiskSize = Vcb->DiskGeometry.Cylinders.QuadPart * - Vcb->DiskGeometry.TracksPerCylinder * - Vcb->DiskGeometry.SectorsPerTrack * - Vcb->DiskGeometry.BytesPerSector; - - IoctlSize = sizeof(PARTITION_INFORMATION); - Status = Ext2DiskIoControl( - TargetDevice, - IOCTL_DISK_GET_PARTITION_INFO, - NULL, - 0, - &Vcb->PartitionInformation, - &IoctlSize ); - if (NT_SUCCESS(Status)) { - PartSize = Vcb->PartitionInformation.PartitionLength.QuadPart; - } else { - Vcb->PartitionInformation.StartingOffset.QuadPart = 0; - Vcb->PartitionInformation.PartitionLength.QuadPart = DiskSize; - PartSize = DiskSize; - Status = STATUS_SUCCESS; - } - Vcb->Header.AllocationSize.QuadPart = - Vcb->Header.FileSize.QuadPart = PartSize; - - Vcb->Header.ValidDataLength.QuadPart = - Vcb->Header.FileSize.QuadPart; - - /* verify count */ - IoctlSize = sizeof(ULONG); - Status = Ext2DiskIoControl( - TargetDevice, - IOCTL_DISK_CHECK_VERIFY, - NULL, - 0, - &ChangeCount, - &IoctlSize ); - - if (!NT_SUCCESS(Status)) { - __leave; - } - Vcb->ChangeCount = ChangeCount; - - /* create the stream object for ext2 volume */ - Vcb->Volume = IoCreateStreamFileObject(NULL, Vcb->Vpb->RealDevice); - if (!Vcb->Volume) { - Status = STATUS_UNRECOGNIZED_VOLUME; - __leave; - } - - /* initialize streaming object file */ - Vcb->Volume->SectionObjectPointer = &(Vcb->SectionObject); - Vcb->Volume->ReadAccess = TRUE; - Vcb->Volume->WriteAccess = TRUE; - Vcb->Volume->DeleteAccess = TRUE; - Vcb->Volume->FsContext = (PVOID) Vcb; - Vcb->Volume->FsContext2 = NULL; - Vcb->Volume->Vpb = Vcb->Vpb; - - FileSizes.AllocationSize.QuadPart = - FileSizes.FileSize.QuadPart = - FileSizes.ValidDataLength.QuadPart = - Vcb->Header.AllocationSize.QuadPart; - - CcInitializeCacheMap( Vcb->Volume, - &FileSizes, - TRUE, - &(Ext2Global->CacheManagerNoOpCallbacks), - Vcb ); - - /* initialize disk block LargetMcb and entry Mcb, - it will raise an expcetion if failed */ - __try { - FsRtlInitializeLargeMcb(&(Vcb->Extents), PagedPool); - } __except (EXCEPTION_EXECUTE_HANDLER) { - Status = STATUS_INSUFFICIENT_RESOURCES; - DbgBreak(); - } - if (!NT_SUCCESS(Status)) { - __leave; - } - ExtentsInitialized = TRUE; - - /* set block device */ - Vcb->bd.bd_dev = Vcb->RealDevice; - Vcb->bd.bd_geo = Vcb->DiskGeometry; - Vcb->bd.bd_part = Vcb->PartitionInformation; - Vcb->bd.bd_volume = Vcb->Volume; - Vcb->bd.bd_priv = (void *) Vcb; - memset(&Vcb->bd.bd_bh_root, 0, sizeof(struct rb_root)); - InitializeListHead(&Vcb->bd.bd_bh_free); - ExInitializeResourceLite(&Vcb->bd.bd_bh_lock); - KeInitializeEvent(&Vcb->bd.bd_bh_notify, - NotificationEvent, TRUE); - Vcb->bd.bd_bh_cache = kmem_cache_create("bd_bh_buffer", - Vcb->BlockSize, 0, 0, NULL); - if (!Vcb->bd.bd_bh_cache) { - Status = STATUS_INSUFFICIENT_RESOURCES; - __leave; - } - - Vcb->SectorBits = Ext2Log2(SECTOR_SIZE); - Vcb->sb.s_magic = sb->s_magic; - Vcb->sb.s_bdev = &Vcb->bd; - Vcb->sb.s_blocksize = BLOCK_SIZE; - Vcb->sb.s_blocksize_bits = BLOCK_BITS; - Vcb->sb.s_priv = (void *) Vcb; - Vcb->sb.s_fs_info = &Vcb->sbi; - - Vcb->sbi.s_es = sb; - Vcb->sbi.s_blocks_per_group = sb->s_blocks_per_group; - Vcb->sbi.s_first_ino = sb->s_first_ino; - Vcb->sbi.s_desc_size = sb->s_desc_size; - - if (EXT3_HAS_INCOMPAT_FEATURE(&Vcb->sb, EXT4_FEATURE_INCOMPAT_64BIT)) { - if (Vcb->sbi.s_desc_size < EXT4_MIN_DESC_SIZE_64BIT || - Vcb->sbi.s_desc_size > EXT4_MAX_DESC_SIZE || - !is_power_of_2(Vcb->sbi.s_desc_size)) { - DEBUG(DL_ERR, ("EXT4-fs: unsupported descriptor size %lu\n", Vcb->sbi.s_desc_size)); - Status = STATUS_DISK_CORRUPT_ERROR; - __leave; - } - } else { - Vcb->sbi.s_desc_size = EXT4_MIN_DESC_SIZE; - } - - Vcb->sbi.s_blocks_per_group = sb->s_blocks_per_group; - Vcb->sbi.s_inodes_per_group = sb->s_inodes_per_group; - if (EXT3_INODES_PER_GROUP(&Vcb->sb) == 0) { - Status = STATUS_DISK_CORRUPT_ERROR; - __leave; - } - Vcb->sbi.s_inodes_per_block = BLOCK_SIZE / Vcb->InodeSize; - if (Vcb->sbi.s_inodes_per_block == 0) { - Status = STATUS_DISK_CORRUPT_ERROR; - __leave; - } - Vcb->sbi.s_itb_per_group = Vcb->sbi.s_inodes_per_group / - Vcb->sbi.s_inodes_per_block; - - - Vcb->sbi.s_desc_per_block = BLOCK_SIZE / GROUP_DESC_SIZE; - Vcb->sbi.s_desc_per_block_bits = ilog2(Vcb->sbi.s_desc_per_block); - - for (i=0; i < 4; i++) { - Vcb->sbi.s_hash_seed[i] = sb->s_hash_seed[i]; - } - Vcb->sbi.s_def_hash_version = sb->s_def_hash_version; - - if (le32_to_cpu(sb->s_rev_level) == EXT3_GOOD_OLD_REV && - (EXT3_HAS_COMPAT_FEATURE(&Vcb->sb, ~0U) || - EXT3_HAS_RO_COMPAT_FEATURE(&Vcb->sb, ~0U) || - EXT3_HAS_INCOMPAT_FEATURE(&Vcb->sb, ~0U))) { - printk(KERN_WARNING - "EXT3-fs warning: feature flags set on rev 0 fs, " - "running e2fsck is recommended\n"); - } - - /* - * Check feature flags regardless of the revision level, since we - * previously didn't change the revision level when setting the flags, - * so there is a chance incompat flags are set on a rev 0 filesystem. - */ - features = EXT3_HAS_INCOMPAT_FEATURE(&Vcb->sb, ~EXT4_FEATURE_INCOMPAT_SUPP); - if (features & EXT4_FEATURE_INCOMPAT_DIRDATA) { - SetLongFlag(Vcb->Flags, VCB_READ_ONLY); - ClearFlag(features, EXT4_FEATURE_INCOMPAT_DIRDATA); - } - if (features) { - printk(KERN_ERR "EXT3-fs: %s: couldn't mount because of " - "unsupported optional features (%x).\n", - Vcb->sb.s_id, le32_to_cpu(features)); - Status = STATUS_UNRECOGNIZED_VOLUME; - __leave; - } - - features = EXT3_HAS_RO_COMPAT_FEATURE(&Vcb->sb, ~EXT4_FEATURE_RO_COMPAT_SUPP); - if (features) { - printk(KERN_ERR "EXT3-fs: %s: unsupported optional features in this volume: (%x).\n", - Vcb->sb.s_id, le32_to_cpu(features)); - if (CanIWrite(Vcb)) { - } else { - SetLongFlag(Vcb->Flags, VCB_READ_ONLY); - } - } - - has_huge_files = EXT3_HAS_RO_COMPAT_FEATURE(&Vcb->sb, EXT4_FEATURE_RO_COMPAT_HUGE_FILE); - - Vcb->sb.s_maxbytes = ext3_max_size(BLOCK_BITS, has_huge_files); - Vcb->max_bitmap_bytes = ext3_max_bitmap_size(BLOCK_BITS, - has_huge_files); - Vcb->max_bytes = ext3_max_size(BLOCK_BITS, has_huge_files); - - /* calculate maximum file bocks ... */ - { - ULONG dwData[EXT2_BLOCK_TYPES] = {EXT2_NDIR_BLOCKS, 1, 1, 1}; - ULONG i; - - ASSERT(BLOCK_BITS == Ext2Log2(BLOCK_SIZE)); - - Vcb->sbi.s_groups_count = (ULONG)(ext3_blocks_count(sb) - sb->s_first_data_block + - sb->s_blocks_per_group - 1) / sb->s_blocks_per_group; - - Vcb->max_data_blocks = 0; - for (i = 0; i < EXT2_BLOCK_TYPES; i++) { - if (BLOCK_BITS >= 12 && i == (EXT2_BLOCK_TYPES - 1)) { - dwData[i] = 0x40000000; - } else { - dwData[i] = dwData[i] << ((BLOCK_BITS - 2) * i); - } - Vcb->max_blocks_per_layer[i] = dwData[i]; - Vcb->max_data_blocks += Vcb->max_blocks_per_layer[i]; - } - } - - Vcb->sbi.s_gdb_count = (Vcb->sbi.s_groups_count + Vcb->sbi.s_desc_per_block - 1) / - Vcb->sbi.s_desc_per_block; - /* load all gorup desc */ - if (!Ext2LoadGroup(Vcb)) { - Status = STATUS_UNSUCCESSFUL; - __leave; - } - GroupLoaded = TRUE; - - /* recovery journal since it's ext3 */ - if (Vcb->IsExt3fs) { - Ext2RecoverJournal(IrpContext, Vcb); - if (IsFlagOn(Vcb->Flags, VCB_JOURNAL_RECOVER)) { - SetLongFlag(Vcb->Flags, VCB_READ_ONLY); - } - } - - /* Now allocating the mcb for root ... */ - Buffer[0] = L'\\'; - Buffer[1] = 0; - RootNode.Buffer = Buffer; - RootNode.MaximumLength = RootNode.Length = 2; - Vcb->McbTree = Ext2AllocateMcb( - Vcb, &RootNode, NULL, - FILE_ATTRIBUTE_DIRECTORY - ); - if (!Vcb->McbTree) { - DbgBreak(); - Status = STATUS_UNSUCCESSFUL; - __leave; - } - - Vcb->sb.s_root = Ext2BuildEntry(Vcb, NULL, &RootNode); - if (!Vcb->sb.s_root) { - DbgBreak(); - Status = STATUS_UNSUCCESSFUL; - __leave; - } - Vcb->sb.s_root->d_sb = &Vcb->sb; - Vcb->sb.s_root->d_inode = &Vcb->McbTree->Inode; - Vcb->McbTree->de = Vcb->sb.s_root; - - /* load root inode */ - Vcb->McbTree->Inode.i_ino = EXT2_ROOT_INO; - Vcb->McbTree->Inode.i_sb = &Vcb->sb; - if (!Ext2LoadInode(Vcb, &Vcb->McbTree->Inode)) { - DbgBreak(); - Status = STATUS_CANT_WAIT; - __leave; - } - - /* initializeroot node */ - Vcb->McbTree->CreationTime = Ext2NtTime(Vcb->McbTree->Inode.i_ctime); - Vcb->McbTree->LastAccessTime = Ext2NtTime(Vcb->McbTree->Inode.i_atime); - Vcb->McbTree->LastWriteTime = Ext2NtTime(Vcb->McbTree->Inode.i_mtime); - Vcb->McbTree->ChangeTime = Ext2NtTime(Vcb->McbTree->Inode.i_mtime); - - /* check bitmap if user specifies it */ - if (IsFlagOn(Ext2Global->Flags, EXT2_CHECKING_BITMAP)) { - Ext2CheckBitmapConsistency(IrpContext, Vcb); - } - - /* get anything doen, then refer target device */ - ObReferenceObject(Vcb->TargetDeviceObject); - - /* query parameters from registry */ - Ext2PerformRegistryVolumeParams(Vcb); - - SetLongFlag(Vcb->Flags, VCB_INITIALIZED); - - } __finally { - - if (!NT_SUCCESS(Status)) { - - if (Vcb->McbTree) { - Ext2FreeMcb(Vcb, Vcb->McbTree); - } - - if (InodeLookasideInitialized) { - ExDeleteNPagedLookasideList(&(Vcb->InodeLookasideList)); - } - - if (ExtentsInitialized) { - if (Vcb->bd.bd_bh_cache) { - if (GroupLoaded) - Ext2PutGroup(Vcb); - kmem_cache_destroy(Vcb->bd.bd_bh_cache); - } - FsRtlUninitializeLargeMcb(&(Vcb->Extents)); - } - - if (Vcb->Volume) { - if (Vcb->Volume->PrivateCacheMap) { - Ext2SyncUninitializeCacheMap(Vcb->Volume); - } - ObDereferenceObject(Vcb->Volume); - } - - if (NotifySyncInitialized) { - FsRtlNotifyUninitializeSync(&Vcb->NotifySync); - } - - if (VcbResourceInitialized) { - ExDeleteResourceLite(&Vcb->FcbLock); - ExDeleteResourceLite(&Vcb->McbLock); - ExDeleteResourceLite(&Vcb->MetaInode); - ExDeleteResourceLite(&Vcb->MetaBlock); - ExDeleteResourceLite(&Vcb->sbi.s_gd_lock); - ExDeleteResourceLite(&Vcb->MainResource); - ExDeleteResourceLite(&Vcb->PagingIoResource); - } - } - } - - return Status; -} - - -VOID -Ext2TearDownStream(IN PEXT2_VCB Vcb) -{ - PFILE_OBJECT Stream = Vcb->Volume; - IO_STATUS_BLOCK IoStatus; - - ASSERT(Vcb != NULL); - ASSERT((Vcb->Identifier.Type == EXT2VCB) && - (Vcb->Identifier.Size == sizeof(EXT2_VCB))); - - if (Stream) { - - Vcb->Volume = NULL; - - if (IsFlagOn(Stream->Flags, FO_FILE_MODIFIED)) { - CcFlushCache(&(Vcb->SectionObject), NULL, 0, &IoStatus); - ClearFlag(Stream->Flags, FO_FILE_MODIFIED); - } - - if (Stream->PrivateCacheMap) { - Ext2SyncUninitializeCacheMap(Stream); - } - - ObDereferenceObject(Stream); - } -} - -VOID -Ext2DestroyVcb (IN PEXT2_VCB Vcb) -{ - ASSERT(Vcb != NULL); - ASSERT((Vcb->Identifier.Type == EXT2VCB) && - (Vcb->Identifier.Size == sizeof(EXT2_VCB))); - - DEBUG(DL_FUN, ("Ext2DestroyVcb ...\n")); - - if (Vcb->Volume) { - Ext2TearDownStream(Vcb); - } - ASSERT(NULL == Vcb->Volume); - - FsRtlNotifyUninitializeSync(&Vcb->NotifySync); - Ext2ListExtents(&Vcb->Extents); - FsRtlUninitializeLargeMcb(&(Vcb->Extents)); - - Ext2CleanupAllMcbs(Vcb); - - Ext2DropBH(Vcb); - - if (Vcb->bd.bd_bh_cache) - kmem_cache_destroy(Vcb->bd.bd_bh_cache); - ExDeleteResourceLite(&Vcb->bd.bd_bh_lock); - - if (Vcb->SuperBlock) { - Ext2FreePool(Vcb->SuperBlock, EXT2_SB_MAGIC); - Vcb->SuperBlock = NULL; - } - - if (IsFlagOn(Vcb->Flags, VCB_NEW_VPB)) { - ASSERT(Vcb->Vpb2 != NULL); - DEBUG(DL_DBG, ("Ext2DestroyVcb: Vpb2 to be freed: %p\n", Vcb->Vpb2)); - ExFreePoolWithTag(Vcb->Vpb2, TAG_VPB); - DEC_MEM_COUNT(PS_VPB, Vcb->Vpb2, sizeof(VPB)); - Vcb->Vpb2 = NULL; - } - - ObDereferenceObject(Vcb->TargetDeviceObject); - - ExDeleteNPagedLookasideList(&(Vcb->InodeLookasideList)); - ExDeleteResourceLite(&Vcb->FcbLock); - ExDeleteResourceLite(&Vcb->McbLock); - ExDeleteResourceLite(&Vcb->MetaInode); - ExDeleteResourceLite(&Vcb->MetaBlock); - ExDeleteResourceLite(&Vcb->sbi.s_gd_lock); - ExDeleteResourceLite(&Vcb->PagingIoResource); - ExDeleteResourceLite(&Vcb->MainResource); - - DEBUG(DL_DBG, ("Ext2DestroyVcb: DevObject=%p Vcb=%p\n", Vcb->DeviceObject, Vcb)); - IoDeleteDevice(Vcb->DeviceObject); - DEC_MEM_COUNT(PS_VCB, Vcb->DeviceObject, sizeof(EXT2_VCB)); -} - - -/* uninitialize cache map */ - -VOID -Ext2SyncUninitializeCacheMap ( - IN PFILE_OBJECT FileObject -) -{ - CACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent; - NTSTATUS WaitStatus; - LARGE_INTEGER Ext2LargeZero = {0,0}; - - - KeInitializeEvent( &UninitializeCompleteEvent.Event, - SynchronizationEvent, - FALSE); - - CcUninitializeCacheMap( FileObject, - &Ext2LargeZero, - &UninitializeCompleteEvent ); - - WaitStatus = KeWaitForSingleObject( &UninitializeCompleteEvent.Event, - Executive, - KernelMode, - FALSE, - NULL); - - ASSERT (NT_SUCCESS(WaitStatus)); -} - -/* Link Mcb to tail of Vcb->McbList queue */ - -VOID -Ext2LinkTailMcb(PEXT2_VCB Vcb, PEXT2_MCB Mcb) -{ - if (Mcb->Inode.i_ino == EXT2_ROOT_INO) { - return; - } - - ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); - - if (IsFlagOn(Mcb->Flags, MCB_VCB_LINK)) { - DEBUG(DL_RES, ( "Ext2LinkTailMcb: %wZ already linked.\n", - &Mcb->FullName)); - } else { - InsertTailList(&Vcb->McbList, &Mcb->Link); - SetLongFlag(Mcb->Flags, MCB_VCB_LINK); - Ext2ReferXcb(&Vcb->NumOfMcb); - } - - ExReleaseResourceLite(&Vcb->McbLock); -} - -/* Link Mcb to head of Vcb->McbList queue */ - -VOID -Ext2LinkHeadMcb(PEXT2_VCB Vcb, PEXT2_MCB Mcb) -{ - if (Mcb->Inode.i_ino == EXT2_ROOT_INO) { - return; - } - - ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); - - if (!IsFlagOn(Mcb->Flags, MCB_VCB_LINK)) { - InsertHeadList(&Vcb->McbList, &Mcb->Link); - SetLongFlag(Mcb->Flags, MCB_VCB_LINK); - Ext2ReferXcb(&Vcb->NumOfMcb); - } else { - DEBUG(DL_RES, ( "Ext2LinkHeadMcb: %wZ already linked.\n", - &Mcb->FullName)); - } - ExReleaseResourceLite(&Vcb->McbLock); -} - -/* Unlink Mcb from Vcb->McbList queue */ - -VOID -Ext2UnlinkMcb(PEXT2_VCB Vcb, PEXT2_MCB Mcb) -{ - if (Mcb->Inode.i_ino == EXT2_ROOT_INO) { - return; - } - - ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); - - if (IsFlagOn(Mcb->Flags, MCB_VCB_LINK)) { - RemoveEntryList(&(Mcb->Link)); - ClearLongFlag(Mcb->Flags, MCB_VCB_LINK); - Ext2DerefXcb(&Vcb->NumOfMcb); - } else { - DEBUG(DL_RES, ( "Ext2UnlinkMcb: %wZ already unlinked.\n", - &Mcb->FullName)); - } - ExReleaseResourceLite(&Vcb->McbLock); -} - -/* get the first Mcb record in Vcb->McbList */ - -PEXT2_MCB -Ext2FirstUnusedMcb(PEXT2_VCB Vcb, BOOLEAN Wait, ULONG Number) -{ - PEXT2_MCB Head = NULL; - PEXT2_MCB Tail = NULL; - PEXT2_MCB Mcb = NULL; - PLIST_ENTRY List = NULL; - ULONG i = 0; - LARGE_INTEGER start, now; - - if (!ExAcquireResourceExclusiveLite(&Vcb->McbLock, Wait)) { - return NULL; - } - - KeQuerySystemTime(&start); - - while (Number--) { - - BOOLEAN Skip = TRUE; - - if (IsListEmpty(&Vcb->McbList)) { - break; - } - - while (i++ < Vcb->NumOfMcb) { - - KeQuerySystemTime(&now); - if (now.QuadPart > start.QuadPart + (LONGLONG)10*1000*1000) { - break; - } - - List = RemoveHeadList(&Vcb->McbList); - Mcb = CONTAINING_RECORD(List, EXT2_MCB, Link); - ASSERT(IsFlagOn(Mcb->Flags, MCB_VCB_LINK)); - - if (Mcb->Fcb == NULL && !IsMcbRoot(Mcb) && - Mcb->Refercount == 0 && - (Mcb->Child == NULL || IsMcbSymLink(Mcb))) { - - Ext2RemoveMcb(Vcb, Mcb); - ClearLongFlag(Mcb->Flags, MCB_VCB_LINK); - Ext2DerefXcb(&Vcb->NumOfMcb); - - /* attach all Mcb into a chain*/ - if (Head) { - ASSERT(Tail != NULL); - Tail->Next = Mcb; - Tail = Mcb; - } else { - Head = Tail = Mcb; - } - Tail->Next = NULL; - Skip = FALSE; - - } else { - - InsertTailList(&Vcb->McbList, &Mcb->Link); - Mcb = NULL; - } - } - - if (Skip) - break; - } - - ExReleaseResourceLite(&Vcb->McbLock); - - return Head; -} - - -/* Reaper thread to release unused Mcb blocks */ -VOID -Ext2McbReaperThread( - PVOID Context -) -{ - PEXT2_REAPER Reaper = Context; - PLIST_ENTRY List = NULL; - LARGE_INTEGER Timeout; - - PEXT2_VCB Vcb = NULL; - PEXT2_MCB Mcb = NULL; - - ULONG i, NumOfMcbs; - - BOOLEAN GlobalAcquired = FALSE; - - BOOLEAN DidNothing = TRUE; - BOOLEAN LastState = TRUE; - BOOLEAN WaitLock; - - __try { - - Reaper->Thread = PsGetCurrentThread(); - - /* wake up DirverEntry */ - KeSetEvent(&Reaper->Engine, 0, FALSE); - - /* now process looping */ - while (!IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) { - - WaitLock = FALSE; - - /* calculate how long we need wait */ - if (Ext2Global->PerfStat.Current.Mcb > (((ULONG)Ext2Global->MaxDepth) * 128)) { - Timeout.QuadPart = (LONGLONG)-1000*1000; /* 0.1 second */ - NumOfMcbs = Ext2Global->MaxDepth * 4; - WaitLock = TRUE; - } else if (Ext2Global->PerfStat.Current.Mcb > (((ULONG)Ext2Global->MaxDepth) * 32)) { - Timeout.QuadPart = (LONGLONG)-1000*1000*5; /* 0.5 second */ - NumOfMcbs = Ext2Global->MaxDepth * 2; - WaitLock = TRUE; - } else if (Ext2Global->PerfStat.Current.Mcb > (((ULONG)Ext2Global->MaxDepth) * 8)) { - Timeout.QuadPart = (LONGLONG)-1000*1000*10; /* 1 second */ - NumOfMcbs = Ext2Global->MaxDepth; - } else if (Ext2Global->PerfStat.Current.Mcb > (((ULONG)Ext2Global->MaxDepth) * 2)) { - Timeout.QuadPart = (LONGLONG)-2*1000*1000*10; /* 2 second */ - NumOfMcbs = Ext2Global->MaxDepth / 4; - } else if (Ext2Global->PerfStat.Current.Mcb > (ULONG)Ext2Global->MaxDepth) { - Timeout.QuadPart = (LONGLONG)-4*1000*1000*10; /* 4 seconds */ - NumOfMcbs = Ext2Global->MaxDepth / 8; - } else if (DidNothing) { - Timeout.QuadPart = (LONGLONG)-8*1000*1000*10; /* 8 seconds */ - if (LastState) { - Timeout.QuadPart *= 2; - } - NumOfMcbs = Ext2Global->MaxDepth / 16; - } else { - Timeout.QuadPart = (LONGLONG)-5*1000*1000*10; /* 5 seconds */ - if (LastState) { - Timeout.QuadPart *= 2; - } - NumOfMcbs = Ext2Global->MaxDepth / 32; - } - - if (NumOfMcbs == 0) - NumOfMcbs = 1; - - LastState = DidNothing; - - /* wait until it is waken or it times out */ - KeWaitForSingleObject( - &Reaper->Wait, - Executive, - KernelMode, - FALSE, - &Timeout - ); - - if (IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) - break; - - DidNothing = TRUE; - - /* acquire global exclusive lock */ - if (!ExAcquireResourceSharedLite(&Ext2Global->Resource, WaitLock)) { - continue; - } - GlobalAcquired = TRUE; - - /* search all Vcb to get unused resources freed to system */ - for (List = Ext2Global->VcbList.Flink; - List != &(Ext2Global->VcbList); - List = List->Flink ) { - - Vcb = CONTAINING_RECORD(List, EXT2_VCB, Next); - - Mcb = Ext2FirstUnusedMcb(Vcb, WaitLock, NumOfMcbs); - while (Mcb) { - PEXT2_MCB Next = Mcb->Next; - DEBUG(DL_RES, ( "Ext2ReaperThread: releasing Mcb (%p): %wZ" - " Total: %xh\n", Mcb, &Mcb->FullName, - Ext2Global->PerfStat.Current.Mcb)); - Ext2FreeMcb(Vcb, Mcb); - Mcb = Next; - LastState = DidNothing = FALSE; - } - } - if (DidNothing) { - KeClearEvent(&Reaper->Wait); - } - if (GlobalAcquired) { - ExReleaseResourceLite(&Ext2Global->Resource); - GlobalAcquired = FALSE; - } - } - - } __finally { - - if (GlobalAcquired) { - ExReleaseResourceLite(&Ext2Global->Resource); - } - - KeSetEvent(&Reaper->Engine, 0, FALSE); - } - - PsTerminateSystemThread(STATUS_SUCCESS); -} - - -/* get buffer heads from global Vcb BH list */ - -BOOLEAN -Ext2QueryUnusedBH(PEXT2_VCB Vcb, PLIST_ENTRY head) -{ - struct buffer_head *bh = NULL; - PLIST_ENTRY next = NULL; - LARGE_INTEGER start, now; - BOOLEAN wake = FALSE; - - KeQuerySystemTime(&start); - - ExAcquireResourceExclusiveLite(&Vcb->bd.bd_bh_lock, TRUE); - - while (!IsListEmpty(&Vcb->bd.bd_bh_free)) { - - KeQuerySystemTime(&now); - if (now.QuadPart > start.QuadPart + (LONGLONG)10*1000*1000) { - break; - } - - next = RemoveHeadList(&Vcb->bd.bd_bh_free); - bh = CONTAINING_RECORD(next, struct buffer_head, b_link); - if (atomic_read(&bh->b_count)) { - InitializeListHead(&bh->b_link); - /* to be inserted by brelse */ - continue; - } - - if ( IsFlagOn(Vcb->Flags, VCB_BEING_DROPPED) || - (bh->b_ts_drop.QuadPart + (LONGLONG)10*1000*1000*15) > now.QuadPart || - (bh->b_ts_creat.QuadPart + (LONGLONG)10*1000*1000*180) > now.QuadPart) { - InsertTailList(head, &bh->b_link); - buffer_head_remove(&Vcb->bd, bh); - } else { - InsertHeadList(&Vcb->bd.bd_bh_free, &bh->b_link); - break; - } - } - - wake = IsListEmpty(&Vcb->bd.bd_bh_free); - ExReleaseResourceLite(&Vcb->bd.bd_bh_lock); - - if (wake) - KeSetEvent(&Vcb->bd.bd_bh_notify, 0, FALSE); - - return IsFlagOn(Vcb->Flags, VCB_BEING_DROPPED); -} - - -/* Reaper thread to release unused buffer heads */ -VOID -Ext2bhReaperThread( - PVOID Context -) -{ - PEXT2_REAPER Reaper = Context; - PEXT2_VCB Vcb = NULL; - LIST_ENTRY List, *Link; - LARGE_INTEGER Timeout; - - BOOLEAN GlobalAcquired = FALSE; - BOOLEAN DidNothing = FALSE; - BOOLEAN NonWait = FALSE; - - __try { - - Reaper->Thread = PsGetCurrentThread(); - - /* wake up DirverEntry */ - KeSetEvent(&Reaper->Engine, 0, FALSE); - - /* now process looping */ - while (!IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) { - - /* wait until it is waken or it times out */ - if (NonWait) { - Timeout.QuadPart = (LONGLONG)-10*1000*10; - NonWait = FALSE; - } else if (DidNothing) { - Timeout.QuadPart = Timeout.QuadPart * 2; - } else { - Timeout.QuadPart = (LONGLONG)-10*1000*1000*10; /* 10 seconds */ - } - KeWaitForSingleObject( - &Reaper->Wait, - Executive, - KernelMode, - FALSE, - &Timeout - ); - - if (IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) - break; - - InitializeListHead(&List); - - /* acquire global exclusive lock */ - ExAcquireResourceSharedLite(&Ext2Global->Resource, TRUE); - GlobalAcquired = TRUE; - /* search all Vcb to get unused resources freed to system */ - for (Link = Ext2Global->VcbList.Flink; - Link != &(Ext2Global->VcbList); - Link = Link->Flink ) { - - Vcb = CONTAINING_RECORD(Link, EXT2_VCB, Next); - NonWait = Ext2QueryUnusedBH(Vcb, &List); - } - DidNothing = IsListEmpty(&List); - if (DidNothing) { - KeClearEvent(&Reaper->Wait); - } - if (GlobalAcquired) { - ExReleaseResourceLite(&Ext2Global->Resource); - GlobalAcquired = FALSE; - } - - while (!IsListEmpty(&List)) { - struct buffer_head *bh; - Link = RemoveHeadList(&List); - bh = CONTAINING_RECORD(Link, struct buffer_head, b_link); - ASSERT(0 == atomic_read(&bh->b_count)); - free_buffer_head(bh); - } - } - - } __finally { - - if (GlobalAcquired) { - ExReleaseResourceLite(&Ext2Global->Resource); - } - - KeSetEvent(&Reaper->Engine, 0, FALSE); - } - - PsTerminateSystemThread(STATUS_SUCCESS); -} - -/* get unused Fcbs to free */ - -BOOLEAN -Ext2QueryUnusedFcb(PEXT2_VCB Vcb, PLIST_ENTRY list) -{ - PEXT2_FCB Fcb; - PLIST_ENTRY next = NULL; - LARGE_INTEGER start, now; - - ULONG count = 0; - ULONG tries = 0; - BOOLEAN wake = FALSE; - BOOLEAN retry = TRUE; - - KeQuerySystemTime(&start); - - ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); - -again: - - KeQuerySystemTime(&now); - while (!IsListEmpty(&Vcb->FcbList)) { - - next = RemoveHeadList(&Vcb->FcbList); - Fcb = CONTAINING_RECORD(next, EXT2_FCB, Next); - - if (Fcb->ReferenceCount > 0) { - InsertTailList(&Vcb->FcbList, &Fcb->Next); - break; - } - - retry = FALSE; - - if (now.QuadPart < Fcb->TsDrop.QuadPart + 10*1000*1000*120) { - InsertHeadList(&Vcb->FcbList, &Fcb->Next); - break; - } - - Ext2UnlinkFcb(Fcb); - Ext2DerefXcb(&Vcb->FcbCount); - InsertTailList(list, &Fcb->Next); - if (++count >= Ext2Global->MaxDepth) { - break; - } - } - - if (start.QuadPart + 10*1000*1000 > now.QuadPart) { - retry = FALSE; - } - - if (retry) { - if (++tries < (Vcb->FcbCount >> 4) ) - goto again; - } - - ExReleaseResourceLite(&Vcb->FcbLock); - - return 0; -} - -/* Reaper thread to release Fcb */ -VOID -Ext2FcbReaperThread( - PVOID Context -) -{ - PEXT2_REAPER Reaper = Context; - PEXT2_VCB Vcb = NULL; - LIST_ENTRY List, *Link; - LARGE_INTEGER Timeout; - - BOOLEAN GlobalAcquired = FALSE; - BOOLEAN DidNothing = FALSE; - BOOLEAN NonWait = FALSE; - - __try { - - Reaper->Thread = PsGetCurrentThread(); - - /* wake up DirverEntry */ - KeSetEvent(&Reaper->Engine, 0, FALSE); - - /* now process looping */ - while (!IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) { - - /* wait until it is waken or it times out */ - if (NonWait) { - Timeout.QuadPart = (LONGLONG)-10*1000*100; - NonWait = FALSE; - } else if (DidNothing) { - Timeout.QuadPart = Timeout.QuadPart * 2; - } else { - Timeout.QuadPart = (LONGLONG)-10*1000*1000*20; /* 20 seconds */ - } - KeWaitForSingleObject( - &Reaper->Wait, - Executive, - KernelMode, - FALSE, - &Timeout - ); - - if (IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) - break; - - InitializeListHead(&List); - - /* acquire global exclusive lock */ - ExAcquireResourceSharedLite(&Ext2Global->Resource, TRUE); - GlobalAcquired = TRUE; - /* search all Vcb to get unused resources freed to system */ - for (Link = Ext2Global->VcbList.Flink; - Link != &(Ext2Global->VcbList); - Link = Link->Flink ) { - - Vcb = CONTAINING_RECORD(Link, EXT2_VCB, Next); - NonWait = Ext2QueryUnusedFcb(Vcb, &List); - } - DidNothing = IsListEmpty(&List); - if (DidNothing) { - KeClearEvent(&Reaper->Wait); - } - if (GlobalAcquired) { - ExReleaseResourceLite(&Ext2Global->Resource); - GlobalAcquired = FALSE; - } - - while (!IsListEmpty(&List)) { - PEXT2_FCB Fcb; - Link = RemoveHeadList(&List); - Fcb = CONTAINING_RECORD(Link, EXT2_FCB, Next); - ASSERT(0 == Fcb->ReferenceCount); - Ext2FreeFcb(Fcb); - } - } - - } __finally { - - if (GlobalAcquired) { - ExReleaseResourceLite(&Ext2Global->Resource); - } - - KeSetEvent(&Reaper->Engine, 0, FALSE); - } - - PsTerminateSystemThread(STATUS_SUCCESS); -} - -NTSTATUS -Ext2StartReaper(PEXT2_REAPER Reaper, EXT2_REAPER_RELEASE Free) -{ - NTSTATUS status = STATUS_SUCCESS; - OBJECT_ATTRIBUTES oa; - HANDLE handle = 0; - LARGE_INTEGER timeout; - - Reaper->Free = Free; - - /* initialize wait event */ - KeInitializeEvent( - &Reaper->Wait, - SynchronizationEvent, FALSE - ); - - /* Reaper thread engine event */ - KeInitializeEvent( - &Reaper->Engine, - SynchronizationEvent, FALSE - ); - - /* initialize oa */ - InitializeObjectAttributes( - &oa, - NULL, - OBJ_CASE_INSENSITIVE | - OBJ_KERNEL_HANDLE, - NULL, - NULL - ); - - /* start a new system thread */ - status = PsCreateSystemThread( - &handle, - 0, - &oa, - NULL, - NULL, - Free, - (PVOID)Reaper - ); - - if (NT_SUCCESS(status)) { - ZwClose(handle); - - /* make sure Reaperthread is started */ - timeout.QuadPart = (LONGLONG)-10*1000*1000*2; /* 2 seconds */ - status = KeWaitForSingleObject( - &Reaper->Engine, - Executive, - KernelMode, - FALSE, - &timeout - ); - if (status != STATUS_SUCCESS) { - status = STATUS_INSUFFICIENT_RESOURCES; - } - } - - return status; -} - - -VOID -Ext2StopReaper(PEXT2_REAPER Reaper) -{ - LARGE_INTEGER timeout; - - Reaper->Flags |= EXT2_REAPER_FLAG_STOP; - KeSetEvent(&Reaper->Wait, 0, FALSE); - - /* make sure Reaperthread is started */ - timeout.QuadPart = (LONGLONG)-10*1000*1000*2; /* 2 seconds */ - KeWaitForSingleObject( - &Reaper->Engine, - Executive, - KernelMode, - FALSE, - &timeout); -} +/* + * COPYRIGHT: See COPYRIGHT.TXT + * PROJECT: Ext2 File System Driver for WinNT/2K/XP + * FILE: memory.c + * PROGRAMMER: Matt Wu + * HOMEPAGE: http://www.ext2fsd.com + * UPDATE HISTORY: + */ + +/* INCLUDES *****************************************************************/ + +#include "ext2fs.h" + +/* GLOBALS ***************************************************************/ + +extern PEXT2_GLOBAL Ext2Global; + +/* DEFINITIONS *************************************************************/ + +#ifdef ALLOC_PRAGMA +#pragma alloc_text(PAGE, Ext2AllocateInode) +#pragma alloc_text(PAGE, Ext2DestroyInode) +#pragma alloc_text(PAGE, Ext2CheckBitmapConsistency) +#pragma alloc_text(PAGE, Ext2CheckSetBlock) +#pragma alloc_text(PAGE, Ext2InitializeVcb) +#pragma alloc_text(PAGE, Ext2TearDownStream) +#pragma alloc_text(PAGE, Ext2DestroyVcb) +#pragma alloc_text(PAGE, Ext2SyncUninitializeCacheMap) +#pragma alloc_text(PAGE, Ext2ReaperThread) +#pragma alloc_text(PAGE, Ext2StartReaper) +#pragma alloc_text(PAGE, Ext2StopReaper) +#endif + +PEXT2_IRP_CONTEXT +Ext2AllocateIrpContext (IN PDEVICE_OBJECT DeviceObject, + IN PIRP Irp ) +{ + PIO_STACK_LOCATION irpSp; + PEXT2_IRP_CONTEXT IrpContext; + + ASSERT(DeviceObject != NULL); + ASSERT(Irp != NULL); + + irpSp = IoGetCurrentIrpStackLocation(Irp); + + IrpContext = (PEXT2_IRP_CONTEXT) ( + ExAllocateFromNPagedLookasideList( + &(Ext2Global->Ext2IrpContextLookasideList))); + + if (IrpContext == NULL) { + return NULL; + } + + RtlZeroMemory(IrpContext, sizeof(EXT2_IRP_CONTEXT) ); + + IrpContext->Identifier.Type = EXT2ICX; + IrpContext->Identifier.Size = sizeof(EXT2_IRP_CONTEXT); + + IrpContext->Irp = Irp; + IrpContext->MajorFunction = irpSp->MajorFunction; + IrpContext->MinorFunction = irpSp->MinorFunction; + IrpContext->DeviceObject = DeviceObject; + IrpContext->FileObject = irpSp->FileObject; + if (NULL != IrpContext->FileObject) { + IrpContext->Fcb = (PEXT2_FCB)IrpContext->FileObject->FsContext; + IrpContext->Ccb = (PEXT2_CCB)IrpContext->FileObject->FsContext2; + } + + if (IrpContext->FileObject != NULL) { + IrpContext->RealDevice = IrpContext->FileObject->DeviceObject; + } else if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL) { + if (irpSp->Parameters.MountVolume.Vpb) { + IrpContext->RealDevice = irpSp->Parameters.MountVolume.Vpb->RealDevice; + } + } + + if (IsFlagOn(irpSp->Flags, SL_WRITE_THROUGH)) { + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WRITE_THROUGH); + } + + if (IsFlagOn(irpSp->Flags, SL_OVERRIDE_VERIFY_VOLUME)) { + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_VERIFY_READ); + } + + if (IrpContext->MajorFunction == IRP_MJ_CLEANUP || + IrpContext->MajorFunction == IRP_MJ_CLOSE || + IrpContext->MajorFunction == IRP_MJ_SHUTDOWN || + IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL || + IrpContext->MajorFunction == IRP_MJ_PNP ) { + + if (IrpContext->MajorFunction == IRP_MJ_FILE_SYSTEM_CONTROL || + IrpContext->MajorFunction == IRP_MJ_PNP) { + if (IoGetCurrentIrpStackLocation(Irp)->FileObject == NULL || + IoIsOperationSynchronous(Irp)) { + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + } else { + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + + } else if (IoIsOperationSynchronous(Irp)) { + + SetFlag(IrpContext->Flags, IRP_CONTEXT_FLAG_WAIT); + } + + IrpContext->IsTopLevel = (IoGetTopLevelIrp() == Irp); + IrpContext->ExceptionInProgress = FALSE; + INC_IRP_COUNT(IrpContext); + + return IrpContext; +} + +VOID +Ext2FreeIrpContext (IN PEXT2_IRP_CONTEXT IrpContext) +{ + ASSERT(IrpContext != NULL); + + ASSERT((IrpContext->Identifier.Type == EXT2ICX) && + (IrpContext->Identifier.Size == sizeof(EXT2_IRP_CONTEXT))); + + /* free the IrpContext to NonPagedList */ + IrpContext->Identifier.Type = 0; + IrpContext->Identifier.Size = 0; + + DEC_IRP_COUNT(IrpContext); + ExFreeToNPagedLookasideList(&(Ext2Global->Ext2IrpContextLookasideList), IrpContext); +} + + +PEXT2_FCB +Ext2AllocateFcb ( + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb +) +{ + PEXT2_FCB Fcb; + + ASSERT(ExIsResourceAcquiredExclusiveLite(&Vcb->FcbLock)); + + Fcb = (PEXT2_FCB) ExAllocateFromNPagedLookasideList( + &(Ext2Global->Ext2FcbLookasideList)); + + if (!Fcb) { + return NULL; + } + + RtlZeroMemory(Fcb, sizeof(EXT2_FCB)); + Fcb->Identifier.Type = EXT2FCB; + Fcb->Identifier.Size = sizeof(EXT2_FCB); + +#ifndef _WIN2K_TARGET_ + ExInitializeFastMutex(&Fcb->Mutex); + FsRtlSetupAdvancedHeader(&Fcb->Header, &Fcb->Mutex); +#endif + + FsRtlInitializeOplock(&Fcb->Oplock); + FsRtlInitializeFileLock ( + &Fcb->FileLockAnchor, + NULL, + NULL ); + + Fcb->OpenHandleCount = 0; + Fcb->ReferenceCount = 0; + Fcb->Vcb = Vcb; + Fcb->Inode = &Mcb->Inode; + + ASSERT(Mcb->Fcb == NULL); + Ext2ReferMcb(Mcb); + Fcb->Mcb = Mcb; + Mcb->Fcb = Fcb; + + DEBUG(DL_RES, ("Ext2AllocateFcb: Fcb %p created: %wZ.\n", + Fcb, &Fcb->Mcb->FullName)); + + RtlZeroMemory(&Fcb->Header, sizeof(FSRTL_COMMON_FCB_HEADER)); + Fcb->Header.NodeTypeCode = (USHORT) EXT2FCB; + Fcb->Header.NodeByteSize = sizeof(EXT2_FCB); + Fcb->Header.IsFastIoPossible = FastIoIsNotPossible; + Fcb->Header.Resource = &(Fcb->MainResource); + Fcb->Header.PagingIoResource = &(Fcb->PagingIoResource); + + Fcb->Header.FileSize.QuadPart = Mcb->Inode.i_size; + Fcb->Header.ValidDataLength.QuadPart = Mcb->Inode.i_size; + Fcb->Header.AllocationSize.QuadPart = CEILING_ALIGNED(ULONGLONG, + Fcb->Header.FileSize.QuadPart, (ULONGLONG)Vcb->BlockSize); + + Fcb->SectionObject.DataSectionObject = NULL; + Fcb->SectionObject.SharedCacheMap = NULL; + Fcb->SectionObject.ImageSectionObject = NULL; + + ExInitializeResourceLite(&(Fcb->MainResource)); + ExInitializeResourceLite(&(Fcb->PagingIoResource)); + + Ext2InsertFcb(Vcb, Fcb); + + INC_MEM_COUNT(PS_FCB, Fcb, sizeof(EXT2_FCB)); + + return Fcb; +} + +VOID +Ext2UnlinkFcb(IN PEXT2_FCB Fcb) +{ + PEXT2_VCB Vcb = Fcb->Vcb; + PEXT2_MCB Mcb; + + ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); + Mcb = Fcb->Mcb; + + DEBUG(DL_INF, ("Ext2FreeFcb: Fcb (%p) to be unlinked: %wZ.\n", + Fcb, Mcb ? &Mcb->FullName : NULL)); + + if ((Mcb != NULL) && + (Mcb->Identifier.Type == EXT2MCB) && + (Mcb->Identifier.Size == sizeof(EXT2_MCB))) { + + ASSERT (Mcb->Fcb == Fcb); + if (IsMcbSpecialFile(Mcb) || + IsFileDeleted(Mcb)) { + + ASSERT(!IsRoot(Fcb)); + Ext2RemoveMcb(Vcb, Mcb); + Mcb->Fcb = NULL; + + Ext2UnlinkMcb(Vcb, Mcb); + Ext2DerefMcb(Mcb); + Ext2LinkHeadMcb(Vcb, Mcb); + + } else { + Mcb->Fcb = NULL; + Ext2DerefMcb(Mcb); + } + Fcb->Mcb = NULL; + } + + ExReleaseResourceLite(&Vcb->McbLock); +} + +VOID +Ext2FreeFcb (IN PEXT2_FCB Fcb) +{ + PEXT2_VCB Vcb = Fcb->Vcb; + + __try { + + ASSERT((Fcb != NULL) && (Fcb->Identifier.Type == EXT2FCB) && + (Fcb->Identifier.Size == sizeof(EXT2_FCB))); + ASSERT(0 == Fcb->ReferenceCount); + +#ifndef _WIN2K_TARGET_ + FsRtlTeardownPerStreamContexts(&Fcb->Header); +#endif + + FsRtlUninitializeFileLock(&Fcb->FileLockAnchor); + FsRtlUninitializeOplock(&Fcb->Oplock); + ExDeleteResourceLite(&Fcb->MainResource); + ExDeleteResourceLite(&Fcb->PagingIoResource); + + Fcb->Identifier.Type = 0; + Fcb->Identifier.Size = 0; + + ExFreeToNPagedLookasideList(&(Ext2Global->Ext2FcbLookasideList), Fcb); + DEC_MEM_COUNT(PS_FCB, Fcb, sizeof(EXT2_FCB)); + + if (0 == Ext2DerefXcb(&Vcb->ReferenceCount)) { + if (!IsMounted(Vcb) || IsDispending(Vcb)) { + Ext2CheckDismount(NULL, Vcb, FALSE); + } + } + + } __finally { + } +} + +VOID +Ext2ReleaseFcb (IN PEXT2_FCB Fcb) +{ + PEXT2_VCB Vcb = Fcb->Vcb; + PEXT2_MCB Mcb; + + if (0 != Ext2DerefXcb(&Fcb->ReferenceCount)) + return; + + ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); + ExAcquireResourceExclusiveLite(&Fcb->MainResource, TRUE); + + Mcb = Fcb->Mcb; + RemoveEntryList(&Fcb->Next); + + if (IsFlagOn(Fcb->Flags, FCB_DELETE_PENDING) || + NULL == Mcb || IsFileDeleted(Mcb)) { + InsertHeadList(&Vcb->FcbList, &Fcb->Next); + Fcb->TsDrop.QuadPart = 0; + } else { + InsertTailList(&Vcb->FcbList, &Fcb->Next); + KeQuerySystemTime(&Fcb->TsDrop); + } + ExReleaseResourceLite(&Fcb->MainResource); + ExReleaseResourceLite(&Vcb->FcbLock); + + if ((Vcb->FcbCount >> 6) > (ULONG)(Ext2Global->MaxDepth)) { + KeSetEvent(&Ext2Global->FcbReaper.Wait, 0, FALSE); + } +} + +/* Insert Fcb to Vcb->FcbList queue, with Vcb->FcbLock Acquired. */ + +VOID +Ext2InsertFcb(PEXT2_VCB Vcb, PEXT2_FCB Fcb) +{ + ASSERT(ExIsResourceAcquiredExclusiveLite(&Vcb->FcbLock)); + + KeQuerySystemTime(&Fcb->TsDrop); + Ext2ReferXcb(&Vcb->FcbCount); + Ext2ReferXcb(&Vcb->ReferenceCount); + InsertTailList(&Vcb->FcbList, &Fcb->Next); +} + +PEXT2_CCB +Ext2AllocateCcb (ULONG Flags, PEXT2_MCB SymLink) +{ + PEXT2_CCB Ccb; + + Ccb = (PEXT2_CCB) (ExAllocateFromNPagedLookasideList( + &(Ext2Global->Ext2CcbLookasideList))); + if (!Ccb) { + return NULL; + } + + DEBUG(DL_RES, ( "ExtAllocateCcb: Ccb created: %ph.\n", Ccb)); + + RtlZeroMemory(Ccb, sizeof(EXT2_CCB)); + + Ccb->Identifier.Type = EXT2CCB; + Ccb->Identifier.Size = sizeof(EXT2_CCB); + Ccb->Flags = Flags; + + Ccb->SymLink = SymLink; + if (SymLink) { + ASSERT(SymLink->Refercount > 0); + Ext2ReferMcb(SymLink); + DEBUG(DL_INF, ( "ExtAllocateCcb: Ccb SymLink: %wZ.\n", + &Ccb->SymLink->FullName)); + } + + Ccb->DirectorySearchPattern.Length = 0; + Ccb->DirectorySearchPattern.MaximumLength = 0; + Ccb->DirectorySearchPattern.Buffer = 0; + + INC_MEM_COUNT(PS_CCB, Ccb, sizeof(EXT2_CCB)); + + return Ccb; +} + +VOID +Ext2FreeCcb (IN PEXT2_VCB Vcb, IN PEXT2_CCB Ccb) +{ + ASSERT(Ccb != NULL); + ASSERT((Ccb->Identifier.Type == EXT2CCB) && + (Ccb->Identifier.Size == sizeof(EXT2_CCB))); + + DEBUG(DL_RES, ( "Ext2FreeCcb: Ccb = %ph.\n", Ccb)); + + if (Ccb->SymLink) { + DEBUG(DL_INF, ( "Ext2FreeCcb: Ccb SymLink: %wZ.\n", + &Ccb->SymLink->FullName)); + if (IsFileDeleted(Ccb->SymLink->Target)) { + Ext2UnlinkMcb(Vcb, Ccb->SymLink); + Ext2DerefMcb(Ccb->SymLink); + Ext2LinkHeadMcb(Vcb, Ccb->SymLink); + } else { + Ext2DerefMcb(Ccb->SymLink); + } + } + + if (Ccb->DirectorySearchPattern.Buffer != NULL) { + DEC_MEM_COUNT(PS_DIR_PATTERN, Ccb->DirectorySearchPattern.Buffer, + Ccb->DirectorySearchPattern.MaximumLength ); + Ext2FreePool(Ccb->DirectorySearchPattern.Buffer, EXT2_DIRSP_MAGIC); + } + + ExFreeToNPagedLookasideList(&(Ext2Global->Ext2CcbLookasideList), Ccb); + DEC_MEM_COUNT(PS_CCB, Ccb, sizeof(EXT2_CCB)); +} + +PEXT2_INODE +Ext2AllocateInode (PEXT2_VCB Vcb) +{ + PVOID inode = NULL; + + inode = ExAllocateFromNPagedLookasideList( + &(Vcb->InodeLookasideList)); + if (!inode) { + return NULL; + } + + RtlZeroMemory(inode, INODE_SIZE); + + DEBUG(DL_INF, ("ExtAllocateInode: Inode created: %ph.\n", inode)); + INC_MEM_COUNT(PS_EXT2_INODE, inode, INODE_SIZE); + + return inode; +} + +VOID +Ext2DestroyInode (IN PEXT2_VCB Vcb, IN PEXT2_INODE inode) +{ + ASSERT(inode != NULL); + + DEBUG(DL_INF, ("Ext2FreeInode: Inode = %ph.\n", inode)); + + ExFreeToNPagedLookasideList(&(Vcb->InodeLookasideList), inode); + DEC_MEM_COUNT(PS_EXT2_INODE, inode, INODE_SIZE); +} + +struct dentry * Ext2AllocateEntry() +{ + struct dentry *de; + + de = (struct dentry *)ExAllocateFromNPagedLookasideList( + &(Ext2Global->Ext2DentryLookasideList)); + if (!de) { + return NULL; + } + + RtlZeroMemory(de, sizeof(struct dentry)); + INC_MEM_COUNT(PS_DENTRY, de, sizeof(struct dentry)); + + return de; +} + +VOID Ext2FreeEntry (IN struct dentry *de) +{ + ASSERT(de != NULL); + + if (de->d_name.name) + ExFreePool(de->d_name.name); + + ExFreeToNPagedLookasideList(&(Ext2Global->Ext2DentryLookasideList), de); + DEC_MEM_COUNT(PS_DENTRY, de, sizeof(struct dentry)); +} + + +struct dentry *Ext2BuildEntry(PEXT2_VCB Vcb, PEXT2_MCB Dcb, PUNICODE_STRING FileName) +{ + OEM_STRING Oem = { 0 }; + struct dentry *de = NULL; + NTSTATUS Status = STATUS_INSUFFICIENT_RESOURCES; + + __try { + + de = Ext2AllocateEntry(); + if (!de) { + DEBUG(DL_ERR, ("Ext2BuildEntry: failed to allocate dentry.\n")); + __leave; + } + de->d_sb = &Vcb->sb; + if (Dcb) + de->d_parent = Dcb->de; + + Oem.MaximumLength = (USHORT)Ext2UnicodeToOEMSize(Vcb, FileName) + 1; + Oem.Buffer = ExAllocatePool(PagedPool, Oem.MaximumLength); + if (!Oem.Buffer) { + DEBUG(DL_ERR, ( "Ex2BuildEntry: failed to allocate OEM name.\n")); + __leave; + } + de->d_name.name = Oem.Buffer; + RtlZeroMemory(Oem.Buffer, Oem.MaximumLength); + Status = Ext2UnicodeToOEM(Vcb, &Oem, FileName); + if (!NT_SUCCESS(Status)) { + DEBUG(DL_CP, ("Ext2BuildEntry: failed to convert %S to OEM.\n", FileName->Buffer)); + __leave; + } + de->d_name.len = Oem.Length; + + } __finally { + + if (!NT_SUCCESS(Status)) { + if (de) + Ext2FreeEntry(de); + } + } + + return de; +} + +PEXT2_EXTENT +Ext2AllocateExtent () +{ + PEXT2_EXTENT Extent; + + Extent = (PEXT2_EXTENT)ExAllocateFromNPagedLookasideList( + &(Ext2Global->Ext2ExtLookasideList)); + if (!Extent) { + return NULL; + } + + RtlZeroMemory(Extent, sizeof(EXT2_EXTENT)); + INC_MEM_COUNT(PS_EXTENT, Extent, sizeof(EXT2_EXTENT)); + + return Extent; +} + +VOID +Ext2FreeExtent (IN PEXT2_EXTENT Extent) +{ + ASSERT(Extent != NULL); + ExFreeToNPagedLookasideList(&(Ext2Global->Ext2ExtLookasideList), Extent); + DEC_MEM_COUNT(PS_EXTENT, Extent, sizeof(EXT2_EXTENT)); +} + +ULONG +Ext2CountExtents(IN PEXT2_EXTENT Chain) +{ + ULONG count = 0; + PEXT2_EXTENT List = Chain; + + while (List) { + count += 1; + List = List->Next; + } + + return count; +} + +VOID +Ext2JointExtents( + IN PEXT2_EXTENT Chain, + IN PEXT2_EXTENT Extent +) +{ + ULONG count = 0; + PEXT2_EXTENT List = Chain; + + while (List->Next) { + List = List->Next; + } + + List->Next = Extent; +} + + +VOID +Ext2DestroyExtentChain(IN PEXT2_EXTENT Chain) +{ + PEXT2_EXTENT Extent = NULL, List = Chain; + + while (List) { + Extent = List->Next; + Ext2FreeExtent(List); + List = Extent; + } +} + +BOOLEAN +Ext2ListExtents(PLARGE_MCB Extents) +{ + if (FsRtlNumberOfRunsInLargeMcb(Extents) != 0) { + + LONGLONG DirtyVba; + LONGLONG DirtyLba; + LONGLONG DirtyLength; + int i, n = 0; + + for (i = 0; FsRtlGetNextLargeMcbEntry( + Extents, i, &DirtyVba, + &DirtyLba, &DirtyLength); i++) { + if (DirtyVba > 0 && DirtyLba != -1) { + DEBUG(DL_EXT, ("Vba:%I64xh Lba:%I64xh Len:%I64xh.\n", DirtyVba, DirtyLba, DirtyLength)); + n++; + } + } + + return n ? TRUE : FALSE; + } + + return FALSE; +} + +VOID +Ext2CheckExtent( + PLARGE_MCB Zone, + LONGLONG Vbn, + LONGLONG Lbn, + LONGLONG Length, + BOOLEAN bAdded +) +{ +#if EXT2_DEBUG + LONGLONG DirtyLbn; + LONGLONG DirtyLen; + LONGLONG RunStart; + LONGLONG RunLength; + ULONG Index; + BOOLEAN bFound = FALSE; + + bFound = FsRtlLookupLargeMcbEntry( + Zone, + Vbn, + &DirtyLbn, + &DirtyLen, + &RunStart, + &RunLength, + &Index ); + + if (!bAdded && (!bFound || DirtyLbn == -1)) { + return; + } + + if ( !bFound || (DirtyLbn == -1) || + (DirtyLbn != Lbn) || + (DirtyLen < Length)) { + + DbgBreak(); + + for (Index = 0; TRUE; Index++) { + + if (!FsRtlGetNextLargeMcbEntry( + Zone, + Index, + &Vbn, + &Lbn, + &Length)) { + break; + } + + DEBUG(DL_EXT, ("Index = %xh Vbn = %I64xh Lbn = %I64xh Len = %I64xh\n", + Index, Vbn, Lbn, Length )); + } + } +#endif +} + +VOID +Ext2ClearAllExtents(PLARGE_MCB Zone) +{ + __try { + FsRtlTruncateLargeMcb(Zone, (LONGLONG)0); + } __except (EXCEPTION_EXECUTE_HANDLER) { + DbgBreak(); + } +} + + +BOOLEAN +Ext2AddVcbExtent ( + IN PEXT2_VCB Vcb, + IN LONGLONG Vbn, + IN LONGLONG Length +) +{ + ULONG TriedTimes = 0; + + LONGLONG Offset = 0; + BOOLEAN rc = FALSE; + + Offset = Vbn & (~(Vcb->IoUnitSize - 1)); + Length = (Vbn - Offset + Length + Vcb->IoUnitSize - 1) & + ~(Vcb->IoUnitSize - 1); + + ASSERT ((Offset & (Vcb->IoUnitSize - 1)) == 0); + ASSERT ((Length & (Vcb->IoUnitSize - 1)) == 0); + + Offset = (Offset >> Vcb->IoUnitBits) + 1; + Length = (Length >> Vcb->IoUnitBits); + +Again: + + __try { + rc = FsRtlAddLargeMcbEntry( + &Vcb->Extents, + Offset, + Offset, + Length + ); + } __except (EXCEPTION_EXECUTE_HANDLER) { + DbgBreak(); + rc = FALSE; + } + + if (!rc && ++TriedTimes < 10) { + Ext2Sleep(TriedTimes * 100); + goto Again; + } + + DEBUG(DL_EXT, ("Ext2AddVcbExtent: Vbn=%I64xh Length=%I64xh," + " rc=%d Runs=%u\n", Offset, Length, rc, + FsRtlNumberOfRunsInLargeMcb(&Vcb->Extents))); + + if (rc) { + Ext2CheckExtent(&Vcb->Extents, Offset, Offset, Length, TRUE); + } + + return rc; +} + +BOOLEAN +Ext2RemoveVcbExtent ( + IN PEXT2_VCB Vcb, + IN LONGLONG Vbn, + IN LONGLONG Length +) +{ + ULONG TriedTimes = 0; + LONGLONG Offset = 0; + BOOLEAN rc = TRUE; + + Offset = Vbn & (~(Vcb->IoUnitSize - 1)); + Length = (Length + Vbn - Offset + Vcb->IoUnitSize - 1) & (~(Vcb->IoUnitSize - 1)); + + ASSERT ((Offset & (Vcb->IoUnitSize - 1)) == 0); + ASSERT ((Length & (Vcb->IoUnitSize - 1)) == 0); + + Offset = (Offset >> Vcb->IoUnitBits) + 1; + Length = (Length >> Vcb->IoUnitBits); + +Again: + + __try { + FsRtlRemoveLargeMcbEntry( + &Vcb->Extents, + Offset, + Length + ); + } __except (EXCEPTION_EXECUTE_HANDLER) { + DbgBreak(); + rc = FALSE; + } + + if (!rc && ++TriedTimes < 10) { + Ext2Sleep(TriedTimes * 100); + goto Again; + } + + DEBUG(DL_EXT, ("Ext2RemoveVcbExtent: Vbn=%I64xh Length=%I64xh Runs=%u\n", + Offset, Length, FsRtlNumberOfRunsInLargeMcb(&Vcb->Extents))); + if (rc) { + Ext2CheckExtent(&Vcb->Extents, Offset, 0, Length, FALSE); + } + + return rc; +} + +BOOLEAN +Ext2LookupVcbExtent ( + IN PEXT2_VCB Vcb, + IN LONGLONG Vbn, + OUT PLONGLONG Lbn, + OUT PLONGLONG Length +) +{ + LONGLONG offset; + BOOLEAN rc; + + offset = Vbn & (~(Vcb->IoUnitSize - 1)); + ASSERT ((offset & (Vcb->IoUnitSize - 1)) == 0); + offset = (offset >> Vcb->IoUnitBits) + 1; + + rc = FsRtlLookupLargeMcbEntry( + &(Vcb->Extents), + offset, + Lbn, + Length, + NULL, + NULL, + NULL + ); + + if (rc) { + + if (Lbn && ((*Lbn) != -1)) { + ASSERT((*Lbn) > 0); + (*Lbn) = (((*Lbn) - 1) << Vcb->IoUnitBits); + (*Lbn) += ((Vbn) & (Vcb->IoUnitSize - 1)); + } + + if (Length && *Length) { + (*Length) <<= Vcb->IoUnitBits; + (*Length) -= ((Vbn) & (Vcb->IoUnitSize - 1)); + } + } + + return rc; +} + + +BOOLEAN +Ext2AddMcbExtent ( + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN LONGLONG Vbn, + IN LONGLONG Lbn, + IN LONGLONG Length +) +{ + ULONG TriedTimes = 0; + LONGLONG Base = 0; + UCHAR Bits = 0; + BOOLEAN rc = FALSE; + + Base = (LONGLONG)BLOCK_SIZE; + Bits = (UCHAR)BLOCK_BITS; + + ASSERT ((Vbn & (Base - 1)) == 0); + ASSERT ((Lbn & (Base - 1)) == 0); + ASSERT ((Length & (Base - 1)) == 0); + + Vbn = (Vbn >> Bits) + 1; + Lbn = (Lbn >> Bits) + 1; + Length = (Length >> Bits); + +Again: + + __try { + + rc = FsRtlAddLargeMcbEntry( + &Mcb->Extents, + Vbn, + Lbn, + Length + ); + + } __except (EXCEPTION_EXECUTE_HANDLER) { + + DbgBreak(); + rc = FALSE; + } + + if (!rc && ++TriedTimes < 10) { + Ext2Sleep(TriedTimes * 100); + goto Again; + } + + DEBUG(DL_EXT, ("Ext2AddMcbExtent: Vbn=%I64xh Lbn=%I64xh Length=%I64xh," + " rc=%d Runs=%u\n", Vbn, Lbn, Length, rc, + FsRtlNumberOfRunsInLargeMcb(&Mcb->Extents))); + + if (rc) { + Ext2CheckExtent(&Mcb->Extents, Vbn, Lbn, Length, TRUE); + } + + return rc; +} + +BOOLEAN +Ext2RemoveMcbExtent ( + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN LONGLONG Vbn, + IN LONGLONG Length +) +{ + ULONG TriedTimes = 0; + LONGLONG Base = 0; + UCHAR Bits = 0; + BOOLEAN rc = TRUE; + + Base = (LONGLONG)BLOCK_SIZE; + Bits = (UCHAR)BLOCK_BITS; + + ASSERT ((Vbn & (Base - 1)) == 0); + ASSERT ((Length & (Base - 1)) == 0); + + Vbn = (Vbn >> Bits) + 1; + Length = (Length >> Bits); + +Again: + + __try { + FsRtlRemoveLargeMcbEntry( + &Mcb->Extents, + Vbn, + Length + ); + } __except (EXCEPTION_EXECUTE_HANDLER) { + DbgBreak(); + rc = FALSE; + } + + if (!rc && ++TriedTimes < 10) { + Ext2Sleep(TriedTimes * 100); + goto Again; + } + + DEBUG(DL_EXT, ("Ext2RemoveMcbExtent: Vbn=%I64xh Length=%I64xh Runs=%u\n", + Vbn, Length, FsRtlNumberOfRunsInLargeMcb(&Mcb->Extents))); + if (rc) { + Ext2CheckExtent(&Mcb->Extents, Vbn, 0, Length, FALSE); + } + + return rc; +} + +BOOLEAN +Ext2LookupMcbExtent ( + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN LONGLONG Vbn, + OUT PLONGLONG Lbn, + OUT PLONGLONG Length +) +{ + LONGLONG offset; + BOOLEAN rc; + + offset = Vbn & (~((LONGLONG)BLOCK_SIZE - 1)); + ASSERT ((offset & (BLOCK_SIZE - 1)) == 0); + offset = (offset >> BLOCK_BITS) + 1; + + rc = FsRtlLookupLargeMcbEntry( + &(Mcb->Extents), + offset, + Lbn, + Length, + NULL, + NULL, + NULL + ); + + if (rc) { + + if (Lbn && ((*Lbn) != -1)) { + ASSERT((*Lbn) > 0); + (*Lbn) = (((*Lbn) - 1) << BLOCK_BITS); + (*Lbn) += ((Vbn) & ((LONGLONG)BLOCK_SIZE - 1)); + } + + if (Length && *Length) { + (*Length) <<= BLOCK_BITS; + (*Length) -= ((Vbn) & ((LONGLONG)BLOCK_SIZE - 1)); + } + } + + return rc; +} + + +BOOLEAN +Ext2AddMcbMetaExts ( + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN ULONG Block, + IN ULONG Length +) +{ + ULONG TriedTimes = 0; + LONGLONG Lbn = Block + 1; + BOOLEAN rc = TRUE; + +Again: + + __try { + + rc = FsRtlAddLargeMcbEntry( + &Mcb->MetaExts, + Lbn, + Lbn, + Length + ); + + } __except (EXCEPTION_EXECUTE_HANDLER) { + + DbgBreak(); + rc = FALSE; + } + + if (!rc && ++TriedTimes < 10) { + Ext2Sleep(TriedTimes * 100); + goto Again; + } + + DEBUG(DL_EXT, ("Ext2AddMcbMetaExts: Block: %xh-%xh rc=%d Runs=%u\n", Block, + Length, rc, FsRtlNumberOfRunsInLargeMcb(&Mcb->MetaExts))); + + if (rc) { + Ext2CheckExtent(&Mcb->MetaExts, Lbn, Lbn, Length, TRUE); + } + + return rc; +} + +BOOLEAN +Ext2RemoveMcbMetaExts ( + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN ULONG Block, + IN ULONG Length +) +{ + ULONG TriedTimes = 0; + LONGLONG Lbn = Block + 1; + BOOLEAN rc = TRUE; + +Again: + + __try { + + FsRtlRemoveLargeMcbEntry( + &Mcb->MetaExts, + Lbn, + Length + ); + + } __except (EXCEPTION_EXECUTE_HANDLER) { + DbgBreak(); + rc = FALSE; + } + + if (!rc && ++TriedTimes < 10) { + Ext2Sleep(TriedTimes * 100); + goto Again; + } + + DEBUG(DL_EXT, ("Ext2RemoveMcbMetaExts: Block: %xh-%xhh Runs=%u\n", Block, + Length, FsRtlNumberOfRunsInLargeMcb(&Mcb->MetaExts))); + if (rc) { + Ext2CheckExtent(&Mcb->MetaExts, Lbn, 0, Length, FALSE); + } + + return rc; +} + + +BOOLEAN +Ext2AddBlockExtent( + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN ULONG Start, + IN ULONG Block, + IN ULONG Number +) +{ + LONGLONG Vbn = 0; + LONGLONG Lbn = 0; + LONGLONG Length = 0; + + Vbn = ((LONGLONG) Start) << BLOCK_BITS; + Lbn = ((LONGLONG) Block) << BLOCK_BITS; + Length = ((LONGLONG)Number << BLOCK_BITS); + + if (Mcb) { +#if EXT2_DEBUG + ULONG _block = 0, _mapped = 0; + BOOLEAN _rc = Ext2LookupBlockExtent(Vcb, Mcb, Start, &_block, &_mapped); + if (_rc && _block != 0 && (_block != Block)) { + DbgBreak(); + } +#endif + return Ext2AddMcbExtent(Vcb, Mcb, Vbn, Lbn, Length); + + } + + ASSERT(Start == Block); + return Ext2AddVcbExtent(Vcb, Vbn, Length); +} + + +BOOLEAN +Ext2LookupBlockExtent( + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN ULONG Start, + IN PULONG Block, + IN PULONG Mapped +) +{ + LONGLONG Vbn = 0; + LONGLONG Lbn = 0; + LONGLONG Length = 0; + + BOOLEAN rc = FALSE; + + Vbn = ((LONGLONG) Start) << BLOCK_BITS; + + if (Mcb) { + rc = Ext2LookupMcbExtent(Vcb, Mcb, Vbn, &Lbn, &Length); + } else { + rc = Ext2LookupVcbExtent(Vcb, Vbn, &Lbn, &Length); + } + + if (rc) { + *Mapped = (ULONG)(Length >> BLOCK_BITS); + if (Lbn != -1 && Length > 0) { + *Block = (ULONG)(Lbn >> BLOCK_BITS); + } else { + *Block = 0; + } + } + + return rc; +} + + +BOOLEAN +Ext2RemoveBlockExtent( + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN ULONG Start, + IN ULONG Number +) +{ + LONGLONG Vbn = 0; + LONGLONG Length = 0; + BOOLEAN rc; + + Vbn = ((LONGLONG) Start) << BLOCK_BITS; + Length = ((LONGLONG)Number << BLOCK_BITS); + + if (Mcb) { + rc = Ext2RemoveMcbExtent(Vcb, Mcb, Vbn, Length); + } else { + rc = Ext2RemoveVcbExtent(Vcb, Vbn, Length); + } + + return rc; +} + +NTSTATUS +Ext2InitializeZone( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb +) +{ + NTSTATUS Status = STATUS_SUCCESS; + + ULONG Start = 0; + ULONG End; + ULONG Block; + ULONG Mapped; + + Ext2ClearAllExtents(&Mcb->Extents); + Ext2ClearAllExtents(&Mcb->MetaExts); + + ASSERT(Mcb != NULL); + End = (ULONG)((Mcb->Inode.i_size + BLOCK_SIZE - 1) >> BLOCK_BITS); + + while (Start < End) { + + Block = Mapped = 0; + + /* mapping file offset to ext2 block */ + if (INODE_HAS_EXTENT(&Mcb->Inode)) { + Status = Ext2MapExtent( + IrpContext, + Vcb, + Mcb, + Start, + FALSE, + &Block, + &Mapped + ); + } else { + Status = Ext2MapIndirect( + IrpContext, + Vcb, + Mcb, + Start, + FALSE, + &Block, + &Mapped + ); + } + + if (!NT_SUCCESS(Status)) { + goto errorout; + } + + /* skip wrong blocks, in case wrongly treating symlink + target names as blocks, silly */ + if (Block >= TOTAL_BLOCKS) { + Block = 0; + } + + if (Block) { + if (!Ext2AddBlockExtent(Vcb, Mcb, Start, Block, Mapped)) { + DbgBreak(); + ClearFlag(Mcb->Flags, MCB_ZONE_INITED); + Ext2ClearAllExtents(&Mcb->Extents); + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + DEBUG(DL_MAP, ("Ext2InitializeZone %wZ: Block = %xh Mapped = %xh\n", + &Mcb->FullName, Block, Mapped)); + } + + /* Mapped is total number of continous blocks or NULL blocks */ + Start += Mapped; + } + + /* set mcb zone as initialized */ + SetLongFlag(Mcb->Flags, MCB_ZONE_INITED); + +errorout: + + if (!IsZoneInited(Mcb)) { + Ext2ClearAllExtents(&Mcb->Extents); + Ext2ClearAllExtents(&Mcb->MetaExts); + } + + return Status; +} + +NTSTATUS +Ext2BuildExtents( + IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_MCB Mcb, + IN ULONGLONG Offset, + IN ULONG Size, + IN BOOLEAN bAlloc, + OUT PEXT2_EXTENT * Chain +) +{ + ULONG Start, End; + ULONG Total = 0; + + LONGLONG Lba = 0; + NTSTATUS Status = STATUS_SUCCESS; + + PEXT2_EXTENT Extent = NULL; + PEXT2_EXTENT List = *Chain = NULL; + + if (!IsZoneInited(Mcb)) { + Status = Ext2InitializeZone(IrpContext, Vcb, Mcb); + if (!NT_SUCCESS(Status)) { + DbgBreak(); + } + } + + if ((IrpContext && IrpContext->Irp) && + ((IrpContext->Irp->Flags & IRP_NOCACHE) || + (IrpContext->Irp->Flags & IRP_PAGING_IO))) { + Size = (Size + SECTOR_SIZE - 1) & (~(SECTOR_SIZE - 1)); + } + + Start = (ULONG)(Offset >> BLOCK_BITS); + End = (ULONG)((Size + Offset + BLOCK_SIZE - 1) >> BLOCK_BITS); + + if (End > (ULONG)((Mcb->Inode.i_size + BLOCK_SIZE - 1) >> BLOCK_BITS) ) { + End = (ULONG)((Mcb->Inode.i_size + BLOCK_SIZE - 1) >> BLOCK_BITS); + } + + while (Size > 0 && Start < End) { + + ULONG Mapped = 0; + ULONG Length = 0; + ULONG Block = 0; + + BOOLEAN rc = FALSE; + + /* try to map file offset to ext2 block upon Extents cache */ + if (IsZoneInited(Mcb)) { + rc = Ext2LookupBlockExtent( + Vcb, + Mcb, + Start, + &Block, + &Mapped); + + if (!rc) { + /* we likely get a sparse file here */ + Mapped = 1; + Block = 0; + } + } + + /* try to BlockMap in case failed to access Extents cache */ + if (!IsZoneInited(Mcb) || (bAlloc && Block == 0)) { + + Status = Ext2BlockMap( + IrpContext, + Vcb, + Mcb, + Start, + bAlloc, + &Block, + &Mapped + ); + if (!NT_SUCCESS(Status)) { + break; + } + + /* skip wrong blocks, in case wrongly treating symlink + target names as blocks, silly */ + if (Block >= TOTAL_BLOCKS) { + Block = 0; + } + + /* add new allocated blocks to Mcb zone */ + if (IsZoneInited(Mcb) && Block) { + if (!Ext2AddBlockExtent(Vcb, Mcb, Start, Block, Mapped)) { + DbgBreak(); + ClearFlag(Mcb->Flags, MCB_ZONE_INITED); + Ext2ClearAllExtents(&Mcb->Extents); + } + } + } + + /* calculate i/o extent */ + Lba = ((LONGLONG)Block << BLOCK_BITS) + Offset - ((LONGLONG)Start << BLOCK_BITS); + Length = (ULONG)(((LONGLONG)(Start + Mapped) << BLOCK_BITS) - Offset); + if (Length > Size) { + Length = Size; + } + + if (0 == Length) { + DbgBreak(); + break; + } + + Start += Mapped; + Offset = (ULONGLONG)Start << BLOCK_BITS; + + if (Block != 0) { + + if (List && List->Lba + List->Length == Lba) { + + /* it's continuous upon previous Extent */ + List->Length += Length; + + } else { + + /* have to allocate a new Extent */ + Extent = Ext2AllocateExtent(); + if (!Extent) { + Status = STATUS_INSUFFICIENT_RESOURCES; + DbgBreak(); + break; + } + + Extent->Lba = Lba; + Extent->Length = Length; + Extent->Offset = Total; + + /* insert new Extent to chain */ + if (List) { + List->Next = Extent; + List = Extent; + } else { + *Chain = List = Extent; + } + } + } else { + if (bAlloc) { + DbgBreak(); + } + } + + Total += Length; + Size -= Length; + } + + return Status; +} + + +BOOLEAN +Ext2BuildName( + IN OUT PUNICODE_STRING Target, + IN PUNICODE_STRING File, + IN PUNICODE_STRING Parent +) +{ + USHORT Length = 0; + USHORT ParentLen = 0; + BOOLEAN bBackslash = TRUE; + + /* free the original buffer */ + if (Target->Buffer) { + DEC_MEM_COUNT(PS_MCB_NAME, Target->Buffer, Target->MaximumLength); + Ext2FreePool(Target->Buffer, EXT2_FNAME_MAGIC); + Target->Length = Target->MaximumLength = 0; + } + + /* check the parent directory's name and backslash */ + if (Parent && Parent->Buffer && Parent->Length > 0) { + ParentLen = Parent->Length / sizeof(WCHAR); + if (Parent->Buffer[ParentLen - 1] == L'\\') { + bBackslash = FALSE; + } + } + + if (Parent == NULL || File->Buffer[0] == L'\\') { + /* must be root inode */ + ASSERT(ParentLen == 0); + bBackslash = FALSE; + } + + /* allocate and initialize new name buffer */ + Length = File->Length; + Length += (ParentLen + (bBackslash ? 1 : 0)) * sizeof(WCHAR); + + Target->Buffer = Ext2AllocatePool( + PagedPool, + Length + 2, + EXT2_FNAME_MAGIC + ); + + if (!Target->Buffer) { + DEBUG(DL_ERR, ( "Ex2BuildName: failed to allocate name bufer.\n")); + return FALSE; + } + RtlZeroMemory(Target->Buffer, Length + 2); + + if (ParentLen) { + RtlCopyMemory(&Target->Buffer[0], + Parent->Buffer, + ParentLen * sizeof(WCHAR)); + } + + if (bBackslash) { + Target->Buffer[ParentLen++] = L'\\'; + } + + RtlCopyMemory( &Target->Buffer[ParentLen], + File->Buffer, + File->Length); + + INC_MEM_COUNT(PS_MCB_NAME, Target->Buffer, Length + 2); + Target->Length = Length; + Target->MaximumLength = Length + 2; + + return TRUE; +} + +PEXT2_MCB +Ext2AllocateMcb ( + IN PEXT2_VCB Vcb, + IN PUNICODE_STRING FileName, + IN PUNICODE_STRING Parent, + IN ULONG FileAttr +) +{ + PEXT2_MCB Mcb = NULL; + NTSTATUS Status = STATUS_SUCCESS; + + /* need wake the reaper thread if there are many Mcb allocated */ + if (Ext2Global->PerfStat.Current.Mcb > (((ULONG)Ext2Global->MaxDepth) * 4)) { + KeSetEvent(&Ext2Global->McbReaper.Wait, 0, FALSE); + } + + /* allocate Mcb from LookasideList */ + Mcb = (PEXT2_MCB) (ExAllocateFromNPagedLookasideList( + &(Ext2Global->Ext2McbLookasideList))); + + if (Mcb == NULL) { + return NULL; + } + + /* initialize Mcb header */ + RtlZeroMemory(Mcb, sizeof(EXT2_MCB)); + Mcb->Identifier.Type = EXT2MCB; + Mcb->Identifier.Size = sizeof(EXT2_MCB); + Mcb->FileAttr = FileAttr; + + Mcb->Inode.i_priv = (PVOID)Mcb; + Mcb->Inode.i_sb = &Vcb->sb; + + /* initialize Mcb names */ + if (FileName) { + +#if EXT2_DEBUG + if ( FileName->Length == 2 && + FileName->Buffer[0] == L'\\') { + DEBUG(DL_RES, ( "Ext2AllocateMcb: Root Mcb is to be created !\n")); + } + + if ( FileName->Length == 2 && + FileName->Buffer[0] == L'.') { + DbgBreak(); + } + + if ( FileName->Length == 4 && + FileName->Buffer[0] == L'.' && + FileName->Buffer[1] == L'.' ) { + DbgBreak(); + } +#endif + + if (( FileName->Length >= 4 && FileName->Buffer[0] == L'.') && + ((FileName->Length == 4 && FileName->Buffer[1] != L'.') || + FileName->Length >= 6 )) { + SetFlag(Mcb->FileAttr, FILE_ATTRIBUTE_HIDDEN); + } + + if (!Ext2BuildName(&Mcb->ShortName, FileName, NULL)) { + goto errorout; + } + if (!Ext2BuildName(&Mcb->FullName, FileName, Parent)) { + goto errorout; + } + } + + /* initialize Mcb Extents, it will raise an expcetion if failed */ + __try { + FsRtlInitializeLargeMcb(&(Mcb->Extents), NonPagedPool); + FsRtlInitializeLargeMcb(&(Mcb->MetaExts), NonPagedPool); + } __except (EXCEPTION_EXECUTE_HANDLER) { + Status = STATUS_INSUFFICIENT_RESOURCES; + DbgBreak(); + } + + if (!NT_SUCCESS(Status)) { + goto errorout; + } + + INC_MEM_COUNT(PS_MCB, Mcb, sizeof(EXT2_MCB)); + DEBUG(DL_INF, ( "Ext2AllocateMcb: Mcb %wZ created.\n", &Mcb->FullName)); + + return Mcb; + +errorout: + + if (Mcb) { + + if (Mcb->ShortName.Buffer) { + DEC_MEM_COUNT(PS_MCB_NAME, Mcb->ShortName.Buffer, + Mcb->ShortName.MaximumLength); + Ext2FreePool(Mcb->ShortName.Buffer, EXT2_FNAME_MAGIC); + } + + if (Mcb->FullName.Buffer) { + DEC_MEM_COUNT(PS_MCB_NAME, Mcb->FullName.Buffer, + Mcb->FullName.MaximumLength); + Ext2FreePool(Mcb->FullName.Buffer, EXT2_FNAME_MAGIC); + } + + ExFreeToNPagedLookasideList(&(Ext2Global->Ext2McbLookasideList), Mcb); + } + + return NULL; +} + +VOID +Ext2FreeMcb (IN PEXT2_VCB Vcb, IN PEXT2_MCB Mcb) +{ + PEXT2_MCB Parent = Mcb->Parent; + + ASSERT(Mcb != NULL); + + ASSERT((Mcb->Identifier.Type == EXT2MCB) && + (Mcb->Identifier.Size == sizeof(EXT2_MCB))); + + if ((Mcb->Identifier.Type != EXT2MCB) || + (Mcb->Identifier.Size != sizeof(EXT2_MCB))) { + return; + } + + DEBUG(DL_INF, ( "Ext2FreeMcb: Mcb %wZ will be freed.\n", &Mcb->FullName)); + + if (IsMcbSymLink(Mcb) && Mcb->Target) { + Ext2DerefMcb(Mcb->Target); + } + + if (FsRtlNumberOfRunsInLargeMcb(&Mcb->Extents)) { + DEBUG(DL_EXT, ("List data extents for: %wZ\n", &Mcb->FullName)); + Ext2ListExtents(&Mcb->Extents); + } + FsRtlUninitializeLargeMcb(&(Mcb->Extents)); + if (FsRtlNumberOfRunsInLargeMcb(&Mcb->MetaExts)) { + DEBUG(DL_EXT, ("List meta extents for: %wZ\n", &Mcb->FullName)); + Ext2ListExtents(&Mcb->MetaExts); + } + FsRtlUninitializeLargeMcb(&(Mcb->MetaExts)); + ClearLongFlag(Mcb->Flags, MCB_ZONE_INITED); + + if (Mcb->ShortName.Buffer) { + DEC_MEM_COUNT(PS_MCB_NAME, Mcb->ShortName.Buffer, + Mcb->ShortName.MaximumLength); + Ext2FreePool(Mcb->ShortName.Buffer, EXT2_FNAME_MAGIC); + } + + if (Mcb->FullName.Buffer) { + DEC_MEM_COUNT(PS_MCB_NAME, Mcb->FullName.Buffer, + Mcb->FullName.MaximumLength); + Ext2FreePool(Mcb->FullName.Buffer, EXT2_FNAME_MAGIC); + } + + /* free dentry */ + if (Mcb->de) { + Ext2FreeEntry(Mcb->de); + } + + Mcb->Identifier.Type = 0; + Mcb->Identifier.Size = 0; + + ExFreeToNPagedLookasideList(&(Ext2Global->Ext2McbLookasideList), Mcb); + DEC_MEM_COUNT(PS_MCB, Mcb, sizeof(EXT2_MCB)); +} + + +PEXT2_MCB +Ext2SearchMcb( + PEXT2_VCB Vcb, + PEXT2_MCB Parent, + PUNICODE_STRING FileName +) +{ + BOOLEAN LockAcquired = FALSE; + PEXT2_MCB Mcb = NULL; + + __try { + ExAcquireResourceSharedLite(&Vcb->McbLock, TRUE); + LockAcquired = TRUE; + Mcb = Ext2SearchMcbWithoutLock(Parent, FileName); + } __finally { + if (LockAcquired) { + ExReleaseResourceLite(&Vcb->McbLock); + } + } + + return Mcb; +} + + +PEXT2_MCB +Ext2SearchMcbWithoutLock( + PEXT2_MCB Parent, + PUNICODE_STRING FileName +) +{ + PEXT2_MCB TmpMcb = NULL; + + DEBUG(DL_RES, ("Ext2SearchMcb: %wZ\n", FileName)); + + __try { + + Ext2ReferMcb(Parent); + + if (Ext2IsDot(FileName)) { + TmpMcb = Parent; + Ext2ReferMcb(Parent); + __leave; + } + + if (Ext2IsDotDot(FileName)) { + if (IsMcbRoot(Parent)) { + TmpMcb = Parent; + } else { + TmpMcb = Parent->Parent; + } + if (TmpMcb) { + Ext2ReferMcb(TmpMcb); + } + __leave; + } + + if (IsMcbSymLink(Parent)) { + if (Parent->Target) { + TmpMcb = Parent->Target->Child; + ASSERT(!IsMcbSymLink(Parent->Target)); + } else { + TmpMcb = NULL; + __leave; + } + } else { + TmpMcb = Parent->Child; + } + + while (TmpMcb) { + + if (!RtlCompareUnicodeString( + &(TmpMcb->ShortName), + FileName, TRUE )) { + Ext2ReferMcb(TmpMcb); + break; + } + + TmpMcb = TmpMcb->Next; + } + + } __finally { + + Ext2DerefMcb(Parent); + } + + return TmpMcb; +} + +VOID +Ext2InsertMcb ( + PEXT2_VCB Vcb, + PEXT2_MCB Parent, + PEXT2_MCB Child +) +{ + BOOLEAN LockAcquired = FALSE; + PEXT2_MCB Mcb = NULL; + + __try { + + ExAcquireResourceExclusiveLite( + &Vcb->McbLock, + TRUE ); + LockAcquired = TRUE; + + /* use it's target if it's a symlink */ + if (IsMcbSymLink(Parent)) { + Parent = Parent->Target; + ASSERT(!IsMcbSymLink(Parent)); + } + + Mcb = Parent->Child; + while (Mcb) { + if (Mcb == Child) { + break; + } + Mcb = Mcb->Next; + } + + if (Mcb) { + /* already attached in the list */ + DEBUG(DL_ERR, ( "Ext2InsertMcb: Child Mcb is alreay attached.\n")); + if (!IsFlagOn(Mcb->Flags, MCB_ENTRY_TREE)) { + SetLongFlag(Child->Flags, MCB_ENTRY_TREE); + DEBUG(DL_ERR, ( "Ext2InsertMcb: Child Mcb's flag isn't set.\n")); + } + + DbgBreak(); + + } else { + + /* insert this Mcb into the head */ + Child->Next = Parent->Child; + Parent->Child = Child; + Child->Parent = Parent; + Child->de->d_parent = Parent->de; + Ext2ReferMcb(Parent); + SetLongFlag(Child->Flags, MCB_ENTRY_TREE); + } + + } __finally { + + if (LockAcquired) { + ExReleaseResourceLite(&Vcb->McbLock); + } + } +} + +BOOLEAN +Ext2RemoveMcb ( + PEXT2_VCB Vcb, + PEXT2_MCB Mcb +) +{ + PEXT2_MCB TmpMcb = NULL; + BOOLEAN LockAcquired = FALSE; + BOOLEAN bLinked = FALSE; + + __try { + + ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); + LockAcquired = TRUE; + + if (Mcb->Parent) { + + if (Mcb->Parent->Child == Mcb) { + Mcb->Parent->Child = Mcb->Next; + bLinked = TRUE; + } else { + TmpMcb = Mcb->Parent->Child; + + while (TmpMcb && TmpMcb->Next != Mcb) { + TmpMcb = TmpMcb->Next; + } + + if (TmpMcb) { + TmpMcb->Next = Mcb->Next; + bLinked = TRUE; + } else { + /* we got errors: link broken */ + } + } + + if (bLinked) { + if (IsFlagOn(Mcb->Flags, MCB_ENTRY_TREE)) { + DEBUG(DL_RES, ("Mcb %p %wZ removed from Mcb %p %wZ\n", Mcb, + &Mcb->FullName, Mcb->Parent, &Mcb->Parent->FullName)); + Ext2DerefMcb(Mcb->Parent); + ClearLongFlag(Mcb->Flags, MCB_ENTRY_TREE); + } else { + DbgBreak(); + } + } else { + if (IsFlagOn(Mcb->Flags, MCB_ENTRY_TREE)) { + ClearLongFlag(Mcb->Flags, MCB_ENTRY_TREE); + } + DbgBreak(); + } + Mcb->Parent = NULL; + Mcb->de->d_parent = NULL; + } + + } __finally { + + if (LockAcquired) { + ExReleaseResourceLite(&Vcb->McbLock); + } + } + + return TRUE; +} + +VOID +Ext2CleanupAllMcbs(PEXT2_VCB Vcb) +{ + BOOLEAN LockAcquired = FALSE; + PEXT2_MCB Mcb = NULL; + + __try { + + ExAcquireResourceExclusiveLite( + &Vcb->McbLock, + TRUE ); + LockAcquired = TRUE; + + while (Mcb = Ext2FirstUnusedMcb(Vcb, TRUE, Vcb->NumOfMcb)) { + while (Mcb) { + PEXT2_MCB Next = Mcb->Next; + if (IsMcbSymLink(Mcb)) { + Mcb->Target = NULL; + } + Ext2FreeMcb(Vcb, Mcb); + Mcb = Next; + } + } + Ext2FreeMcb(Vcb, Vcb->McbTree); + Vcb->McbTree = NULL; + + } __finally { + + if (LockAcquired) { + ExReleaseResourceLite(&Vcb->McbLock); + } + } +} + +BOOLEAN +Ext2CheckSetBlock(PEXT2_IRP_CONTEXT IrpContext, PEXT2_VCB Vcb, LONGLONG Block) +{ + PEXT2_GROUP_DESC gd; + struct buffer_head *gb = NULL; + struct buffer_head *bh = NULL; + ULONG group, dwBlk, Length; + RTL_BITMAP bitmap; + BOOLEAN bModified = FALSE; + + group = (ULONG)(Block - EXT2_FIRST_DATA_BLOCK) / BLOCKS_PER_GROUP; + dwBlk = (ULONG)(Block - EXT2_FIRST_DATA_BLOCK) % BLOCKS_PER_GROUP; + + gd = ext4_get_group_desc(&Vcb->sb, group, &gb); + if (!gd) { + return FALSE; + } + bh = sb_getblk(&Vcb->sb, ext4_block_bitmap(&Vcb->sb, gd)); + + if (group == Vcb->sbi.s_groups_count - 1) { + Length = (ULONG)(TOTAL_BLOCKS % BLOCKS_PER_GROUP); + + /* s_blocks_count is integer multiple of s_blocks_per_group */ + if (Length == 0) + Length = BLOCKS_PER_GROUP; + } else { + Length = BLOCKS_PER_GROUP; + } + + if (dwBlk >= Length) { + fini_bh(&gb); + fini_bh(&bh); + return FALSE; + } + + + RtlInitializeBitMap(&bitmap, (PULONG)bh->b_data, Length); + + if (RtlCheckBit(&bitmap, dwBlk) == 0) { + DbgBreak(); + RtlSetBits(&bitmap, dwBlk, 1); + bModified = TRUE; + mark_buffer_dirty(bh); + } + + fini_bh(&gb); + fini_bh(&bh); + + return (!bModified); +} + +BOOLEAN +Ext2CheckBitmapConsistency(PEXT2_IRP_CONTEXT IrpContext, PEXT2_VCB Vcb) +{ + ULONG i, j, InodeBlocks; + + for (i = 0; i < Vcb->sbi.s_groups_count; i++) { + + PEXT2_GROUP_DESC gd; + struct buffer_head *bh = NULL; + + gd = ext4_get_group_desc(&Vcb->sb, i, &bh); + if (!gd) + continue; + Ext2CheckSetBlock(IrpContext, Vcb, ext4_block_bitmap(&Vcb->sb, gd)); + Ext2CheckSetBlock(IrpContext, Vcb, ext4_inode_bitmap(&Vcb->sb, gd)); + + + if (i == Vcb->sbi.s_groups_count - 1) { + InodeBlocks = ((INODES_COUNT % INODES_PER_GROUP) * + Vcb->InodeSize + Vcb->BlockSize - 1) / + (Vcb->BlockSize); + } else { + InodeBlocks = (INODES_PER_GROUP * Vcb->InodeSize + + Vcb->BlockSize - 1) / (Vcb->BlockSize); + } + + for (j = 0; j < InodeBlocks; j++ ) + Ext2CheckSetBlock(IrpContext, Vcb, ext4_inode_table(&Vcb->sb, gd) + j); + + fini_bh(&bh); + } + + return TRUE; +} + +/* Ext2Global->Resource should be already acquired */ +VOID +Ext2InsertVcb(PEXT2_VCB Vcb) +{ + InsertTailList(&(Ext2Global->VcbList), &Vcb->Next); +} + + +/* Ext2Global->Resource should be already acquired */ +VOID +Ext2RemoveVcb(PEXT2_VCB Vcb) +{ + RemoveEntryList(&Vcb->Next); + InitializeListHead(&Vcb->Next); +} + +NTSTATUS +Ext2QueryVolumeParams(IN PEXT2_VCB Vcb, IN PUNICODE_STRING Params) +{ + NTSTATUS Status; + RTL_QUERY_REGISTRY_TABLE QueryTable[2]; + + UNICODE_STRING UniName; + PUSHORT UniBuffer = NULL; + + USHORT UUID[50]; + + int i; + int len = 0; + + /* zero params */ + RtlZeroMemory(Params, sizeof(UNICODE_STRING)); + + /* constructing volume UUID name */ + memset(UUID, 0, sizeof(USHORT) * 50); + for (i=0; i < 16; i++) { + if (i == 0) { + swprintf((wchar_t *)&UUID[len], L"{%2.2X",Vcb->SuperBlock->s_uuid[i]); + len += 3; + } else if (i == 15) { + swprintf((wchar_t *)&UUID[len], L"-%2.2X}", Vcb->SuperBlock->s_uuid[i]); + len +=4; + } else { + swprintf((wchar_t *)&UUID[len], L"-%2.2X", Vcb->SuperBlock->s_uuid[i]); + len += 3; + } + } + + /* allocating memory for UniBuffer */ + UniBuffer = Ext2AllocatePool(PagedPool, 1024, EXT2_PARAM_MAGIC); + if (NULL == UniBuffer) { + Status = STATUS_INSUFFICIENT_RESOURCES; + goto errorout; + } + RtlZeroMemory(UniBuffer, 1024); + + /* querying volume parameter string */ + RtlZeroMemory(&QueryTable[0], sizeof(RTL_QUERY_REGISTRY_TABLE) * 2); + QueryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; + QueryTable[0].Name = UUID; + QueryTable[0].EntryContext = &(UniName); + UniName.MaximumLength = 1024; + UniName.Length = 0; + UniName.Buffer = UniBuffer; + + Status = RtlQueryRegistryValues( + RTL_REGISTRY_ABSOLUTE, + Ext2Global->RegistryPath.Buffer, + &QueryTable[0], + NULL, + NULL + ); + + if (!NT_SUCCESS(Status)) { + goto errorout; + } + +errorout: + + if (NT_SUCCESS(Status)) { + *Params = UniName; + } else { + if (UniBuffer) { + Ext2FreePool(UniBuffer, EXT2_PARAM_MAGIC); + } + } + + return Status; +} + +VOID +Ext2ParseRegistryVolumeParams( + IN PUNICODE_STRING Params, + OUT PEXT2_VOLUME_PROPERTY3 Property +) +{ + WCHAR Codepage[CODEPAGE_MAXLEN]; + WCHAR Prefix[HIDINGPAT_LEN]; + WCHAR Suffix[HIDINGPAT_LEN]; + USHORT MountPoint[4]; + UCHAR DrvLetter[4]; + WCHAR wUID[8], wGID[8], wEUID[8], wEGID[8]; + CHAR sUID[8], sGID[8], sEUID[8], sEGID[8]; + + BOOLEAN bWriteSupport = FALSE, + bCheckBitmap = FALSE, + bCodeName = FALSE, + bMountPoint = FALSE; + BOOLEAN bUID = 0, bGID = 0, bEUID = 0, bEGID = 0; + + struct { + PWCHAR Name; /* parameters name */ + PBOOLEAN bExist; /* is it contained in params */ + USHORT Length; /* parameter value length */ + PWCHAR uValue; /* value buffer in unicode */ + PCHAR aValue; /* value buffer in ansi */ + } ParamPattern[] = { + /* writing support */ + {READING_ONLY, &Property->bReadonly, 0, NULL, NULL}, + {WRITING_SUPPORT, &bWriteSupport, 0, NULL, NULL}, + {EXT3_FORCEWRITING, &Property->bExt3Writable, 0, NULL, NULL}, + + /* need check bitmap */ + {CHECKING_BITMAP, &bCheckBitmap, 0, NULL, NULL}, + /* codepage */ + {CODEPAGE_NAME, &bCodeName, CODEPAGE_MAXLEN, + &Codepage[0], Property->Codepage}, + /* filter prefix and suffix */ + {HIDING_PREFIX, &Property->bHidingPrefix, HIDINGPAT_LEN, + &Prefix[0], Property->sHidingPrefix}, + {HIDING_SUFFIX, &Property->bHidingSuffix, HIDINGPAT_LEN, + &Suffix[0], Property->sHidingSuffix}, + {MOUNT_POINT, &bMountPoint, 4, + &MountPoint[0], &DrvLetter[0]}, + + {UID, &bUID, 8, &wUID[0], &sUID[0],}, + {GID, &bGID, 8, &wGID[0], &sGID[0]}, + {EUID, &bEUID, 8, &wEUID[0], &sEUID[0]}, + {EGID, &bEGID, 8, &wEGID[0], &sEGID[0]}, + + /* end */ + {NULL, NULL, 0, NULL} + }; + + USHORT i, j, k; + + RtlZeroMemory(Codepage, CODEPAGE_MAXLEN); + RtlZeroMemory(Prefix, HIDINGPAT_LEN); + RtlZeroMemory(Suffix, HIDINGPAT_LEN); + RtlZeroMemory(MountPoint, sizeof(USHORT) * 4); + RtlZeroMemory(DrvLetter, sizeof(CHAR) * 4); + + RtlZeroMemory(Property, sizeof(EXT2_VOLUME_PROPERTY3)); + Property->Magic = EXT2_VOLUME_PROPERTY_MAGIC; + Property->Command = APP_CMD_SET_PROPERTY3; + + for (i=0; ParamPattern[i].Name != NULL; i++) { + + UNICODE_STRING Name1=*Params, Name2; + RtlInitUnicodeString(&Name2, ParamPattern[i].Name); + *ParamPattern[i].bExist = FALSE; + + for (j=0; j * sizeof(WCHAR) + Name2.Length <= Params->Length ; j++) { + + Name1.MaximumLength = Params->Length - j * sizeof(WCHAR); + Name1.Length = Name2.Length; + Name1.Buffer = &Params->Buffer[j]; + + if (!RtlCompareUnicodeString(&Name1, &Name2, TRUE)) { + if (j * sizeof(WCHAR) + Name2.Length == Params->Length || + Name1.Buffer[Name2.Length/sizeof(WCHAR)] == L';' || + Name1.Buffer[Name2.Length/sizeof(WCHAR)] == L',' ) { + *(ParamPattern[i].bExist) = TRUE; + } else if ((j * 2 + Name2.Length < Params->Length + 2) || + (Name1.Buffer[Name2.Length/sizeof(WCHAR)] == L'=' )) { + j += Name2.Length/sizeof(WCHAR) + 1; + k = 0; + while ( j + k < Params->Length/2 && + k < ParamPattern[i].Length && + Params->Buffer[j+k] != L';' && + Params->Buffer[j+k] != L',' ) { + ParamPattern[i].uValue[k] = Params->Buffer[j + k++]; + } + if (k) { + NTSTATUS status; + ANSI_STRING AnsiName; + AnsiName.Length = 0; + AnsiName.MaximumLength =ParamPattern[i].Length; + AnsiName.Buffer = ParamPattern[i].aValue; + + Name2.Buffer = ParamPattern[i].uValue; + Name2.MaximumLength = Name2.Length = k * sizeof(WCHAR); + status = RtlUnicodeStringToAnsiString( + &AnsiName, &Name2, FALSE); + if (NT_SUCCESS(status)) { + *(ParamPattern[i].bExist) = TRUE; + } else { + *ParamPattern[i].bExist = FALSE; + } + } + } + break; + } + } + } + + if (bMountPoint) { + Property->DrvLetter = DrvLetter[0]; + Property->DrvLetter |= 0x80; + } + + if (bUID && bGID) { + SetFlag(Property->Flags2, EXT2_VPROP3_USERIDS); + sUID[7] = sGID[7] = sEUID[7] = sEGID[7] = 0; + Property->uid = (USHORT)atoi(sUID); + Property->gid = (USHORT)atoi(sGID); + if (bEUID) { + Property->euid = (USHORT)atoi(sEUID); + Property->egid = (USHORT)atoi(sEGID); + Property->EIDS = TRUE; + } else { + Property->EIDS = FALSE; + } + } else { + ClearFlag(Property->Flags2, EXT2_VPROP3_USERIDS); + } +} + +NTSTATUS +Ext2PerformRegistryVolumeParams(IN PEXT2_VCB Vcb) +{ + NTSTATUS Status; + UNICODE_STRING VolumeParams; + + Status = Ext2QueryVolumeParams(Vcb, &VolumeParams); + if (NT_SUCCESS(Status)) { + + /* set Vcb settings from registery */ + EXT2_VOLUME_PROPERTY3 Property; + Ext2ParseRegistryVolumeParams(&VolumeParams, &Property); + Ext2ProcessVolumeProperty(Vcb, &Property, sizeof(Property)); + + } else { + + /* don't support auto mount */ + if (IsFlagOn(Ext2Global->Flags, EXT2_AUTO_MOUNT)) { + Status = STATUS_SUCCESS; + } else { + Status = STATUS_UNSUCCESSFUL; + goto errorout; + } + + /* set Vcb settings from Ext2Global */ + if (IsFlagOn(Ext2Global->Flags, EXT2_SUPPORT_WRITING)) { + if (Vcb->IsExt3fs) { + if (IsFlagOn(Ext2Global->Flags, EXT3_FORCE_WRITING)) { + ClearLongFlag(Vcb->Flags, VCB_READ_ONLY); + } else { + SetLongFlag(Vcb->Flags, VCB_READ_ONLY); + } + } else { + ClearLongFlag(Vcb->Flags, VCB_READ_ONLY); + } + } else { + SetLongFlag(Vcb->Flags, VCB_READ_ONLY); + } + + /* set the default codepage */ + Vcb->Codepage.PageTable = Ext2Global->Codepage.PageTable; + memcpy(Vcb->Codepage.AnsiName, Ext2Global->Codepage.AnsiName, CODEPAGE_MAXLEN); + Vcb->Codepage.PageTable = Ext2Global->Codepage.PageTable; + + if (Vcb->bHidingPrefix = Ext2Global->bHidingPrefix) { + RtlCopyMemory( Vcb->sHidingPrefix, + Ext2Global->sHidingPrefix, + HIDINGPAT_LEN); + } else { + RtlZeroMemory( Vcb->sHidingPrefix, + HIDINGPAT_LEN); + } + + if (Vcb->bHidingSuffix = Ext2Global->bHidingSuffix) { + RtlCopyMemory( Vcb->sHidingSuffix, + Ext2Global->sHidingSuffix, + HIDINGPAT_LEN); + } else { + RtlZeroMemory( Vcb->sHidingSuffix, + HIDINGPAT_LEN); + } + } + +errorout: + + if (VolumeParams.Buffer) { + Ext2FreePool(VolumeParams.Buffer, EXT2_PARAM_MAGIC); + } + + return Status; +} + +NTSTATUS +Ext2InitializeLabel( + IN PEXT2_VCB Vcb, + IN PEXT2_SUPER_BLOCK Sb +) +{ + NTSTATUS status; + + USHORT Length; + UNICODE_STRING Label; + OEM_STRING OemName; + + Label.MaximumLength = 16 * sizeof(WCHAR); + Label.Length = 0; + Label.Buffer = Vcb->Vpb->VolumeLabel; + Vcb->Vpb->VolumeLabelLength = 0; + RtlZeroMemory(Label.Buffer, Label.MaximumLength); + + Length = 16; + while ( (Length > 0) && + ((Sb->s_volume_name[Length -1] == 0x00) || + (Sb->s_volume_name[Length - 1] == 0x20) ) + ) { + Length--; + } + + if (Length == 0) { + return STATUS_SUCCESS; + } + + OemName.Buffer = Sb->s_volume_name; + OemName.MaximumLength = 16; + OemName.Length = Length; + + status = Ext2OEMToUnicode(Vcb, &Label, &OemName); + if (NT_SUCCESS(status)) { + Vcb->Vpb->VolumeLabelLength = Label.Length; + } + + return status; +} + +static __inline BOOLEAN Ext2IsNullUuid (__u8 * uuid) +{ + int i; + for (i = 0; i < 16; i++) { + if (uuid[i]) { + break; + } + } + + return (i >= 16); +} + +#define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0)) + +NTSTATUS +Ext2InitializeVcb( IN PEXT2_IRP_CONTEXT IrpContext, + IN PEXT2_VCB Vcb, + IN PEXT2_SUPER_BLOCK sb, + IN PDEVICE_OBJECT TargetDevice, + IN PDEVICE_OBJECT VolumeDevice, + IN PVPB Vpb ) +{ + NTSTATUS Status = STATUS_UNRECOGNIZED_VOLUME; + ULONG IoctlSize; + LONGLONG DiskSize; + LONGLONG PartSize; + UNICODE_STRING RootNode; + USHORT Buffer[2]; + ULONG ChangeCount = 0, features; + CC_FILE_SIZES FileSizes; + int i, has_huge_files; + + BOOLEAN VcbResourceInitialized = FALSE; + BOOLEAN NotifySyncInitialized = FALSE; + BOOLEAN ExtentsInitialized = FALSE; + BOOLEAN InodeLookasideInitialized = FALSE; + BOOLEAN GroupLoaded = FALSE; + + __try { + + if (Vpb == NULL) { + Status = STATUS_DEVICE_NOT_READY; + __leave; + } + + /* Reject mounting volume if we encounter unsupported incompat features */ + if (FlagOn(sb->s_feature_incompat, ~EXT4_FEATURE_INCOMPAT_SUPP)) { + Status = STATUS_UNRECOGNIZED_VOLUME; + __leave; + } + + /* Mount the volume RO if we encounter unsupported ro_compat features */ + if (FlagOn(sb->s_feature_ro_compat, ~EXT4_FEATURE_RO_COMPAT_SUPP)) { + SetLongFlag(Vcb->Flags, VCB_RO_COMPAT_READ_ONLY); + } + + /* Recognize the filesystem as Ext3fs if it supports journalling */ + if (IsFlagOn(sb->s_feature_compat, EXT4_FEATURE_COMPAT_HAS_JOURNAL)) { + Vcb->IsExt3fs = TRUE; + } + + /* check block size */ + Vcb->BlockSize = (EXT2_MIN_BLOCK_SIZE << sb->s_log_block_size); + /* we cannot handle volume with block size bigger than 64k */ + if (Vcb->BlockSize > EXT2_MAX_USER_BLKSIZE) { + Status = STATUS_UNRECOGNIZED_VOLUME; + __leave; + } + + if (Vcb->BlockSize >= PAGE_SIZE) { + Vcb->IoUnitBits = PAGE_SHIFT; + Vcb->IoUnitSize = PAGE_SIZE; + } else { + Vcb->IoUnitSize = Vcb->BlockSize; + Vcb->IoUnitBits = Ext2Log2(Vcb->BlockSize); + } + + /* initialize vcb header members ... */ + Vcb->Header.IsFastIoPossible = FastIoIsNotPossible; + Vcb->Header.Resource = &(Vcb->MainResource); + Vcb->Header.PagingIoResource = &(Vcb->PagingIoResource); + Vcb->OpenVolumeCount = 0; + Vcb->OpenHandleCount = 0; + Vcb->ReferenceCount = 0; + + /* initialize eresources */ + ExInitializeResourceLite(&Vcb->MainResource); + ExInitializeResourceLite(&Vcb->PagingIoResource); + ExInitializeResourceLite(&Vcb->MetaInode); + ExInitializeResourceLite(&Vcb->MetaBlock); + ExInitializeResourceLite(&Vcb->McbLock); + ExInitializeResourceLite(&Vcb->FcbLock); + ExInitializeResourceLite(&Vcb->sbi.s_gd_lock); +#ifndef _WIN2K_TARGET_ + ExInitializeFastMutex(&Vcb->Mutex); + FsRtlSetupAdvancedHeader(&Vcb->Header, &Vcb->Mutex); +#endif + VcbResourceInitialized = TRUE; + + /* initialize Fcb list head */ + InitializeListHead(&Vcb->FcbList); + + /* initialize Mcb list head */ + InitializeListHead(&(Vcb->McbList)); + + /* initialize directory notify list */ + InitializeListHead(&Vcb->NotifyList); + FsRtlNotifyInitializeSync(&Vcb->NotifySync); + NotifySyncInitialized = TRUE; + + /* superblock checking */ + Vcb->SuperBlock = sb; + + /* initialize Vpb and Label */ + Vcb->DeviceObject = VolumeDevice; + Vcb->TargetDeviceObject = TargetDevice; + Vcb->Vpb = Vpb; + Vcb->RealDevice = Vpb->RealDevice; + Vpb->DeviceObject = VolumeDevice; + + /* set inode size */ + Vcb->InodeSize = (ULONG)sb->s_inode_size; + if (Vcb->InodeSize == 0) { + Vcb->InodeSize = EXT2_GOOD_OLD_INODE_SIZE; + } + + /* initialize inode lookaside list */ + ExInitializeNPagedLookasideList(&(Vcb->InodeLookasideList), + NULL, NULL, 0, Vcb->InodeSize, + 'SNIE', 0); + + InodeLookasideInitialized = TRUE; + + /* initialize label in Vpb */ + Status = Ext2InitializeLabel(Vcb, sb); + if (!NT_SUCCESS(Status)) { + DbgBreak(); + } + + /* check device characteristics flags */ + if (IsFlagOn(Vpb->RealDevice->Characteristics, FILE_REMOVABLE_MEDIA)) { + SetLongFlag(Vcb->Flags, VCB_REMOVABLE_MEDIA); + } + + if (IsFlagOn(Vpb->RealDevice->Characteristics, FILE_FLOPPY_DISKETTE)) { + SetLongFlag(Vcb->Flags, VCB_FLOPPY_DISK); + } + + if (IsFlagOn(Vpb->RealDevice->Characteristics, FILE_READ_ONLY_DEVICE)) { + SetLongFlag(Vcb->Flags, VCB_WRITE_PROTECTED); + } + + if (IsFlagOn(TargetDevice->Characteristics, FILE_READ_ONLY_DEVICE)) { + SetLongFlag(Vcb->Flags, VCB_WRITE_PROTECTED); + } + + /* verify device is writable ? */ + if (Ext2IsMediaWriteProtected(IrpContext, TargetDevice)) { + SetFlag(Vcb->Flags, VCB_WRITE_PROTECTED); + } + + /* initialize UUID and serial number */ + if (Ext2IsNullUuid(sb->s_uuid)) { + ExUuidCreate((UUID *)sb->s_uuid); + } + Vpb->SerialNumber = ((ULONG*)sb->s_uuid)[0] + + ((ULONG*)sb->s_uuid)[1] + + ((ULONG*)sb->s_uuid)[2] + + ((ULONG*)sb->s_uuid)[3]; + + /* query partition size and disk geometry parameters */ + DiskSize = Vcb->DiskGeometry.Cylinders.QuadPart * + Vcb->DiskGeometry.TracksPerCylinder * + Vcb->DiskGeometry.SectorsPerTrack * + Vcb->DiskGeometry.BytesPerSector; + + IoctlSize = sizeof(PARTITION_INFORMATION); + Status = Ext2DiskIoControl( + TargetDevice, + IOCTL_DISK_GET_PARTITION_INFO, + NULL, + 0, + &Vcb->PartitionInformation, + &IoctlSize ); + if (NT_SUCCESS(Status)) { + PartSize = Vcb->PartitionInformation.PartitionLength.QuadPart; + } else { + Vcb->PartitionInformation.StartingOffset.QuadPart = 0; + Vcb->PartitionInformation.PartitionLength.QuadPart = DiskSize; + PartSize = DiskSize; + Status = STATUS_SUCCESS; + } + Vcb->Header.AllocationSize.QuadPart = + Vcb->Header.FileSize.QuadPart = PartSize; + + Vcb->Header.ValidDataLength.QuadPart = + Vcb->Header.FileSize.QuadPart; + + /* verify count */ + IoctlSize = sizeof(ULONG); + Status = Ext2DiskIoControl( + TargetDevice, + IOCTL_DISK_CHECK_VERIFY, + NULL, + 0, + &ChangeCount, + &IoctlSize ); + + if (!NT_SUCCESS(Status)) { + __leave; + } + Vcb->ChangeCount = ChangeCount; + + /* create the stream object for ext2 volume */ + Vcb->Volume = IoCreateStreamFileObject(NULL, Vcb->Vpb->RealDevice); + if (!Vcb->Volume) { + Status = STATUS_UNRECOGNIZED_VOLUME; + __leave; + } + + /* initialize streaming object file */ + Vcb->Volume->SectionObjectPointer = &(Vcb->SectionObject); + Vcb->Volume->ReadAccess = TRUE; + Vcb->Volume->WriteAccess = TRUE; + Vcb->Volume->DeleteAccess = TRUE; + Vcb->Volume->FsContext = (PVOID) Vcb; + Vcb->Volume->FsContext2 = NULL; + Vcb->Volume->Vpb = Vcb->Vpb; + + FileSizes.AllocationSize.QuadPart = + FileSizes.FileSize.QuadPart = + FileSizes.ValidDataLength.QuadPart = + Vcb->Header.AllocationSize.QuadPart; + + CcInitializeCacheMap( Vcb->Volume, + &FileSizes, + TRUE, + &(Ext2Global->CacheManagerNoOpCallbacks), + Vcb ); + + /* initialize disk block LargetMcb and entry Mcb, + it will raise an expcetion if failed */ + __try { + FsRtlInitializeLargeMcb(&(Vcb->Extents), PagedPool); + } __except (EXCEPTION_EXECUTE_HANDLER) { + Status = STATUS_INSUFFICIENT_RESOURCES; + DbgBreak(); + } + if (!NT_SUCCESS(Status)) { + __leave; + } + ExtentsInitialized = TRUE; + + /* set block device */ + Vcb->bd.bd_dev = Vcb->RealDevice; + Vcb->bd.bd_geo = Vcb->DiskGeometry; + Vcb->bd.bd_part = Vcb->PartitionInformation; + Vcb->bd.bd_volume = Vcb->Volume; + Vcb->bd.bd_priv = (void *) Vcb; + memset(&Vcb->bd.bd_bh_root, 0, sizeof(struct rb_root)); + InitializeListHead(&Vcb->bd.bd_bh_free); + ExInitializeResourceLite(&Vcb->bd.bd_bh_lock); + KeInitializeEvent(&Vcb->bd.bd_bh_notify, + NotificationEvent, TRUE); + Vcb->bd.bd_bh_cache = kmem_cache_create("bd_bh_buffer", + Vcb->BlockSize, 0, 0, NULL); + if (!Vcb->bd.bd_bh_cache) { + Status = STATUS_INSUFFICIENT_RESOURCES; + __leave; + } + + Vcb->SectorBits = Ext2Log2(SECTOR_SIZE); + Vcb->sb.s_magic = sb->s_magic; + Vcb->sb.s_bdev = &Vcb->bd; + Vcb->sb.s_blocksize = BLOCK_SIZE; + Vcb->sb.s_blocksize_bits = BLOCK_BITS; + Vcb->sb.s_priv = (void *) Vcb; + Vcb->sb.s_fs_info = &Vcb->sbi; + + Vcb->sbi.s_es = sb; + Vcb->sbi.s_blocks_per_group = sb->s_blocks_per_group; + Vcb->sbi.s_first_ino = sb->s_first_ino; + Vcb->sbi.s_desc_size = sb->s_desc_size; + Vcb->sbi.s_clusters_per_group = sb->s_clusters_per_group; + Vcb->sbi.s_inode_size = sb->s_inode_size; + Vcb->sbi.s_csum_seed = ext4_chksum(&Vcb->sbi, ~0, sb->s_uuid, sizeof(sb->s_uuid)); + + if (EXT3_HAS_INCOMPAT_FEATURE(&Vcb->sb, EXT4_FEATURE_INCOMPAT_64BIT)) { + if (Vcb->sbi.s_desc_size < EXT4_MIN_DESC_SIZE_64BIT || + Vcb->sbi.s_desc_size > EXT4_MAX_DESC_SIZE || + !is_power_of_2(Vcb->sbi.s_desc_size)) { + DEBUG(DL_ERR, ("EXT4-fs: unsupported descriptor size %lu\n", Vcb->sbi.s_desc_size)); + Status = STATUS_DISK_CORRUPT_ERROR; + __leave; + } + } else { + Vcb->sbi.s_desc_size = EXT4_MIN_DESC_SIZE; + } + + Vcb->sbi.s_blocks_per_group = sb->s_blocks_per_group; + Vcb->sbi.s_inodes_per_group = sb->s_inodes_per_group; + if (EXT3_INODES_PER_GROUP(&Vcb->sb) == 0) { + Status = STATUS_DISK_CORRUPT_ERROR; + __leave; + } + Vcb->sbi.s_inodes_per_block = BLOCK_SIZE / Vcb->InodeSize; + if (Vcb->sbi.s_inodes_per_block == 0) { + Status = STATUS_DISK_CORRUPT_ERROR; + __leave; + } + Vcb->sbi.s_itb_per_group = Vcb->sbi.s_inodes_per_group / + Vcb->sbi.s_inodes_per_block; + + + Vcb->sbi.s_desc_per_block = BLOCK_SIZE / GROUP_DESC_SIZE; + Vcb->sbi.s_desc_per_block_bits = ilog2(Vcb->sbi.s_desc_per_block); + + for (i=0; i < 4; i++) { + Vcb->sbi.s_hash_seed[i] = sb->s_hash_seed[i]; + } + Vcb->sbi.s_def_hash_version = sb->s_def_hash_version; + + if (le32_to_cpu(sb->s_rev_level) == EXT3_GOOD_OLD_REV && + (EXT3_HAS_COMPAT_FEATURE(&Vcb->sb, ~0U) || + EXT3_HAS_RO_COMPAT_FEATURE(&Vcb->sb, ~0U) || + EXT3_HAS_INCOMPAT_FEATURE(&Vcb->sb, ~0U))) { + printk(KERN_WARNING + "EXT3-fs warning: feature flags set on rev 0 fs, " + "running e2fsck is recommended\n"); + } + + /* + * Check feature flags regardless of the revision level, since we + * previously didn't change the revision level when setting the flags, + * so there is a chance incompat flags are set on a rev 0 filesystem. + */ + features = EXT3_HAS_INCOMPAT_FEATURE(&Vcb->sb, ~EXT4_FEATURE_INCOMPAT_SUPP); + if (features & EXT4_FEATURE_INCOMPAT_DIRDATA) { + SetLongFlag(Vcb->Flags, VCB_READ_ONLY); + ClearFlag(features, EXT4_FEATURE_INCOMPAT_DIRDATA); + } + if (features) { + printk(KERN_ERR "EXT3-fs: %s: couldn't mount because of " + "unsupported optional features (%x).\n", + Vcb->sb.s_id, le32_to_cpu(features)); + Status = STATUS_UNRECOGNIZED_VOLUME; + __leave; + } + + features = EXT3_HAS_RO_COMPAT_FEATURE(&Vcb->sb, ~EXT4_FEATURE_RO_COMPAT_SUPP); + if (features) { + printk(KERN_ERR "EXT3-fs: %s: unsupported optional features in this volume: (%x).\n", + Vcb->sb.s_id, le32_to_cpu(features)); + if (CanIWrite(Vcb)) { + } else { + SetLongFlag(Vcb->Flags, VCB_READ_ONLY); + } + } + + /* + * Mount ext4 with 64-bit block numbers read-only while testing. + */ + if (EXT3_HAS_INCOMPAT_FEATURE(&Vcb->sb, EXT4_FEATURE_INCOMPAT_64BIT)) { + printk(KERN_ERR "EXT3-fs: %s: Mounting ext4 with 64-bit block numbers read-only.\n", + Vcb->sb.s_id); + SetLongFlag(Vcb->Flags, VCB_READ_ONLY); + } + + has_huge_files = EXT3_HAS_RO_COMPAT_FEATURE(&Vcb->sb, EXT4_FEATURE_RO_COMPAT_HUGE_FILE); + + Vcb->sb.s_maxbytes = ext3_max_size(BLOCK_BITS, has_huge_files); + Vcb->max_bitmap_bytes = ext3_max_bitmap_size(BLOCK_BITS, + has_huge_files); + Vcb->max_bytes = ext3_max_size(BLOCK_BITS, has_huge_files); + + /* calculate maximum file bocks ... */ + { + ULONG dwData[EXT2_BLOCK_TYPES] = {EXT2_NDIR_BLOCKS, 1, 1, 1}; + ULONG i; + + ASSERT(BLOCK_BITS == Ext2Log2(BLOCK_SIZE)); + + Vcb->sbi.s_groups_count = (ULONG)(ext3_blocks_count(sb) - sb->s_first_data_block + + sb->s_blocks_per_group - 1) / sb->s_blocks_per_group; + + Vcb->max_data_blocks = 0; + for (i = 0; i < EXT2_BLOCK_TYPES; i++) { + if (BLOCK_BITS >= 12 && i == (EXT2_BLOCK_TYPES - 1)) { + dwData[i] = 0x40000000; + } else { + dwData[i] = dwData[i] << ((BLOCK_BITS - 2) * i); + } + Vcb->max_blocks_per_layer[i] = dwData[i]; + Vcb->max_data_blocks += Vcb->max_blocks_per_layer[i]; + } + } + + Vcb->sbi.s_gdb_count = (Vcb->sbi.s_groups_count + Vcb->sbi.s_desc_per_block - 1) / + Vcb->sbi.s_desc_per_block; + /* load all gorup desc */ + if (!Ext2LoadGroup(Vcb)) { + Status = STATUS_UNSUCCESSFUL; + __leave; + } + GroupLoaded = TRUE; + + /* recovery journal since it's ext3 */ + if (Vcb->IsExt3fs) { + Ext2RecoverJournal(IrpContext, Vcb); + if (IsFlagOn(Vcb->Flags, VCB_JOURNAL_RECOVER)) { + SetLongFlag(Vcb->Flags, VCB_READ_ONLY); + } + } + + /* Now allocating the mcb for root ... */ + Buffer[0] = L'\\'; + Buffer[1] = 0; + RootNode.Buffer = Buffer; + RootNode.MaximumLength = RootNode.Length = 2; + Vcb->McbTree = Ext2AllocateMcb( + Vcb, &RootNode, NULL, + FILE_ATTRIBUTE_DIRECTORY + ); + if (!Vcb->McbTree) { + DbgBreak(); + Status = STATUS_UNSUCCESSFUL; + __leave; + } + + Vcb->sb.s_root = Ext2BuildEntry(Vcb, NULL, &RootNode); + if (!Vcb->sb.s_root) { + DbgBreak(); + Status = STATUS_UNSUCCESSFUL; + __leave; + } + Vcb->sb.s_root->d_sb = &Vcb->sb; + Vcb->sb.s_root->d_inode = &Vcb->McbTree->Inode; + Vcb->McbTree->de = Vcb->sb.s_root; + + /* load root inode */ + Vcb->McbTree->Inode.i_ino = EXT2_ROOT_INO; + Vcb->McbTree->Inode.i_sb = &Vcb->sb; + if (!Ext2LoadInode(Vcb, &Vcb->McbTree->Inode)) { + DbgBreak(); + Status = STATUS_CANT_WAIT; + __leave; + } + + /* initializeroot node */ + Vcb->McbTree->CreationTime = Ext2NtTime(Vcb->McbTree->Inode.i_ctime); + Vcb->McbTree->LastAccessTime = Ext2NtTime(Vcb->McbTree->Inode.i_atime); + Vcb->McbTree->LastWriteTime = Ext2NtTime(Vcb->McbTree->Inode.i_mtime); + Vcb->McbTree->ChangeTime = Ext2NtTime(Vcb->McbTree->Inode.i_mtime); + + /* check bitmap if user specifies it */ + if (IsFlagOn(Ext2Global->Flags, EXT2_CHECKING_BITMAP)) { + Ext2CheckBitmapConsistency(IrpContext, Vcb); + } + + /* get anything doen, then refer target device */ + ObReferenceObject(Vcb->TargetDeviceObject); + + /* query parameters from registry */ + Ext2PerformRegistryVolumeParams(Vcb); + + SetLongFlag(Vcb->Flags, VCB_INITIALIZED); + + } __finally { + + if (!NT_SUCCESS(Status)) { + + if (Vcb->McbTree) { + Ext2FreeMcb(Vcb, Vcb->McbTree); + } + + if (InodeLookasideInitialized) { + ExDeleteNPagedLookasideList(&(Vcb->InodeLookasideList)); + } + + if (ExtentsInitialized) { + if (Vcb->bd.bd_bh_cache) { + if (GroupLoaded) + Ext2PutGroup(Vcb); + kmem_cache_destroy(Vcb->bd.bd_bh_cache); + } + FsRtlUninitializeLargeMcb(&(Vcb->Extents)); + } + + if (Vcb->Volume) { + if (Vcb->Volume->PrivateCacheMap) { + Ext2SyncUninitializeCacheMap(Vcb->Volume); + } + ObDereferenceObject(Vcb->Volume); + } + + if (NotifySyncInitialized) { + FsRtlNotifyUninitializeSync(&Vcb->NotifySync); + } + + if (VcbResourceInitialized) { + ExDeleteResourceLite(&Vcb->FcbLock); + ExDeleteResourceLite(&Vcb->McbLock); + ExDeleteResourceLite(&Vcb->MetaInode); + ExDeleteResourceLite(&Vcb->MetaBlock); + ExDeleteResourceLite(&Vcb->sbi.s_gd_lock); + ExDeleteResourceLite(&Vcb->MainResource); + ExDeleteResourceLite(&Vcb->PagingIoResource); + } + } + } + + return Status; +} + + +VOID +Ext2TearDownStream(IN PEXT2_VCB Vcb) +{ + PFILE_OBJECT Stream = Vcb->Volume; + IO_STATUS_BLOCK IoStatus; + + ASSERT(Vcb != NULL); + ASSERT((Vcb->Identifier.Type == EXT2VCB) && + (Vcb->Identifier.Size == sizeof(EXT2_VCB))); + + if (Stream) { + + Vcb->Volume = NULL; + + if (IsFlagOn(Stream->Flags, FO_FILE_MODIFIED)) { + CcFlushCache(&(Vcb->SectionObject), NULL, 0, &IoStatus); + ClearFlag(Stream->Flags, FO_FILE_MODIFIED); + } + + if (Stream->PrivateCacheMap) { + Ext2SyncUninitializeCacheMap(Stream); + } + + ObDereferenceObject(Stream); + } +} + +VOID +Ext2DestroyVcb (IN PEXT2_VCB Vcb) +{ + ASSERT(Vcb != NULL); + ASSERT((Vcb->Identifier.Type == EXT2VCB) && + (Vcb->Identifier.Size == sizeof(EXT2_VCB))); + + DEBUG(DL_FUN, ("Ext2DestroyVcb ...\n")); + + if (Vcb->Volume) { + Ext2TearDownStream(Vcb); + } + ASSERT(NULL == Vcb->Volume); + + FsRtlNotifyUninitializeSync(&Vcb->NotifySync); + Ext2ListExtents(&Vcb->Extents); + FsRtlUninitializeLargeMcb(&(Vcb->Extents)); + + Ext2CleanupAllMcbs(Vcb); + + Ext2DropBH(Vcb); + + if (Vcb->bd.bd_bh_cache) + kmem_cache_destroy(Vcb->bd.bd_bh_cache); + ExDeleteResourceLite(&Vcb->bd.bd_bh_lock); + + if (Vcb->SuperBlock) { + Ext2FreePool(Vcb->SuperBlock, EXT2_SB_MAGIC); + Vcb->SuperBlock = NULL; + } + + if (IsFlagOn(Vcb->Flags, VCB_NEW_VPB)) { + ASSERT(Vcb->Vpb2 != NULL); + DEBUG(DL_DBG, ("Ext2DestroyVcb: Vpb2 to be freed: %p\n", Vcb->Vpb2)); + ExFreePoolWithTag(Vcb->Vpb2, TAG_VPB); + DEC_MEM_COUNT(PS_VPB, Vcb->Vpb2, sizeof(VPB)); + Vcb->Vpb2 = NULL; + } + + ObDereferenceObject(Vcb->TargetDeviceObject); + + ExDeleteNPagedLookasideList(&(Vcb->InodeLookasideList)); + ExDeleteResourceLite(&Vcb->FcbLock); + ExDeleteResourceLite(&Vcb->McbLock); + ExDeleteResourceLite(&Vcb->MetaInode); + ExDeleteResourceLite(&Vcb->MetaBlock); + ExDeleteResourceLite(&Vcb->sbi.s_gd_lock); + ExDeleteResourceLite(&Vcb->PagingIoResource); + ExDeleteResourceLite(&Vcb->MainResource); + + DEBUG(DL_DBG, ("Ext2DestroyVcb: DevObject=%p Vcb=%p\n", Vcb->DeviceObject, Vcb)); + IoDeleteDevice(Vcb->DeviceObject); + DEC_MEM_COUNT(PS_VCB, Vcb->DeviceObject, sizeof(EXT2_VCB)); +} + + +/* uninitialize cache map */ + +VOID +Ext2SyncUninitializeCacheMap ( + IN PFILE_OBJECT FileObject +) +{ + CACHE_UNINITIALIZE_EVENT UninitializeCompleteEvent; + NTSTATUS WaitStatus; + LARGE_INTEGER Ext2LargeZero = {0,0}; + + + KeInitializeEvent( &UninitializeCompleteEvent.Event, + SynchronizationEvent, + FALSE); + + CcUninitializeCacheMap( FileObject, + &Ext2LargeZero, + &UninitializeCompleteEvent ); + + WaitStatus = KeWaitForSingleObject( &UninitializeCompleteEvent.Event, + Executive, + KernelMode, + FALSE, + NULL); + + ASSERT (NT_SUCCESS(WaitStatus)); +} + +/* Link Mcb to tail of Vcb->McbList queue */ + +VOID +Ext2LinkTailMcb(PEXT2_VCB Vcb, PEXT2_MCB Mcb) +{ + if (Mcb->Inode.i_ino == EXT2_ROOT_INO) { + return; + } + + ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); + + if (IsFlagOn(Mcb->Flags, MCB_VCB_LINK)) { + DEBUG(DL_RES, ( "Ext2LinkTailMcb: %wZ already linked.\n", + &Mcb->FullName)); + } else { + InsertTailList(&Vcb->McbList, &Mcb->Link); + SetLongFlag(Mcb->Flags, MCB_VCB_LINK); + Ext2ReferXcb(&Vcb->NumOfMcb); + } + + ExReleaseResourceLite(&Vcb->McbLock); +} + +/* Link Mcb to head of Vcb->McbList queue */ + +VOID +Ext2LinkHeadMcb(PEXT2_VCB Vcb, PEXT2_MCB Mcb) +{ + if (Mcb->Inode.i_ino == EXT2_ROOT_INO) { + return; + } + + ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); + + if (!IsFlagOn(Mcb->Flags, MCB_VCB_LINK)) { + InsertHeadList(&Vcb->McbList, &Mcb->Link); + SetLongFlag(Mcb->Flags, MCB_VCB_LINK); + Ext2ReferXcb(&Vcb->NumOfMcb); + } else { + DEBUG(DL_RES, ( "Ext2LinkHeadMcb: %wZ already linked.\n", + &Mcb->FullName)); + } + ExReleaseResourceLite(&Vcb->McbLock); +} + +/* Unlink Mcb from Vcb->McbList queue */ + +VOID +Ext2UnlinkMcb(PEXT2_VCB Vcb, PEXT2_MCB Mcb) +{ + if (Mcb->Inode.i_ino == EXT2_ROOT_INO) { + return; + } + + ExAcquireResourceExclusiveLite(&Vcb->McbLock, TRUE); + + if (IsFlagOn(Mcb->Flags, MCB_VCB_LINK)) { + RemoveEntryList(&(Mcb->Link)); + ClearLongFlag(Mcb->Flags, MCB_VCB_LINK); + Ext2DerefXcb(&Vcb->NumOfMcb); + } else { + DEBUG(DL_RES, ( "Ext2UnlinkMcb: %wZ already unlinked.\n", + &Mcb->FullName)); + } + ExReleaseResourceLite(&Vcb->McbLock); +} + +/* get the first Mcb record in Vcb->McbList */ + +PEXT2_MCB +Ext2FirstUnusedMcb(PEXT2_VCB Vcb, BOOLEAN Wait, ULONG Number) +{ + PEXT2_MCB Head = NULL; + PEXT2_MCB Tail = NULL; + PEXT2_MCB Mcb = NULL; + PLIST_ENTRY List = NULL; + ULONG i = 0; + LARGE_INTEGER start, now; + + if (!ExAcquireResourceExclusiveLite(&Vcb->McbLock, Wait)) { + return NULL; + } + + KeQuerySystemTime(&start); + + while (Number--) { + + BOOLEAN Skip = TRUE; + + if (IsListEmpty(&Vcb->McbList)) { + break; + } + + while (i++ < Vcb->NumOfMcb) { + + KeQuerySystemTime(&now); + if (now.QuadPart > start.QuadPart + (LONGLONG)10*1000*1000) { + break; + } + + List = RemoveHeadList(&Vcb->McbList); + Mcb = CONTAINING_RECORD(List, EXT2_MCB, Link); + ASSERT(IsFlagOn(Mcb->Flags, MCB_VCB_LINK)); + + if (Mcb->Fcb == NULL && !IsMcbRoot(Mcb) && + Mcb->Refercount == 0 && + (Mcb->Child == NULL || IsMcbSymLink(Mcb))) { + + Ext2RemoveMcb(Vcb, Mcb); + ClearLongFlag(Mcb->Flags, MCB_VCB_LINK); + Ext2DerefXcb(&Vcb->NumOfMcb); + + /* attach all Mcb into a chain*/ + if (Head) { + ASSERT(Tail != NULL); + Tail->Next = Mcb; + Tail = Mcb; + } else { + Head = Tail = Mcb; + } + Tail->Next = NULL; + Skip = FALSE; + + } else { + + InsertTailList(&Vcb->McbList, &Mcb->Link); + Mcb = NULL; + } + } + + if (Skip) + break; + } + + ExReleaseResourceLite(&Vcb->McbLock); + + return Head; +} + + +/* Reaper thread to release unused Mcb blocks */ +VOID +Ext2McbReaperThread( + PVOID Context +) +{ + PEXT2_REAPER Reaper = Context; + PLIST_ENTRY List = NULL; + LARGE_INTEGER Timeout; + + PEXT2_VCB Vcb = NULL; + PEXT2_MCB Mcb = NULL; + + ULONG i, NumOfMcbs; + + BOOLEAN GlobalAcquired = FALSE; + + BOOLEAN DidNothing = TRUE; + BOOLEAN LastState = TRUE; + BOOLEAN WaitLock; + + __try { + + Reaper->Thread = PsGetCurrentThread(); + + /* wake up DirverEntry */ + KeSetEvent(&Reaper->Engine, 0, FALSE); + + /* now process looping */ + while (!IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) { + + WaitLock = FALSE; + + /* calculate how long we need wait */ + if (Ext2Global->PerfStat.Current.Mcb > (((ULONG)Ext2Global->MaxDepth) * 128)) { + Timeout.QuadPart = (LONGLONG)-1000*1000; /* 0.1 second */ + NumOfMcbs = Ext2Global->MaxDepth * 4; + WaitLock = TRUE; + } else if (Ext2Global->PerfStat.Current.Mcb > (((ULONG)Ext2Global->MaxDepth) * 32)) { + Timeout.QuadPart = (LONGLONG)-1000*1000*5; /* 0.5 second */ + NumOfMcbs = Ext2Global->MaxDepth * 2; + WaitLock = TRUE; + } else if (Ext2Global->PerfStat.Current.Mcb > (((ULONG)Ext2Global->MaxDepth) * 8)) { + Timeout.QuadPart = (LONGLONG)-1000*1000*10; /* 1 second */ + NumOfMcbs = Ext2Global->MaxDepth; + } else if (Ext2Global->PerfStat.Current.Mcb > (((ULONG)Ext2Global->MaxDepth) * 2)) { + Timeout.QuadPart = (LONGLONG)-2*1000*1000*10; /* 2 second */ + NumOfMcbs = Ext2Global->MaxDepth / 4; + } else if (Ext2Global->PerfStat.Current.Mcb > (ULONG)Ext2Global->MaxDepth) { + Timeout.QuadPart = (LONGLONG)-4*1000*1000*10; /* 4 seconds */ + NumOfMcbs = Ext2Global->MaxDepth / 8; + } else if (DidNothing) { + Timeout.QuadPart = (LONGLONG)-8*1000*1000*10; /* 8 seconds */ + if (LastState) { + Timeout.QuadPart *= 2; + } + NumOfMcbs = Ext2Global->MaxDepth / 16; + } else { + Timeout.QuadPart = (LONGLONG)-5*1000*1000*10; /* 5 seconds */ + if (LastState) { + Timeout.QuadPart *= 2; + } + NumOfMcbs = Ext2Global->MaxDepth / 32; + } + + if (NumOfMcbs == 0) + NumOfMcbs = 1; + + LastState = DidNothing; + + /* wait until it is waken or it times out */ + KeWaitForSingleObject( + &Reaper->Wait, + Executive, + KernelMode, + FALSE, + &Timeout + ); + + if (IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) + break; + + DidNothing = TRUE; + + /* acquire global exclusive lock */ + if (!ExAcquireResourceSharedLite(&Ext2Global->Resource, WaitLock)) { + continue; + } + GlobalAcquired = TRUE; + + /* search all Vcb to get unused resources freed to system */ + for (List = Ext2Global->VcbList.Flink; + List != &(Ext2Global->VcbList); + List = List->Flink ) { + + Vcb = CONTAINING_RECORD(List, EXT2_VCB, Next); + + Mcb = Ext2FirstUnusedMcb(Vcb, WaitLock, NumOfMcbs); + while (Mcb) { + PEXT2_MCB Next = Mcb->Next; + DEBUG(DL_RES, ( "Ext2ReaperThread: releasing Mcb (%p): %wZ" + " Total: %xh\n", Mcb, &Mcb->FullName, + Ext2Global->PerfStat.Current.Mcb)); + Ext2FreeMcb(Vcb, Mcb); + Mcb = Next; + LastState = DidNothing = FALSE; + } + } + if (DidNothing) { + KeClearEvent(&Reaper->Wait); + } + if (GlobalAcquired) { + ExReleaseResourceLite(&Ext2Global->Resource); + GlobalAcquired = FALSE; + } + } + + } __finally { + + if (GlobalAcquired) { + ExReleaseResourceLite(&Ext2Global->Resource); + } + + KeSetEvent(&Reaper->Engine, 0, FALSE); + } + + PsTerminateSystemThread(STATUS_SUCCESS); +} + + +/* get buffer heads from global Vcb BH list */ + +BOOLEAN +Ext2QueryUnusedBH(PEXT2_VCB Vcb, PLIST_ENTRY head) +{ + struct buffer_head *bh = NULL; + PLIST_ENTRY next = NULL; + LARGE_INTEGER start, now; + BOOLEAN wake = FALSE; + + KeQuerySystemTime(&start); + + ExAcquireResourceExclusiveLite(&Vcb->bd.bd_bh_lock, TRUE); + + while (!IsListEmpty(&Vcb->bd.bd_bh_free)) { + + KeQuerySystemTime(&now); + if (now.QuadPart > start.QuadPart + (LONGLONG)10*1000*1000) { + break; + } + + next = RemoveHeadList(&Vcb->bd.bd_bh_free); + bh = CONTAINING_RECORD(next, struct buffer_head, b_link); + if (atomic_read(&bh->b_count)) { + InitializeListHead(&bh->b_link); + /* to be inserted by brelse */ + continue; + } + + if ( IsFlagOn(Vcb->Flags, VCB_BEING_DROPPED) || + (bh->b_ts_drop.QuadPart + (LONGLONG)10*1000*1000*15) > now.QuadPart || + (bh->b_ts_creat.QuadPart + (LONGLONG)10*1000*1000*180) > now.QuadPart) { + InsertTailList(head, &bh->b_link); + buffer_head_remove(&Vcb->bd, bh); + } else { + InsertHeadList(&Vcb->bd.bd_bh_free, &bh->b_link); + break; + } + } + + wake = IsListEmpty(&Vcb->bd.bd_bh_free); + ExReleaseResourceLite(&Vcb->bd.bd_bh_lock); + + if (wake) + KeSetEvent(&Vcb->bd.bd_bh_notify, 0, FALSE); + + return IsFlagOn(Vcb->Flags, VCB_BEING_DROPPED); +} + + +/* Reaper thread to release unused buffer heads */ +VOID +Ext2bhReaperThread( + PVOID Context +) +{ + PEXT2_REAPER Reaper = Context; + PEXT2_VCB Vcb = NULL; + LIST_ENTRY List, *Link; + LARGE_INTEGER Timeout; + + BOOLEAN GlobalAcquired = FALSE; + BOOLEAN DidNothing = FALSE; + BOOLEAN NonWait = FALSE; + + __try { + + Reaper->Thread = PsGetCurrentThread(); + + /* wake up DirverEntry */ + KeSetEvent(&Reaper->Engine, 0, FALSE); + + /* now process looping */ + while (!IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) { + + /* wait until it is waken or it times out */ + if (NonWait) { + Timeout.QuadPart = (LONGLONG)-10*1000*10; + NonWait = FALSE; + } else if (DidNothing) { + Timeout.QuadPart = Timeout.QuadPart * 2; + } else { + Timeout.QuadPart = (LONGLONG)-10*1000*1000*10; /* 10 seconds */ + } + KeWaitForSingleObject( + &Reaper->Wait, + Executive, + KernelMode, + FALSE, + &Timeout + ); + + if (IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) + break; + + InitializeListHead(&List); + + /* acquire global exclusive lock */ + ExAcquireResourceSharedLite(&Ext2Global->Resource, TRUE); + GlobalAcquired = TRUE; + /* search all Vcb to get unused resources freed to system */ + for (Link = Ext2Global->VcbList.Flink; + Link != &(Ext2Global->VcbList); + Link = Link->Flink ) { + + Vcb = CONTAINING_RECORD(Link, EXT2_VCB, Next); + NonWait = Ext2QueryUnusedBH(Vcb, &List); + } + DidNothing = IsListEmpty(&List); + if (DidNothing) { + KeClearEvent(&Reaper->Wait); + } + if (GlobalAcquired) { + ExReleaseResourceLite(&Ext2Global->Resource); + GlobalAcquired = FALSE; + } + + while (!IsListEmpty(&List)) { + struct buffer_head *bh; + Link = RemoveHeadList(&List); + bh = CONTAINING_RECORD(Link, struct buffer_head, b_link); + ASSERT(0 == atomic_read(&bh->b_count)); + free_buffer_head(bh); + } + } + + } __finally { + + if (GlobalAcquired) { + ExReleaseResourceLite(&Ext2Global->Resource); + } + + KeSetEvent(&Reaper->Engine, 0, FALSE); + } + + PsTerminateSystemThread(STATUS_SUCCESS); +} + +/* get unused Fcbs to free */ + +BOOLEAN +Ext2QueryUnusedFcb(PEXT2_VCB Vcb, PLIST_ENTRY list) +{ + PEXT2_FCB Fcb; + PLIST_ENTRY next = NULL; + LARGE_INTEGER start, now; + + ULONG count = 0; + ULONG tries = 0; + BOOLEAN wake = FALSE; + BOOLEAN retry = TRUE; + + KeQuerySystemTime(&start); + + ExAcquireResourceExclusiveLite(&Vcb->FcbLock, TRUE); + +again: + + KeQuerySystemTime(&now); + while (!IsListEmpty(&Vcb->FcbList)) { + + next = RemoveHeadList(&Vcb->FcbList); + Fcb = CONTAINING_RECORD(next, EXT2_FCB, Next); + + if (Fcb->ReferenceCount > 0) { + InsertTailList(&Vcb->FcbList, &Fcb->Next); + break; + } + + retry = FALSE; + + if (now.QuadPart < Fcb->TsDrop.QuadPart + 10*1000*1000*120) { + InsertHeadList(&Vcb->FcbList, &Fcb->Next); + break; + } + + Ext2UnlinkFcb(Fcb); + Ext2DerefXcb(&Vcb->FcbCount); + InsertTailList(list, &Fcb->Next); + if (++count >= Ext2Global->MaxDepth) { + break; + } + } + + if (start.QuadPart + 10*1000*1000 > now.QuadPart) { + retry = FALSE; + } + + if (retry) { + if (++tries < (Vcb->FcbCount >> 4) ) + goto again; + } + + ExReleaseResourceLite(&Vcb->FcbLock); + + return 0; +} + +/* Reaper thread to release Fcb */ +VOID +Ext2FcbReaperThread( + PVOID Context +) +{ + PEXT2_REAPER Reaper = Context; + PEXT2_VCB Vcb = NULL; + LIST_ENTRY List, *Link; + LARGE_INTEGER Timeout; + + BOOLEAN GlobalAcquired = FALSE; + BOOLEAN DidNothing = FALSE; + BOOLEAN NonWait = FALSE; + + __try { + + Reaper->Thread = PsGetCurrentThread(); + + /* wake up DirverEntry */ + KeSetEvent(&Reaper->Engine, 0, FALSE); + + /* now process looping */ + while (!IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) { + + /* wait until it is waken or it times out */ + if (NonWait) { + Timeout.QuadPart = (LONGLONG)-10*1000*100; + NonWait = FALSE; + } else if (DidNothing) { + Timeout.QuadPart = Timeout.QuadPart * 2; + } else { + Timeout.QuadPart = (LONGLONG)-10*1000*1000*20; /* 20 seconds */ + } + KeWaitForSingleObject( + &Reaper->Wait, + Executive, + KernelMode, + FALSE, + &Timeout + ); + + if (IsFlagOn(Reaper->Flags, EXT2_REAPER_FLAG_STOP)) + break; + + InitializeListHead(&List); + + /* acquire global exclusive lock */ + ExAcquireResourceSharedLite(&Ext2Global->Resource, TRUE); + GlobalAcquired = TRUE; + /* search all Vcb to get unused resources freed to system */ + for (Link = Ext2Global->VcbList.Flink; + Link != &(Ext2Global->VcbList); + Link = Link->Flink ) { + + Vcb = CONTAINING_RECORD(Link, EXT2_VCB, Next); + NonWait = Ext2QueryUnusedFcb(Vcb, &List); + } + DidNothing = IsListEmpty(&List); + if (DidNothing) { + KeClearEvent(&Reaper->Wait); + } + if (GlobalAcquired) { + ExReleaseResourceLite(&Ext2Global->Resource); + GlobalAcquired = FALSE; + } + + while (!IsListEmpty(&List)) { + PEXT2_FCB Fcb; + Link = RemoveHeadList(&List); + Fcb = CONTAINING_RECORD(Link, EXT2_FCB, Next); + ASSERT(0 == Fcb->ReferenceCount); + Ext2FreeFcb(Fcb); + } + } + + } __finally { + + if (GlobalAcquired) { + ExReleaseResourceLite(&Ext2Global->Resource); + } + + KeSetEvent(&Reaper->Engine, 0, FALSE); + } + + PsTerminateSystemThread(STATUS_SUCCESS); +} + +NTSTATUS +Ext2StartReaper(PEXT2_REAPER Reaper, EXT2_REAPER_RELEASE Free) +{ + NTSTATUS status = STATUS_SUCCESS; + OBJECT_ATTRIBUTES oa; + HANDLE handle = 0; + LARGE_INTEGER timeout; + + Reaper->Free = Free; + + /* initialize wait event */ + KeInitializeEvent( + &Reaper->Wait, + SynchronizationEvent, FALSE + ); + + /* Reaper thread engine event */ + KeInitializeEvent( + &Reaper->Engine, + SynchronizationEvent, FALSE + ); + + /* initialize oa */ + InitializeObjectAttributes( + &oa, + NULL, + OBJ_CASE_INSENSITIVE | + OBJ_KERNEL_HANDLE, + NULL, + NULL + ); + + /* start a new system thread */ + status = PsCreateSystemThread( + &handle, + 0, + &oa, + NULL, + NULL, + Free, + (PVOID)Reaper + ); + + if (NT_SUCCESS(status)) { + ZwClose(handle); + + /* make sure Reaperthread is started */ + timeout.QuadPart = (LONGLONG)-10*1000*1000*2; /* 2 seconds */ + status = KeWaitForSingleObject( + &Reaper->Engine, + Executive, + KernelMode, + FALSE, + &timeout + ); + if (status != STATUS_SUCCESS) { + status = STATUS_INSUFFICIENT_RESOURCES; + } + } + + return status; +} + + +VOID +Ext2StopReaper(PEXT2_REAPER Reaper) +{ + LARGE_INTEGER timeout; + + Reaper->Flags |= EXT2_REAPER_FLAG_STOP; + KeSetEvent(&Reaper->Wait, 0, FALSE); + + /* make sure Reaperthread is started */ + timeout.QuadPart = (LONGLONG)-10*1000*1000*2; /* 2 seconds */ + KeWaitForSingleObject( + &Reaper->Engine, + Executive, + KernelMode, + FALSE, + &timeout); +}