summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2023-01-31 00:19:47 -0500
committerTheodore Ts'o <tytso@mit.edu>2023-02-01 00:39:18 -0500
commit0e0c7537eb5fdcef9a90b1a3ed8365c3878392a1 (patch)
treed24337ca2b7f6a8795eda025d29a245e04992eb7
parentf6cb7f010d137f83a37d1de375a9e73a84c4edf1 (diff)
downloade2fsprogs-0e0c7537eb5fdcef9a90b1a3ed8365c3878392a1.tar.gz
libext2fs: unix_io: fix_potential error path deadlock in flush_cached_blocks()
We can't call the error handler while holding the CACHE_MUTEX (see previous commit, "libext2fs: unix_io: fix_potential error path deadlock in reuse_cache()" for details), so first try to write out all of the dirty blocks in the cache, and then for those where we had errors, then call the error handler. Signed-off-by: Theodore Ts'o <tytso@mit.edu>
-rw-r--r--lib/ext2fs/unix_io.c61
1 files changed, 48 insertions, 13 deletions
diff --git a/lib/ext2fs/unix_io.c b/lib/ext2fs/unix_io.c
index 2e108a2f..353d85af 100644
--- a/lib/ext2fs/unix_io.c
+++ b/lib/ext2fs/unix_io.c
@@ -614,31 +614,66 @@ static errcode_t flush_cached_blocks(io_channel channel,
int flags)
{
struct unix_cache *cache;
- errcode_t retval, retval2;
+ errcode_t retval, retval2 = 0;
int i;
+ int errors_found = 0;
- retval2 = 0;
if ((flags & FLUSH_NOLOCK) == 0)
mutex_lock(data, CACHE_MTX);
for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
- if (!cache->in_use)
+ if (!cache->in_use || !cache->dirty)
continue;
-
- if (flags & FLUSH_INVALIDATE)
- cache->in_use = 0;
-
- if (!cache->dirty)
- continue;
-
retval = raw_write_blk(channel, data,
- cache->block, 1, cache->buf, 0);
- if (retval)
+ cache->block, 1, cache->buf,
+ RAW_WRITE_NO_HANDLER);
+ if (retval) {
+ cache->write_err = 1;
+ errors_found = 1;
retval2 = retval;
- else
+ } else {
cache->dirty = 0;
+ cache->write_err = 0;
+ if (flags & FLUSH_INVALIDATE)
+ cache->in_use = 0;
+ }
}
if ((flags & FLUSH_NOLOCK) == 0)
mutex_unlock(data, CACHE_MTX);
+retry:
+ while (errors_found) {
+ if ((flags & FLUSH_NOLOCK) == 0)
+ mutex_lock(data, CACHE_MTX);
+ errors_found = 0;
+ for (i=0, cache = data->cache; i < CACHE_SIZE; i++, cache++) {
+ if (!cache->in_use || !cache->write_err)
+ continue;
+ errors_found = 1;
+ if (cache->write_err && channel->write_error) {
+ char *err_buf = NULL;
+ unsigned long long err_block = cache->block;
+
+ cache->dirty = 0;
+ cache->in_use = 0;
+ cache->write_err = 0;
+ if (io_channel_alloc_buf(channel, 0,
+ &err_buf))
+ err_buf = NULL;
+ else
+ memcpy(err_buf, cache->buf,
+ channel->block_size);
+ mutex_unlock(data, CACHE_MTX);
+ (channel->write_error)(channel, err_block,
+ 1, err_buf, channel->block_size, -1,
+ retval2);
+ if (err_buf)
+ ext2fs_free_mem(&err_buf);
+ goto retry;
+ } else
+ cache->write_err = 0;
+ }
+ if ((flags & FLUSH_NOLOCK) == 0)
+ mutex_unlock(data, CACHE_MTX);
+ }
return retval2;
}
#endif /* NO_IO_CACHE */