// 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 *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(); /* 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; }