diff options
author | Max Kellermann <max@duempel.org> | 2014-09-26 13:29:44 +0200 |
---|---|---|
committer | Max Kellermann <max@duempel.org> | 2014-10-01 23:10:32 +0200 |
commit | 0661fd6f7c66ae888b6fc253af6dd0514798eff5 (patch) | |
tree | 9cc213a9d3be7709f937df4cfb40255d74146338 /src/lib/nfs/Connection.cxx | |
parent | edd003b62af802fae7828336628adb0ea3f6bd21 (diff) | |
download | mpd-0661fd6f7c66ae888b6fc253af6dd0514798eff5.tar.gz mpd-0661fd6f7c66ae888b6fc253af6dd0514798eff5.tar.xz mpd-0661fd6f7c66ae888b6fc253af6dd0514798eff5.zip |
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().
Diffstat (limited to 'src/lib/nfs/Connection.cxx')
-rw-r--r-- | src/lib/nfs/Connection.cxx | 43 |
1 files changed, 42 insertions, 1 deletions
diff --git a/src/lib/nfs/Connection.cxx b/src/lib/nfs/Connection.cxx index 853cacc9a..e5dc87727 100644 --- a/src/lib/nfs/Connection.cxx +++ b/src/lib/nfs/Connection.cxx @@ -81,9 +81,22 @@ NfsConnection::CancellableCallback::Read(nfs_context *ctx, struct nfsfh *fh, } 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(); @@ -225,6 +242,13 @@ NfsConnection::Close(struct nfsfh *fh) } void +NfsConnection::CancelAndClose(struct nfsfh *fh, NfsCallback &callback) +{ + CancellableCallback &cancel = callbacks.Get(callback); + cancel.CancelAndScheduleClose(fh); +} + +void NfsConnection::DestroyContext() { assert(GetEventLoop().IsInside()); @@ -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(); |