reactos/sdk/lib/fslib/ext2lib/Inode.c

695 lines
16 KiB
C

/*
* PROJECT: Mke2fs
* FILE: Inode.c
* PROGRAMMER: Matt Wu <mattwu@163.com>
* HOMEPAGE: http://ext2.yeah.net
*/
/* INCLUDES **************************************************************/
#include "Mke2fs.h"
#include <debug.h>
/* DEFINITIONS ***********************************************************/
extern char *device_name;
/* FUNCTIONS *************************************************************/
bool ext2_get_inode_lba(PEXT2_FILESYS Ext2Sys, ULONG no, LONGLONG *offset)
{
LONGLONG loc = 0;
PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
if (no < 1 || no > pExt2Sb->s_inodes_count)
{
DPRINT1("Mke2fs: Inode value %lu was out of range in load_inode.(1-%ld)\n",
no, pExt2Sb->s_inodes_count);
*offset = 0;
return false;
}
loc = (LONGLONG)(Ext2Sys->blocksize) * Ext2Sys->group_desc[(no - 1) / pExt2Sb->s_inodes_per_group].bg_inode_table +
((no - 1) % pExt2Sb->s_inodes_per_group) * sizeof(EXT2_INODE);
*offset = loc;
return true;
}
bool ext2_load_inode(PEXT2_FILESYS Ext2Sys, ULONG no, PEXT2_INODE pInode)
{
LONGLONG Offset;
bool bRet = false;
if (ext2_get_inode_lba(Ext2Sys, no, &Offset))
{
bRet = NT_SUCCESS(Ext2ReadDisk(
Ext2Sys,
Offset,
sizeof(EXT2_INODE),
(unsigned char *)pInode));
}
return bRet;
}
bool ext2_save_inode(PEXT2_FILESYS Ext2Sys, ULONG no, PEXT2_INODE pInode)
{
LONGLONG offset;
bool bRet = false;
if (ext2_get_inode_lba(Ext2Sys, no, &offset))
{
bRet = NT_SUCCESS(Ext2WriteDisk(
Ext2Sys,
offset,
sizeof(EXT2_INODE),
(unsigned char *)pInode));
}
return bRet;
}
/*
* Right now, just search forward from the parent directory's block
* group to find the next free inode.
*
* Should have a special policy for directories.
*/
bool ext2_new_inode(PEXT2_FILESYS fs, ULONG dir, int mode,
PEXT2_INODE_BITMAP map, ULONG *ret)
{
ULONG dir_group = 0;
ULONG i;
ULONG start_inode;
if (!map)
map = fs->inode_map;
if (!map)
return false;
if (dir > 0)
dir_group = (dir - 1) / EXT2_INODES_PER_GROUP(fs->ext2_sb);
start_inode = (dir_group * EXT2_INODES_PER_GROUP(fs->ext2_sb)) + 1;
if (start_inode < EXT2_FIRST_INODE(fs->ext2_sb))
start_inode = EXT2_FIRST_INODE(fs->ext2_sb);
i = start_inode;
do
{
if (!ext2_test_inode_bitmap(map, i))
break;
i++;
if (i > fs->ext2_sb->s_inodes_count)
i = EXT2_FIRST_INODE(fs->ext2_sb);
} while (i != start_inode);
if (ext2_test_inode_bitmap(map, i))
return false;
*ret = i;
return true;
}
bool ext2_expand_block( PEXT2_FILESYS Ext2Sys, PEXT2_INODE Inode,
ULONG dwContent, ULONG Index, int layer,
ULONG newBlk, ULONG *dwRet, ULONG *off )
{
ULONG *pData = NULL;
ULONG i = 0, j = 0, temp = 1;
ULONG dwBlk;
ULONG dwNewBlk = newBlk;
bool bDirty = false;
bool bRet = true;
ULONG Offset = 0;
PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
pData = (ULONG *)RtlAllocateHeap(RtlGetProcessHeap(), 0, Ext2Sys->blocksize);
if (!pData)
{
bRet = false;
goto errorout;
}
if (!ext2_read_block(Ext2Sys, dwContent, (void *)pData))
{
bRet = false;
goto errorout;
}
if (layer == 1)
{
*dwRet = dwContent;
*off = Index;
pData[Index] = newBlk;
bDirty = TRUE;
}
else if (layer <= 3)
{
temp = 1 << ((10 + pExt2Sb->s_log_block_size - 2) * (layer - 1));
i = Index / temp;
j = Index % temp;
dwBlk = pData[i];
if (dwBlk == 0)
{
if (ext2_alloc_block(Ext2Sys, 0, &dwBlk) )
{
pData[i] = dwBlk;
bDirty = true;
Inode->i_blocks += (Ext2Sys->blocksize / SECTOR_SIZE);
}
if (!bDirty)
goto errorout;
}
if (!ext2_expand_block(Ext2Sys, Inode, dwBlk, j, layer - 1, bDirty, &dwNewBlk, &Offset))
{
bRet = false;
DPRINT1("Mke2fs: ext2_expand_block: ... error recuise...\n");
goto errorout;
}
}
if (bDirty)
{
bRet = ext2_write_block(Ext2Sys, dwContent, (void *)pData);
}
errorout:
if (pData)
RtlFreeHeap(RtlGetProcessHeap(), 0, pData);
if (bRet && dwRet)
*dwRet = dwNewBlk;
return bRet;
}
bool ext2_expand_inode( PEXT2_FILESYS Ext2Sys,
PEXT2_INODE Inode,
ULONG newBlk )
{
ULONG dwSizes[4] = {12, 1, 1, 1};
ULONG Index = 0;
ULONG dwTotal = 0;
ULONG dwBlk = 0, dwNewBlk = 0, Offset = 0;
PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
int i = 0;
bool bRet = true;
ULONG TotalBlocks;
TotalBlocks = Inode->i_blocks / (Ext2Sys->blocksize / SECTOR_SIZE);
Index = Ext2DataBlocks(Ext2Sys, TotalBlocks);
for (i = 0; i < 4; i++)
{
dwSizes[i] = dwSizes[i] << ((10 + pExt2Sb->s_log_block_size - 2) * i);
dwTotal += dwSizes[i];
}
if (Index >= dwTotal)
{
DPRINT1("Mke2fs: ext2_expand_inode: beyond the maxinum size of an inode.\n");
return false;
}
for (i = 0; i < 4; i++)
{
if (Index < dwSizes[i])
{
if (i == 0)
{
Inode->i_block[Index] = newBlk;
}
else
{
dwBlk = Inode->i_block[(i + 12 - 1)];
if (dwBlk == 0)
{
if (ext2_alloc_block(Ext2Sys, 0, &dwBlk))
{
Inode->i_block[(i + 12 - 1)] = dwBlk;
Inode->i_blocks += (Ext2Sys->blocksize / SECTOR_SIZE);
}
else
{
break;
}
}
dwNewBlk = 0;
bRet = ext2_expand_block(
Ext2Sys,
Inode,
dwBlk,
Index,
i,
newBlk,
&dwNewBlk,
&Offset );
}
break;
}
Index -= dwSizes[i];
}
return bRet;
}
bool ext2_get_block(PEXT2_FILESYS Ext2Sys, ULONG dwContent, ULONG Index, int layer, ULONG *dwRet)
{
ULONG *pData = NULL;
LONGLONG Offset = 0;
ULONG i = 0, j = 0, temp = 1;
ULONG dwBlk = 0;
bool bRet = true;
PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
Offset = (LONGLONG) dwContent;
Offset = Offset * Ext2Sys->blocksize;
pData = (ULONG *)RtlAllocateHeap(RtlGetProcessHeap(), 0, Ext2Sys->blocksize);
if (!pData)
{
return false;
}
memset(pData, 0, Ext2Sys->blocksize);
if (layer == 0)
{
dwBlk = dwContent;
}
else if (layer <= 3)
{
if (!ext2_read_block(Ext2Sys, dwContent, (void *)pData))
{
bRet = false;
goto errorout;
}
temp = 1 << ((10 + pExt2Sb->s_log_block_size - 2) * (layer - 1));
i = Index / temp;
j = Index % temp;
if (!ext2_get_block(Ext2Sys, pData[i], j, layer - 1, &dwBlk))
{
bRet = false;
DPRINT1("Mke2fs: ext2_get_block: ... error recuise...\n");
goto errorout;
}
}
errorout:
if (pData)
RtlFreeHeap(RtlGetProcessHeap(), 0, pData);
if (bRet && dwRet)
*dwRet = dwBlk;
return bRet;
}
bool ext2_block_map(PEXT2_FILESYS Ext2Sys, PEXT2_INODE inode, ULONG block, ULONG *dwRet)
{
ULONG dwSizes[4] = { 12, 1, 1, 1 };
ULONG Index = 0;
ULONG dwBlk = 0;
PEXT2_SUPER_BLOCK pExt2Sb = Ext2Sys->ext2_sb;
UINT i;
bool bRet = false;
Index = block;
for (i = 0; i < 4; i++)
{
dwSizes[i] = dwSizes[i] << ((10 + pExt2Sb->s_log_block_size - 2) * i);
}
if (Index >= inode->i_blocks / (Ext2Sys->blocksize / SECTOR_SIZE))
{
DPRINT1("Mke2fs: ext2_block_map: beyond the size of the inode.\n");
return false;
}
for (i = 0; i < 4; i++)
{
if (Index < dwSizes[i])
{
dwBlk = inode->i_block[i==0 ? (Index):(i + 12 - 1)];
bRet = ext2_get_block(Ext2Sys, dwBlk, Index , i, &dwBlk);
break;
}
Index -= dwSizes[i];
}
if (bRet && dwBlk)
{
if (dwRet)
*dwRet = dwBlk;
return true;
}
else
return false;
}
ULONG ext2_build_bdl(PEXT2_FILESYS Ext2Sys,
PEXT2_INODE ext2_inode,
IN ULONG offset,
IN ULONG size,
OUT PEXT2_BDL *ext2_bdl )
{
ULONG nBeg, nEnd, nBlocks;
ULONG dwBlk;
ULONG i;
ULONG dwBytes = 0;
LONGLONG lba;
PEXT2_BDL ext2bdl = NULL;
*ext2_bdl = NULL;
if (offset >= ext2_inode->i_size)
{
DPRINT1("Mke2fs: ext2_build_bdl: beyond the file range.\n");
return 0;
}
/*
if (offset + size > ext2_inode->i_size)
{
size = ext2_inode->i_size - offset;
}
*/
nBeg = offset / Ext2Sys->blocksize;
nEnd = (size + offset + Ext2Sys->blocksize - 1) / Ext2Sys->blocksize;
nBlocks = nEnd - nBeg;
if (nBlocks > 0)
{
ext2bdl = (PEXT2_BDL)
RtlAllocateHeap(RtlGetProcessHeap(), 0, sizeof(EXT2_BDL) * nBlocks);
if (ext2bdl)
{
memset(ext2bdl, 0, sizeof(EXT2_BDL) * nBlocks);
for (i = nBeg; i < nEnd; i++)
{
if (!ext2_block_map(Ext2Sys, ext2_inode, i, &dwBlk))
{
goto fail;
}
lba = (LONGLONG) dwBlk;
lba = lba * Ext2Sys->blocksize;
if (nBlocks == 1) // ie. (nBeg == nEnd - 1)
{
dwBytes = size;
ext2bdl[i - nBeg].Lba = lba + (LONGLONG)(offset % (Ext2Sys->blocksize));
ext2bdl[i - nBeg].Length = dwBytes;
ext2bdl[i - nBeg].Offset = 0;
}
else
{
if (i == nBeg)
{
dwBytes = Ext2Sys->blocksize - (offset % (Ext2Sys->blocksize));
ext2bdl[i - nBeg].Lba = lba + (LONGLONG)(offset % (Ext2Sys->blocksize));
ext2bdl[i - nBeg].Length = dwBytes;
ext2bdl[i - nBeg].Offset = 0;
}
else if (i == nEnd - 1)
{
ext2bdl[i - nBeg].Lba = lba;
ext2bdl[i - nBeg].Length = size - dwBytes;
ext2bdl[i - nBeg].Offset = dwBytes;
dwBytes = size;
}
else
{
ext2bdl[i - nBeg].Lba = lba;
ext2bdl[i - nBeg].Length = Ext2Sys->blocksize;
ext2bdl[i - nBeg].Offset = dwBytes;
dwBytes += Ext2Sys->blocksize;
}
}
}
*ext2_bdl = ext2bdl;
return nBlocks;
}
}
fail:
if (ext2bdl)
RtlFreeHeap(RtlGetProcessHeap(), 0, ext2bdl);
// Error
return 0;
}
bool ext2_read_inode(PEXT2_FILESYS Ext2Sys,
ULONG ino,
ULONG offset,
PVOID Buffer,
ULONG size,
PULONG dwReturn)
{
PEXT2_BDL ext2_bdl = NULL;
ULONG blocks, i;
bool bRet = true;
EXT2_INODE ext2_inode;
ULONG dwTotal = 0;
if (!ext2_load_inode(Ext2Sys, ino, &ext2_inode))
{
return false;
}
blocks = ext2_build_bdl(Ext2Sys, &ext2_inode, offset, size, &ext2_bdl);
if (blocks <= 0)
return false;
for(i = 0; i < blocks; i++)
{
bRet = NT_SUCCESS(Ext2ReadDisk(
Ext2Sys,
ext2_bdl[i].Lba,
ext2_bdl[i].Length,
(PUCHAR)Buffer + ext2_bdl[i].Offset
));
if (!bRet)
break;
dwTotal += ext2_bdl[i].Length;
}
*dwReturn = dwTotal;
if (ext2_bdl)
RtlFreeHeap(RtlGetProcessHeap(), 0, ext2_bdl);
return bRet;
}
bool ext2_write_inode (PEXT2_FILESYS Ext2Sys,
ULONG ino,
ULONG offset,
PVOID Buffer,
ULONG size,
PULONG dwReturn )
{
PEXT2_BDL ext2_bdl = NULL;
ULONG blocks, i;
bool bRet = true;
EXT2_INODE inode;
ULONG dwTotal = 0;
ULONG dwBlk = 0;
ULONG TotalBlks;
blocks = (size + offset + Ext2Sys->blocksize - 1) / Ext2Sys->blocksize;
if (!ext2_load_inode(Ext2Sys, ino, &inode))
{
return false;
}
TotalBlks = inode.i_blocks / (Ext2Sys->blocksize / SECTOR_SIZE);
TotalBlks = Ext2DataBlocks(Ext2Sys, TotalBlks);
if (blocks > TotalBlks)
{
for (i=0; i < (blocks - TotalBlks); i++)
{
if (ext2_alloc_block(Ext2Sys, 0, &dwBlk) )
{
ext2_expand_inode(Ext2Sys, &inode, dwBlk);
inode.i_blocks += (Ext2Sys->blocksize/SECTOR_SIZE);
}
}
}
blocks = ext2_build_bdl(Ext2Sys, &inode, offset, size, &ext2_bdl);
if (blocks <= 0)
return false;
for(i = 0; i < blocks; i++)
{
bRet = NT_SUCCESS(Ext2WriteDisk(
Ext2Sys,
ext2_bdl[i].Lba,
ext2_bdl[i].Length,
(PUCHAR)Buffer + ext2_bdl[i].Offset
));
if (!bRet)
{
goto errorout;
}
dwTotal += ext2_bdl[i].Length;
}
*dwReturn = dwTotal;
if (size + offset > inode.i_size)
{
inode.i_size = size + offset;
}
ext2_save_inode(Ext2Sys, ino, &inode);
errorout:
if (ext2_bdl)
RtlFreeHeap(RtlGetProcessHeap(), 0, ext2_bdl);
return bRet;
}
bool
ext2_add_entry( PEXT2_FILESYS Ext2Sys,
ULONG parent, ULONG inode,
int filetype, char *name )
{
PEXT2_DIR_ENTRY2 dir = NULL, newdir = NULL;
EXT2_INODE parent_inode;
ULONG dwRet;
char *buf;
int rec_len;
bool bRet = false;
rec_len = EXT2_DIR_REC_LEN(strlen(name));
if (!ext2_load_inode(Ext2Sys, parent, &parent_inode))
{
return false;
}
buf = (char *)RtlAllocateHeap(RtlGetProcessHeap(), 0, parent_inode.i_size);
if (!ext2_read_inode(Ext2Sys, parent, 0, buf, parent_inode.i_size, &dwRet))
{
return false;
}
dir = (PEXT2_DIR_ENTRY2) buf;
while ((char *)dir < buf + parent_inode.i_size)
{
if ((dir->inode == 0 && dir->rec_len >= rec_len) ||
(dir->rec_len >= dir->name_len + rec_len) )
{
if (dir->inode)
{
newdir = (PEXT2_DIR_ENTRY2) ((PUCHAR)dir + EXT2_DIR_REC_LEN(dir->name_len));
newdir->rec_len = dir->rec_len - EXT2_DIR_REC_LEN(dir->name_len);
dir->rec_len = EXT2_DIR_REC_LEN(dir->name_len);
dir = newdir;
}
dir->file_type = filetype;
dir->inode = inode;
dir->name_len = strlen(name);
memcpy(dir->name, name, strlen(name));
bRet = true;
break;
}
dir = (PEXT2_DIR_ENTRY2) (dir->rec_len + (PUCHAR) dir);
}
if (bRet)
return ext2_write_inode(Ext2Sys, parent, 0, buf, parent_inode.i_size, &dwRet);
return bRet;
}
bool ext2_reserve_inodes(PEXT2_FILESYS fs)
{
ULONG i;
int group;
for (i = EXT2_ROOT_INO + 1; i < EXT2_FIRST_INODE(fs->ext2_sb); i++)
{
ext2_mark_inode_bitmap(fs->inode_map, i);
group = ext2_group_of_ino(fs, i);
fs->group_desc[group].bg_free_inodes_count--;
fs->ext2_sb->s_free_inodes_count--;
}
return true;
}