/* * PROJECT: Mke2fs * FILE: Inode.c * PROGRAMMER: Matt Wu * HOMEPAGE: http://ext2.yeah.net */ /* INCLUDES **************************************************************/ #include "Mke2fs.h" #include /* 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; }