hos/kernel/fs/ext2/Ext2OpenFile.cpp

229 lines
5.8 KiB
C++

// Ext2OpenFile.cpp
// Author: Josh Holtrop
// Date: 12/26/05
// Modified: 12/26/05
#include "Ext2OpenFile.h"
extern "C" {
#include "lang/lang.h"
#include "functions.h"
}
lock_t Ext2OpenFileListLock;
vector<Ext2OpenFileHandle> *Ext2OpenFileList = NULL;
/* make an Ext2OpenFile object
* maintain vector of open files for notifying when one is truncated
*/
Ext2OpenFile::Ext2OpenFile(Ext2fs *fs, u32_t inum, int mode)
{
myMode = mode;
myFS = fs;
myInum = inum;
myBeenTruncated = 0;
myPosition = 0;
myBlockSize = 1024 << fs->getSuper()->s_log_block_size;
myBlockCache = new u8_t[myBlockSize];
myBlockCacheBlock = 0;
myBlockCacheStatus = 0; /* 0: invalid; 1: valid; 2: dirty */
fs->readInode(inum, &myInode);
myCache = new Ext2BlockCache(fs, inum, &myInode);
lock(&Ext2OpenFileListLock);
if ((mode & VFS_MODE_RW_MASK) == VFS_MODE_WRITE)
{
if ((mode & VFS_MODE_WRITE_MASK) == VFS_MODE_TRUNCATE)
{
/* file opened for write/truncate
* tell any open files that it's been truncated, then
* tell ext2 filesystem to free its blocks
*/
u32_t size = Ext2OpenFileList->size();
for (u32_t i = 0; i < size; i++)
{
if ((*Ext2OpenFileList)[i].inode == inum)
(*Ext2OpenFileList)[i].ofile->notifyTruncated();
}
fs->truncateInode(inum);
fs->readInode(inum, &myInode); /* re-read inode */
}
else
myPosition = myInode.i_size;
}
if (Ext2OpenFileList == NULL)
Ext2OpenFileList = new vector<Ext2OpenFileHandle>();
/* add ourselves to the list */
Ext2OpenFileHandle thisHandle = {inum, this};
Ext2OpenFileList->add(thisHandle);
unlock(&Ext2OpenFileListLock);
}
/* destroy an Ext2OpenFile object */
Ext2OpenFile::~Ext2OpenFile()
{
/* remove ourselves from the open file list */
lock(&Ext2OpenFileListLock);
u32_t size = Ext2OpenFileList->size();
for (u32_t i = 0; i < size; i++)
{
if ((*Ext2OpenFileList)[i].ofile == this)
{
Ext2OpenFileList->remove(i);
break;
}
}
unlock(&Ext2OpenFileListLock);
/* write back any open caches if in write mode */
if ( !myBeenTruncated && ((myMode & VFS_MODE_RW_MASK) == VFS_MODE_WRITE) )
{
if (myBlockCacheStatus == 2)
myCache->writeBlock(myBlockCacheBlock, myBlockCache);
myFS->writeInode(myInum, &myInode);
}
/* free the memory we used */
delete myCache;
delete[] myBlockCache;
}
/* seek to a certain position in the file (only for reading) */
int Ext2OpenFile::seek(int pos, int mode)
{
if ((myMode & VFS_MODE_RW_MASK) != VFS_MODE_READ)
return -1;
if (mode == SEEK_ABSOLUTE)
{
if (pos < 0)
return -2;
if ((u32_t)pos >= myInode.i_size)
return -3;
myPosition = pos;
return 0;
}
else if (mode == SEEK_RELATIVE)
{
if (myPosition + pos < 0)
return -2;
if (myPosition + pos >= myInode.i_size)
return -3;
myPosition += pos;
return 0;
}
else if (mode == SEEK_END)
{
if (myInode.i_size + pos < 0)
return -2;
if (myInode.i_size + pos >= myInode.i_size)
return -3;
myPosition = myInode.i_size + pos;
return 0;
}
return -4;
}
/* read a byte from the current file position, return error code/EOF */
int Ext2OpenFile::read()
{
if ((myMode & VFS_MODE_RW_MASK) != VFS_MODE_READ)
return -1;
if (myBeenTruncated || myPosition >= myInode.i_size)
return EOF;
u32_t theBlock = myPosition / myBlockSize;
updateCache(theBlock);
return myBlockCache[myPosition++ % myBlockSize];
}
/* read num bytes from the current file position, put in buf
* return: number of bytes read (>=0) or error code (<0)
*/
int Ext2OpenFile::read(void *buf, u32_t num)
{
if ((myMode & VFS_MODE_RW_MASK) != VFS_MODE_READ)
return -1;
if (myBeenTruncated || myPosition >= myInode.i_size)
return EOF;
if (myPosition + num > myInode.i_size)
num = myInode.i_size - myPosition;
u32_t bytesRead = 0;
u32_t bytesIntoFirstBlock = myPosition % myBlockSize;
if (bytesIntoFirstBlock)
{
updateCache(myPosition / myBlockSize);
bytesRead = min(myBlockSize - bytesIntoFirstBlock, num);
memcpy(buf, myBlockCache + bytesIntoFirstBlock, bytesRead);
myPosition += bytesRead;
}
while (bytesRead < num)
{
updateCache(myPosition / myBlockSize);
u32_t bytesToRead = min(num - bytesRead, myBlockSize);
memcpy((u8_t *)buf + bytesRead, myBlockCache, bytesToRead);
bytesRead += bytesToRead;
myPosition += bytesToRead;
}
return bytesRead;
}
/* write a byte to the current file position */
int Ext2OpenFile::write(int chr)
{
if ((myMode & VFS_MODE_RW_MASK) != VFS_MODE_WRITE)
return -1;
if (myBeenTruncated)
return EOF;
u32_t theBlock = myPosition / myBlockSize;
updateCache(theBlock);
myBlockCache[myPosition++ % myBlockSize] = chr;
myBlockCacheStatus = 2;
myInode.i_size++;
return 0;
}
/* write num bytes from buf to the current file position
* return: number of bytes written (>=0) or error code (<0)
*/
int Ext2OpenFile::write(void *buf, u32_t num)
{
if (myBeenTruncated)
return -1;
if ((myMode & VFS_MODE_RW_MASK) != VFS_MODE_WRITE)
return -2;
u32_t bytesIntoFirstBlock = myPosition % myBlockSize;
u32_t bytesWritten = 0;
if (bytesIntoFirstBlock)
{
updateCache(myPosition / myBlockSize);
bytesWritten = min(myBlockSize - bytesIntoFirstBlock, num);
memcpy(myBlockCache + bytesIntoFirstBlock, buf, bytesWritten);
myBlockCacheStatus = 2;
myPosition += bytesWritten;
}
while (bytesWritten < num)
{
updateCache(myPosition / myBlockSize);
u32_t bytesToWrite = min(num - bytesWritten, myBlockSize);
memcpy(myBlockCache, (u8_t *)buf + bytesWritten, bytesToWrite);
myBlockCacheStatus = 2;
bytesWritten += bytesToWrite;
myPosition += bytesToWrite;
}
myInode.i_size += bytesWritten;
return bytesWritten;
}
void Ext2OpenFile::updateCache(u32_t blockNum)
{
if (myBlockCacheStatus && myBlockCacheBlock == blockNum)
return;
if (myBlockCacheStatus == 2)
myCache->writeBlock(myBlockCacheBlock, myBlockCache);
myCache->readBlock(blockNum, myBlockCache);
myBlockCacheStatus = 1;
myBlockCacheBlock = blockNum;
}
void Ext2OpenFile::notifyTruncated()
{
myBeenTruncated = 1;
}