in lsvmutils/ext2.c [3804:4060]
EXT2Err EXT2Rm(
EXT2* ext2,
const char* path)
{
EXT2_DECLARE_ERR(err);
char dirname[EXT2_PATH_MAX];
char filename[EXT2_PATH_MAX];
void* blocks = NULL;
UINT32 blocks_size = 0;
void* new_blocks = NULL;
UINT32 new_blocks_size = 0;
BufU32 blknos = BUF_U32_INITIALIZER;
EXT2Ino ino;
EXT2Inode inode;
const EXT2DirEntry* ent = NULL;
#if !defined(BUILD_EFI)
(void)_DumpDirectoryEntries;
#endif /* !defined(BUILD_EFI) */
/* Check parameters */
if (!EXT2Valid(ext2) && !path)
{
err = EXT2_ERR_INVALID_PARAMETER;
GOTO(done);
}
/* Truncate the file first */
if (EXT2_IFERR(err = EXT2Trunc(ext2, path)))
{
GOTO(done);
}
/* Split the path */
if (EXT2_IFERR(err = _SplitFullPath(path, dirname, filename)))
{
GOTO(done);
}
/* Load the directory inode */
if (EXT2_IFERR(err = EXT2PathToInode(ext2, dirname, &ino, &inode)))
{
GOTO(done);
}
/* Load the directory file */
if (EXT2_IFERR(err = EXT2LoadFileFromInode(
ext2,
&inode,
&blocks,
&blocks_size)))
{
GOTO(done);
}
/* Load the block numbers (including the block blocks) */
if (EXT2_IFERR(err = _LoadBlockNumbersFromInode(
ext2,
&inode,
1, /* include_block_blocks */
&blknos)))
{
GOTO(done);
}
/* Find 'filename' within this directory */
if (!(ent = _FindDirectoryEntry(filename, blocks, blocks_size)))
{
GOTO(done);
}
/* Allow removal of empty directories only */
if (ent->file_type == EXT2_FT_DIR)
{
EXT2Ino dir_ino;
EXT2Inode dir_inode;
UINT32 count;
/* Find the inode of the filename */
if (EXT2_IFERR(err = EXT2PathToInode(
ext2, filename, &dir_ino, &dir_inode)))
{
GOTO(done);
}
/* Disallow removal if directory is non empty */
if (EXT2_IFERR(err = _CountDirectoryEntriesIno(ext2, dir_ino, &count)))
{
GOTO(done);
}
/* Expect just "." and ".." entries */
if (count != 2)
{
GOTO(done);
}
}
/* Convert from 'indexed' to 'linked list' directory format */
{
new_blocks_size = blocks_size;
const char* src = (const char*)blocks;
const char* src_end = (const char*)blocks + inode.i_size;
char* dest = NULL;
/* Allocate a buffer to hold 'linked list' directory */
if (!(new_blocks = Calloc(new_blocks_size, 1)))
{
GOTO(done);
}
/* Set current and end pointers to new buffer */
dest = (char*)new_blocks;
/* Copy over directory entries (skipping removed entry) */
{
EXT2DirEntry* prev = NULL;
while (src < src_end)
{
const EXT2DirEntry* curr_ent =
(const EXT2DirEntry*)src;
UINT32 rec_len;
UINT32 offset;
/* Skip the removed directory entry */
if (curr_ent == ent || !ent->name)
{
src += curr_ent->rec_len;
continue;
}
/* Compute size of the new directory entry */
rec_len = sizeof(*curr_ent) - EXT2_PATH_MAX + curr_ent->name_len;
rec_len = _NextMult(rec_len, 4);
/* Compute byte offset into current block */
offset = (dest - (char*)new_blocks) % ext2->block_size;
/* If new entry would overflow the block */
if (offset + rec_len > ext2->block_size)
{
UINT32 rem = ext2->block_size - offset;
if (!prev)
{
GOTO(done);
}
/* Adjust previous entry to point to next block */
prev->rec_len += rem;
dest += rem;
}
/* Copy this entry into new buffer */
{
EXT2DirEntry* new_ent =
(EXT2DirEntry*)dest;
Memset(new_ent, 0, rec_len);
Memcpy(new_ent, curr_ent,
sizeof(*curr_ent) + curr_ent->name_len);
new_ent->rec_len = rec_len;
prev = new_ent;
dest += rec_len;
}
src += curr_ent->rec_len;
}
/* Set final entry to point to end of the block */
if (prev)
{
UINT32 offset;
UINT32 rem;
/* Compute byte offset into current block */
offset = (dest - (char*)new_blocks) % ext2->block_size;
/* Compute remaining bytes */
rem = ext2->block_size - offset;
/* Set record length of final entry to end of block */
prev->rec_len += rem;
/* Advance dest to block boundary */
dest += rem;
}
/* Size down the new blocks size */
new_blocks_size = (UINT32)(dest - (char*)new_blocks);
if (EXT2_IFERR(err = _CheckDirectoryEntries(
ext2,
new_blocks,
new_blocks_size)))
{
GOTO(done);
}
}
}
/* Count directory entries before and after */
{
UINT32 count;
UINT32 new_count;
if (EXT2_IFERR(err = _CountDirectoryEntries(
ext2,
blocks,
blocks_size,
&count)))
{
GOTO(done);
}
if (EXT2_IFERR(err = _CountDirectoryEntries(
ext2,
new_blocks,
new_blocks_size,
&new_count)))
{
GOTO(done);
}
}
/* Return all directory blocks to the free list */
if (EXT2_IFERR(err = _PutBlocks(ext2, blknos.data, blknos.size)))
{
GOTO(done);
}
/* Update the inode blocks */
if (EXT2_IFERR(err = _UpdateInodeDataBlocks(
ext2,
ino,
&inode,
new_blocks,
new_blocks_size,
1))) /* is_dir */
{
GOTO(done);
}
err = EXT2_ERR_NONE;
done:
if (blocks)
Free(blocks);
if (new_blocks)
Free(new_blocks);
BufU32Release(&blknos);
return err;
}