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:
@@ -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();
|
||||
|
Reference in New Issue
Block a user