improve create(), rename(), unlink()

git-svn-id: https://profuse.googlecode.com/svn/branches/v2@300 aa027e90-d47c-11dd-86d7-074df07e0730
This commit is contained in:
ksherlock 2010-05-30 20:18:24 +00:00
parent 2a87b480a1
commit c4ea7cc0f8
3 changed files with 257 additions and 67 deletions

View File

@ -109,10 +109,9 @@ void FileEntry::setName(const char *name)
for (unsigned i = 0; i < length; ++i) for (unsigned i = 0; i < length; ++i)
_fileName[i] = std::toupper(name[i]); _fileName[i] = std::toupper(name[i]);
// not sure if this is a good idea or not.
//_modification = Date::Today();
parent()->writeEntry(this); // parent's responsibility.
//parent()->writeEntry(this);
} }
unsigned FileEntry::fileSize() unsigned FileEntry::fileSize()

View File

@ -275,17 +275,26 @@ FileEntry *VolumeEntry::fileByName(const char *name) const
return NULL; return NULL;
} }
unsigned VolumeEntry::unlink(const char *name) /*
* Remove a file.
* Returns 0 on succes, -1 (and errno) on failure.
* May also throw a device error.
*/
int VolumeEntry::unlink(const char *name)
{ {
unsigned index; unsigned index;
std::vector<FileEntry *>::iterator iter; std::vector<FileEntry *>::iterator iter;
if (_device->readOnly())
{
errno = EROFS;
return -1;
}
// TODO -- update _maxFileSize. // TODO -- consider having an unordered_map of file names.
if (_device->readOnly()) return ProFUSE::drvrWrtProt; // WRITE-PROTECTED DISK for (iter = _files.begin(); iter != _files.end(); ++iter)
for (index = 0, iter = _files.begin(); iter != _files.end(); ++iter, ++index)
{ {
FileEntry *e = *iter; FileEntry *e = *iter;
if (::strcasecmp(name, e->name()) == 0) if (::strcasecmp(name, e->name()) == 0)
@ -304,12 +313,18 @@ unsigned VolumeEntry::unlink(const char *name)
break; break;
} }
} }
if (iter == _files.end()) return ProFUSE::fileNotFound; // FILE NOT FOUND if (iter == _files.end())
{
errno = ENOENT;
return -1;
}
_files.erase(iter); _files.erase(iter);
_fileCount--; _fileCount--;
index = distance(_files.begin(), iter);
// reset addresses. // reset addresses.
for ( ; iter != _files.end(); ++iter) for ( ; iter != _files.end(); ++iter)
{ {
@ -339,35 +354,49 @@ unsigned VolumeEntry::unlink(const char *name)
} }
// TODO -- if newName exists, atomically remove it. /*
unsigned VolumeEntry::rename(const char *oldName, const char *newName) * Renames a file, removing any other file with newName (if present)
* Returns 0 on success, -1 on error (and sets errno)
* May also throw an error of some sort.
*/
int VolumeEntry::rename(const char *oldName, const char *newName)
{ {
FileEntry *e; FileEntry *e;
// 1. verify old name exists. // check if read only.
if (_device->readOnly())
{
errno = EROFS;
return -1;
}
// verify file exists.
e = fileByName(oldName); e = fileByName(oldName);
if (!e) if (!e)
return ProFUSE::fileNotFound; {
errno = ENOENT;
return -1;
}
// 2. verify new name is valid // verify new name is valid.
if (!FileEntry::ValidName(newName)) if (!FileEntry::ValidName(newName))
return ProFUSE::badPathSyntax; {
errno = EINVAL; // invalid name, at least.
return -1;
}
// delete the new file
if (unlink(newName) != 0)
{
if (errno != ENOENT) return -1;
}
// 3. verify new name does not exist. // update with the new name.
if (fileByName(newName))
return ProFUSE::dupPathName;
// 4. set the name (throws on error, which won't happen since
// we already verified the name is valid.
e->setName(newName); e->setName(newName);
// and commit to disk.
// 5. write to disk.
writeEntry(e); writeEntry(e);
_cache->sync(); _cache->sync();
@ -381,9 +410,36 @@ unsigned VolumeEntry::rename(const char *oldName, const char *newName)
* if newName exists, delete it. * if newName exists, delete it.
* *
*/ */
unsigned VolumeEntry::copy(const char *oldName, const char *newName) int VolumeEntry::copy(const char *oldName, const char *newName)
{ {
FileEntry * e;
// check if read only.
if (_device->readOnly())
{
errno = EROFS;
return -1;
}
// verify file exists.
e = fileByName(oldName);
if (!e)
{
errno = ENOENT;
return -1;
}
// verify new name is valid.
if (!FileEntry::ValidName(newName))
{
errno = EINVAL; // invalid name, at least.
return -1;
}
// if newName does not exist, call create w/ block size and write everything.
// if newName does exist, overwrite it if it will fit. Otherwise, remove it, create a file, etc.
return 0; return 0;
} }
@ -391,6 +447,7 @@ unsigned VolumeEntry::copy(const char *oldName, const char *newName)
/* /*
* create a file. if blocks is defined, verifies the file could * create a file. if blocks is defined, verifies the file could
* expand to fit. * expand to fit.
* returns FileEntry on success, NULL (and errno) on failure.
* *
*/ */
/* /*
@ -409,16 +466,18 @@ FileEntry *VolumeEntry::create(const char *name, unsigned blocks)
// 6. create the file entry. // 6. create the file entry.
// 7. insert into _files, write to disk, update _maxFileSize // 7. insert into _files, write to disk, update _maxFileSize
unsigned lastBlock; unsigned lastBlock = _lastBlock;
unsigned maxBlocks;
std::auto_ptr<FileEntry>entry; std::auto_ptr<FileEntry>entry;
FileEntry *prev = NULL; FileEntry *prev = NULL;
FileEntry *curr = NULL; FileEntry *curr = NULL;
std::vector<FileEntry *>::iterator iter;
if (readOnly()) if (readOnly())
{ {
errno = EACCES; errno = EROFS;
return NULL; return NULL;
} }
@ -428,26 +487,10 @@ FileEntry *VolumeEntry::create(const char *name, unsigned blocks)
return NULL; return NULL;
} }
if (_fileCount)
{
prev = _files.back();
lastBlock = prev->_lastBlock;
}
else {
lastBlock = _lastBlock;
}
maxBlocks = _lastVolumeBlock - lastBlock;
if (maxBlocks < blocks || maxBlocks == 0)
{
errno = ENOSPC;
return NULL;
}
if (!FileEntry::ValidName(name)) if (!FileEntry::ValidName(name))
{ {
errno = ENAMETOOLONG; // or invalid. errno = EINVAL;
return NULL; return NULL;
} }
@ -460,24 +503,107 @@ FileEntry *VolumeEntry::create(const char *name, unsigned blocks)
entry.reset(new FileEntry(name, kUntypedFile)); entry.reset(new FileEntry(name, kUntypedFile));
_files.push_back(entry.get());
curr = entry.release();
curr->_firstBlock = lastBlock; std::auto_ptr<uint8_t> buffer(readDirectoryHeader());
curr->_lastBlock = lastBlock + 1;
curr->_lastByte = 0; for (iter = _files.begin(); iter != _files.end(); ++iter)
curr->_maxFileSize = maxBlocks * 512; {
FileEntry *e = *iter;
if (prev)
{ unsigned freeSpace = e->_firstBlock - _lastBlock;
prev->_maxFileSize = prev->blocks() * 512; // this could do something stupid like selecting a slot with only 1 free block but too bad.
if (freeSpace < blocks)
{
lastBlock = e->_lastBlock;
prev = e;
continue;
}
// update previous entry max file size.
if (prev)
{
prev->_maxFileSize = prev->blocks() * 512;
}
// insert() inserts an item *before* the current item.
// afterwards, iter will point to curr+1
// keep track of the index *before* the insert.
unsigned index = distance(_files.begin(), iter); // current index.
_files.insert(iter, entry.get());
_fileCount++;
curr = entry.release();
curr->_firstBlock = lastBlock;
curr->_lastBlock = lastBlock + 1;
curr->_lastByte = 0;
curr->_maxFileSize = freeSpace * 512;
//curr->_address = 2 * 512 + 0x1a + 0x1a * index;
// move all entries after this one up by 0x1a bytes, then write this header.
unsigned count = _fileCount - index - 1;
unsigned offset = index * 0x1a + 0x1a;
std::memmove(buffer.get() + offset + 0x1a, buffer.get() + offset, count * 0x1a);
break;
} }
writeEntry(curr); if (iter == _files.end())
{
// check if we can append
unsigned freeSpace = _lastVolumeBlock - lastBlock;
if (freeSpace < blocks)
{
errno = ENOSPC;
return NULL;
}
_files.push_back(entry.get());
_fileCount++;
curr = entry.release();
curr->_firstBlock = lastBlock;
curr->_lastBlock = lastBlock + 1;
curr->_lastByte = 0;
curr->_maxFileSize = freeSpace * 512;
curr->_address = 2 * 512 + 0x1a * _fileCount; // s/b +1 since entry 0 is the header.
if (prev)
{
prev->_maxFileSize = prev->blocks() * 512;
}
writeEntry(curr);
writeEntry(); // header.
}
else
{
// update all addresses to make life easier.
unsigned address = 2 * 512 + 0x1a;
for (iter = _files.begin(); iter != _files.end(); ++iter, address += 0x1a)
{
FileEntry *e = *iter;
e->_address = address;
}
IOBuffer b(buffer.get(), 4 * 512);
writeDirectoryEntry(&b);
b.setOffset(curr->_address - 2 * 512);
curr->writeDirectoryEntry(&b);
writeDirectoryHeader(buffer.get());
}
_cache->sync();
return curr; return curr;
} }
@ -486,8 +612,15 @@ FileEntry *VolumeEntry::create(const char *name, unsigned blocks)
* TODO -- consider trying to move files from the end to fill gaps * TODO -- consider trying to move files from the end to fill gaps
* if it would reduce the number of blocks that need to be re-arranged. * if it would reduce the number of blocks that need to be re-arranged.
* *
* build a list of gaps, reverse iterate and move last file to first gap
* only 77 files, so can consider all options.
*
*
*
*
*
*/ */
unsigned VolumeEntry::krunch() int VolumeEntry::krunch()
{ {
unsigned prevBlock; unsigned prevBlock;
@ -619,6 +752,54 @@ unsigned VolumeEntry::freeBlocks(bool krunched) const
} }
/*
* return true if there are any gaps, false otherwise.
*
*
*/
bool VolumeEntry::canKrunch() const
{
std::vector<FileEntry *>::const_iterator iter;
unsigned lastBlock = _lastBlock;
for (iter = _files.begin(); iter != _files.end(); ++iter)
{
const FileEntry *e = *iter;
if (e->_lastBlock != lastBlock) return true;
lastBlock = e->_lastBlock;
}
return false;
}
/*
* returns the largest free block.
*
*/
unsigned VolumeEntry::maxContiguousBlocks() const
{
unsigned max = 0;
std::vector<FileEntry *>::const_iterator iter;
unsigned lastBlock = _lastBlock;
for (iter = _files.begin(); iter != _files.end(); ++iter)
{
const FileEntry *e = *iter;
unsigned free = e->_firstBlock - lastBlock;
max = std::max(max, free);
lastBlock = e->_lastBlock;
}
max = std::max(max, _lastVolumeBlock - lastBlock);
return max;
}
@ -693,6 +874,14 @@ void VolumeEntry::writeBlocks(void *buffer, unsigned startingBlock, unsigned cou
} }
// write directory entry, does not sync.
void VolumeEntry::writeEntry()
{
IOBuffer iob(loadBlock(2),512);
writeDirectoryEntry(&iob);
unloadBlock(2, true);
}
// does not sync. // does not sync.
void VolumeEntry::writeEntry(FileEntry *e) void VolumeEntry::writeEntry(FileEntry *e)
{ {

View File

@ -32,6 +32,8 @@ namespace Pascal {
Pascal::Date lastBoot() const { return _lastBoot; } Pascal::Date lastBoot() const { return _lastBoot; }
unsigned freeBlocks(bool krunched = false) const; unsigned freeBlocks(bool krunched = false) const;
unsigned maxContiguousBlocks() const;
bool canKrunch() const;
FileEntry *fileAtIndex(unsigned i) const; FileEntry *fileAtIndex(unsigned i) const;
@ -48,14 +50,13 @@ namespace Pascal {
bool readOnly() { return _device->readOnly(); } bool readOnly() { return _device->readOnly(); }
unsigned unlink(const char *name); int unlink(const char *name);
unsigned rename(const char *oldName, const char *newName); int rename(const char *oldName, const char *newName);
unsigned copy(const char *oldName, const char *newName); int copy(const char *oldName, const char *newName);
FileEntry *create(const char *name, unsigned blocks); FileEntry *create(const char *name, unsigned blocks);
int krunch();
unsigned krunch();
protected: protected:
@ -77,6 +78,7 @@ namespace Pascal {
void writeBlocks(void *buffer, unsigned startingBlock, unsigned count); void writeBlocks(void *buffer, unsigned startingBlock, unsigned count);
void writeEntry(FileEntry *e); void writeEntry(FileEntry *e);
void writeEntry();
void calcMaxFileSize(); void calcMaxFileSize();