* Fixed recently introduced write lock leak, when something failed
before file_cache_write() or nothing had to be written. * Fixed race condition. While neither transaction nor read lock are held, the file size can change. * Add the inode to the transaction whenever possible, i.e. on error before file_cache_write() and after it as well. This should prevent readers from seeing inconsistent blocks when the transaction has to be rolled back. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@26736 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -1412,33 +1412,34 @@ Inode::WriteAt(Transaction& transaction, off_t pos, const uint8* buffer,
|
||||
// TODO: support INODE_LOGGED!
|
||||
|
||||
size_t length = *_length;
|
||||
bool changeSize = false;
|
||||
bool changeSize = pos + length > Size();
|
||||
|
||||
// set/check boundaries for pos/length
|
||||
if (pos < 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (pos + length > Size())
|
||||
changeSize = true;
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
// the transaction doesn't have to be started already
|
||||
if (changeSize && !transaction.IsStarted())
|
||||
transaction.Start(fVolume, BlockNumber());
|
||||
|
||||
// TODO: we actually need to call WriteLockInTransaction() here, but we
|
||||
// cannot do this with the current locking model (ie. file cache functions
|
||||
// are not to be called with the inode lock held).
|
||||
// But this cannot work anyway, since we hold the lock when calling
|
||||
// file_cache_set_size(), too... (possible deadlock)
|
||||
rw_lock_write_lock(&fLock);
|
||||
WriteLocker writeLocker(fLock);
|
||||
|
||||
// Work around possible race condition: Someone might have shrunken the file
|
||||
// while we had no lock.
|
||||
if (!transaction.IsStarted() && pos + length > Size()) {
|
||||
writeLocker.Unlock();
|
||||
transaction.Start(fVolume, BlockNumber());
|
||||
writeLocker.Lock();
|
||||
}
|
||||
|
||||
if (pos + length > Size()) {
|
||||
// let's grow the data stream to the size needed
|
||||
status_t status = SetFileSize(transaction, pos + length);
|
||||
if (status < B_OK) {
|
||||
*_length = 0;
|
||||
WriteLockInTransaction(transaction);
|
||||
RETURN_ERROR(status);
|
||||
}
|
||||
// TODO: In theory we would need to update the file size
|
||||
@@ -1449,18 +1450,25 @@ Inode::WriteAt(Transaction& transaction, off_t pos, const uint8* buffer,
|
||||
// go into this transaction (we cannot wait until the file
|
||||
// is closed)
|
||||
status = WriteBack(transaction);
|
||||
if (status < B_OK)
|
||||
if (status < B_OK) {
|
||||
WriteLockInTransaction(transaction);
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
writeLocker.Unlock();
|
||||
|
||||
// If we don't want to write anything, we can now return (we may
|
||||
// just have changed the file size using the position parameter)
|
||||
if (length == 0)
|
||||
return B_OK;
|
||||
|
||||
rw_lock_write_unlock(&fLock);
|
||||
status_t status = file_cache_write(FileCache(), NULL, pos, buffer, _length);
|
||||
|
||||
return file_cache_write(FileCache(), NULL, pos, buffer, _length);
|
||||
if (transaction.IsStarted())
|
||||
WriteLockInTransaction(transaction);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user