aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/nfs/Connection.cxx
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2014-09-26 13:29:44 +0200
committerMax Kellermann <max@duempel.org>2014-10-01 23:10:32 +0200
commit0661fd6f7c66ae888b6fc253af6dd0514798eff5 (patch)
tree9cc213a9d3be7709f937df4cfb40255d74146338 /src/lib/nfs/Connection.cxx
parentedd003b62af802fae7828336628adb0ea3f6bd21 (diff)
downloadmpd-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 '')
-rw-r--r--src/lib/nfs/Connection.cxx43
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();