229 lines
5.8 KiB
C++
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);
|
|
lockit(&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 */
|
|
lockit(&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;
|
|
}
|