hos/kernel/fs/ext2/Ext2OpenDirectory.cpp

227 lines
6.3 KiB
C++

// Ext2OpenDirectory.cpp
// Author: Josh Holtrop
// Date: 12/26/05
// Modified: 12/26/05
extern "C" {
#include "display/kout.h"
#include "lang/lang.h"
}
#include "fs/vfs.h"
#include "Ext2OpenDirectory.h"
#include "Ext2OpenFile.h"
#include "ext2.h"
Ext2OpenDirectory::Ext2OpenDirectory(Ext2fs *fs, u32_t inum, int mode)
{
myPosition = 0;
myFS = fs;
myInum = inum;
myMode = mode;
myCache = new Ext2BlockCache(fs, inum);
fs->readInode(inum, &myInode);
u32_t size = myInode.i_size;
u32_t logBlockSize = fs->getSuper()->s_log_block_size;
myBlockSize = 1024 << logBlockSize;
if (size % myBlockSize)
size += myBlockSize - (size % myBlockSize);
myBlocks = size >> (10 + logBlockSize);
myBuffer = new u8_t[size];
if ((mode & VFS_MODE_RW_MASK) == VFS_MODE_READ)
readDirectory();
}
/* destroy an open ext2 directory object */
Ext2OpenDirectory::~Ext2OpenDirectory()
{
delete myCache;
if ((myMode & VFS_MODE_RW_MASK) == VFS_MODE_READ)
delete[] myBuffer;
}
void Ext2OpenDirectory::readDirectory()
{
/* read in the entire directory */
for (u32_t i = 0; i < myBlocks; i++)
myCache->readBlock(i, myBuffer + i * myBlockSize);
}
/* seek absolute or relative, only positive direction */
int Ext2OpenDirectory::seek(int pos, int mode)
{
if (mode == SEEK_ABSOLUTE)
myPosition = 0;
if (pos > 0)
{
while (myPosition < myInode.i_size)
{
ext2_dir_entry_t *de =
(ext2_dir_entry_t *) (myBuffer + myPosition);
myPosition += de->length;
pos--;
if (pos < 1)
return 0;
}
return -1;
}
return -2;
}
/* read a directory entry from the directory */
int Ext2OpenDirectory::read(vfs_dir_entry_t *ent)
{
if ((myMode & VFS_MODE_RW_MASK) != VFS_MODE_READ)
return -1;
if (myPosition >= myInode.i_size)
return EOF;
ext2_dir_entry_t *de = (ext2_dir_entry_t *) (myBuffer + myPosition);
ent->inum = de->inode;
memcpy(ent->name, de->name, de->name_length);
ent->name[de->name_length] = 0;
myPosition += de->length;
return 0;
}
int Ext2OpenDirectory::create(char *name, int mode, u32_t permissions, u32_t dev)
{
if ((myMode & VFS_MODE_RW_MASK) != VFS_MODE_WRITE)
return -1;
readDirectory();
myPosition = 0;
u32_t name_length = strlen(name);
while (myPosition < myInode.i_size)
{
ext2_dir_entry_t *de =
(ext2_dir_entry_t *) (myBuffer + myPosition);
if (name_length == de->name_length && !strncmp(name, de->name, name_length))
return -2; /* entry already exists */
myPosition += de->length;
}
u32_t newInode = myFS->allocInode();
if (newInode == 0)
return -3; /* no free inodes! */
/* initialize the inode */
ext2_inode_t in;
memset(&in, 0, sizeof(ext2_inode_t));
u32_t ext2_mode;
switch(mode)
{
case VFS_FT_DIR: ext2_mode = EXT2_I_MODE_DIR; break;
case VFS_FT_CHAR: ext2_mode = EXT2_I_MODE_CHAR;
in.i_block[0] = dev; break;
case VFS_FT_BLOCK: ext2_mode = EXT2_I_MODE_BLOCK;
in.i_block[0] = dev; break;
case VFS_FT_FIFO: ext2_mode = EXT2_I_MODE_FIFO; break;
case VFS_FT_SOCK: ext2_mode = EXT2_I_MODE_SOCK; break;
case VFS_FT_SYMLINK: ext2_mode = EXT2_I_MODE_SYM; break;
case VFS_FT_FILE:
default: ext2_mode = EXT2_I_MODE_FILE; break;
}
in.i_mode = ext2_mode | (permissions & EXT2_I_MODE_ATTR_MASK);
in.i_links_count = 1;
myFS->writeInode(newInode, &in);
u32_t entryLength = name_length + 8;
if (entryLength & 0x3)
entryLength = (entryLength + 4) & 0xFFFFFFFC; /* multiple of 4 bytes */
myPosition = 0;
for (;;)
{
ext2_dir_entry_t *de =
(ext2_dir_entry_t *) (myBuffer + myPosition);
u32_t thisEntryLength = de->name_length + 8;
if (thisEntryLength & 0x3)
thisEntryLength = (thisEntryLength + 4) & 0xFFFFFFFC;
if (de->length - thisEntryLength >= entryLength)
{
/* There is room in this entry for the new one */
u32_t newLength = de->length - entryLength;
de->length = entryLength;
u32_t firstPosition = myPosition;
myPosition += de->length;
de = (ext2_dir_entry_t *) (myBuffer + myPosition);
de->length = newLength;
de->name_length = name_length;
de->inode = newInode;
memcpy(de->name, name, name_length);
writeInclusiveRange(firstPosition,
myPosition + entryLength - 1);
return 0;
}
if (myPosition + de->length >= myInode.i_size)
{
if (myFS->getSuper()->s_free_blocks_count < 2)
return -4; /* out of space on device */
/* This is the last entry, we need to add a block */
myBlocks++;
u8_t *newBuffer = new u8_t[myBlockSize * myBlocks];
memcpy(newBuffer, myBuffer, myBlockSize * (myBlocks - 1));
delete[] myBuffer;
myBuffer = newBuffer;
de = (ext2_dir_entry_t *) (myBuffer + myPosition);
u32_t thisEntryLength = de->name_length + 8;
if (thisEntryLength & 0x3)
thisEntryLength = (thisEntryLength + 4) & 0xFFFFFFFC;
u32_t leftovers = de->length - thisEntryLength;
de->length -= leftovers;
u32_t firstPosition = myPosition;
myPosition += thisEntryLength;
de = (ext2_dir_entry_t *) (myBuffer + myPosition);
de->length = myBlockSize + leftovers;
de->name_length = name_length;
de->inode = newInode;
memcpy(de->name, name, name_length);
writeInclusiveRange(firstPosition,
myPosition + myBlockSize + leftovers - 1);
return 0;
}
myPosition += de->length;
}
}
void Ext2OpenDirectory::writeInclusiveRange(u32_t first, u32_t last)
{
u32_t firstBlock = first / myBlockSize;
u32_t lastBlock = last / myBlockSize;
for (u32_t i = firstBlock; i <= lastBlock; i++)
myCache->writeBlock(i, myBuffer + i * myBlockSize);
}
int Ext2OpenDirectory::unlink(char *name)
{
if ((myMode & VFS_MODE_RW_MASK) != VFS_MODE_WRITE)
return -1;
readDirectory();
myPosition = 0;
u32_t lastPosition = 0;
u32_t name_length = strlen(name);
while (myPosition < myInode.i_size)
{
ext2_dir_entry_t *de =
(ext2_dir_entry_t *) (myBuffer + myPosition);
if (name_length == de->name_length && !strncmp(name, de->name, name_length))
{
ext2_inode_t inode;
myFS->readInode(de->inode, &inode);
if ((inode.i_mode & EXT2_I_MODE_TYPE_MASK) == EXT2_I_MODE_DIR)
{
if (myFS->attemptRemoveDir(de->inode))
return -3; /* can't unlink non-empty directory */
}
/* found entry to unlink
add the space to entry at lastPosition */
myFS->unlink(de->inode);
ext2_dir_entry_t *lde =
(ext2_dir_entry_t *) (myBuffer + lastPosition);
lde->length += de->length;
writeInclusiveRange(lastPosition, lastPosition + 8);
return 0;
}
lastPosition = myPosition;
myPosition += de->length;
}
return -2; /* entry not found */
}