227 lines
6.3 KiB
C++
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 */
|
|
}
|
|
|