lib/nfs/FileReader: postpone the nfs_close_async() call

If an async opertion is in progress, nfs_close_async() will make
libnfs crash because the RPC callback will dereference an object that
was freed by nfs_close_async().
This commit is contained in:
Max Kellermann
2014-09-26 13:29:44 +02:00
parent edd003b62a
commit 0661fd6f7c
4 changed files with 92 additions and 6 deletions

View File

@@ -80,10 +80,23 @@ NfsConnection::CancellableCallback::Read(nfs_context *ctx, struct nfsfh *fh,
return true;
}
inline void
NfsConnection::CancellableCallback::CancelAndScheduleClose(struct nfsfh *fh)
{
assert(!open);
assert(close_fh == nullptr);
assert(fh != nullptr);
close_fh = fh;
Cancel();
}
inline void
NfsConnection::CancellableCallback::Callback(int err, void *data)
{
if (!IsCancelled()) {
assert(close_fh == nullptr);
NfsCallback &cb = Get();
connection.callbacks.Remove(*this);
@@ -98,9 +111,12 @@ NfsConnection::CancellableCallback::Callback(int err, void *data)
/* a nfs_open_async() call was cancelled - to
avoid a memory leak, close the newly
allocated file handle immediately */
assert(close_fh == nullptr);
struct nfsfh *fh = (struct nfsfh *)data;
connection.Close(fh);
}
} else if (close_fh != nullptr)
connection.DeferClose(close_fh);
connection.callbacks.Remove(*this);
}
@@ -135,6 +151,7 @@ NfsConnection::~NfsConnection()
assert(new_leases.empty());
assert(active_leases.empty());
assert(callbacks.IsEmpty());
assert(deferred_close.empty());
if (context != nullptr)
DestroyContext();
@@ -224,6 +241,13 @@ NfsConnection::Close(struct nfsfh *fh)
ScheduleSocket();
}
void
NfsConnection::CancelAndClose(struct nfsfh *fh, NfsCallback &callback)
{
CancellableCallback &cancel = callbacks.Get(callback);
cancel.CancelAndScheduleClose(fh);
}
void
NfsConnection::DestroyContext()
{
@@ -237,6 +261,15 @@ NfsConnection::DestroyContext()
context = nullptr;
}
inline void
NfsConnection::DeferClose(struct nfsfh *fh)
{
assert(in_event);
assert(in_service);
deferred_close.push_front(fh);
}
void
NfsConnection::ScheduleSocket()
{
@@ -257,6 +290,8 @@ NfsConnection::ScheduleSocket()
bool
NfsConnection::OnSocketReady(unsigned flags)
{
assert(deferred_close.empty());
bool closed = false;
const bool was_mounted = mount_finished;
@@ -278,6 +313,12 @@ NfsConnection::OnSocketReady(unsigned flags)
assert(in_service);
in_service = false;
while (!deferred_close.empty()) {
nfs_close_async(context, deferred_close.front(),
DummyCallback, nullptr);
deferred_close.pop_front();
}
if (!was_mounted && mount_finished) {
if (postponed_mount_error.IsDefined()) {
DestroyContext();