aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/nfs/FileReader.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/nfs/FileReader.cxx')
-rw-r--r--src/lib/nfs/FileReader.cxx244
1 files changed, 244 insertions, 0 deletions
diff --git a/src/lib/nfs/FileReader.cxx b/src/lib/nfs/FileReader.cxx
new file mode 100644
index 000000000..52d951fa6
--- /dev/null
+++ b/src/lib/nfs/FileReader.cxx
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2003-2014 The Music Player Daemon Project
+ * http://www.musicpd.org
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+#include "FileReader.hxx"
+#include "Glue.hxx"
+#include "Connection.hxx"
+#include "Domain.hxx"
+#include "event/Call.hxx"
+#include "IOThread.hxx"
+#include "util/StringUtil.hxx"
+#include "util/Error.hxx"
+
+#include <utility>
+
+#include <assert.h>
+#include <string.h>
+#include <fcntl.h>
+
+NfsFileReader::NfsFileReader()
+ :DeferredMonitor(io_thread_get()), state(State::INITIAL)
+{
+}
+
+NfsFileReader::~NfsFileReader()
+{
+ assert(state == State::INITIAL);
+}
+
+void
+NfsFileReader::Close()
+{
+ if (state == State::INITIAL)
+ return;
+
+ if (state == State::DEFER) {
+ state = State::INITIAL;
+ DeferredMonitor::Cancel();
+ return;
+ }
+
+ connection->RemoveLease(*this);
+
+ if (state > State::MOUNT && state != State::IDLE)
+ connection->Cancel(*this);
+
+ if (state > State::OPEN)
+ connection->Close(fh);
+
+ state = State::INITIAL;
+}
+
+void
+NfsFileReader::DeferClose()
+{
+ BlockingCall(io_thread_get(), [this](){ Close(); });
+}
+
+bool
+NfsFileReader::Open(const char *uri, Error &error)
+{
+ assert(state == State::INITIAL);
+
+ if (!StringStartsWith(uri, "nfs://")) {
+ error.Set(nfs_domain, "Malformed nfs:// URI");
+ return false;
+ }
+
+ uri += 6;
+
+ const char *slash = strchr(uri, '/');
+ if (slash == nullptr) {
+ error.Set(nfs_domain, "Malformed nfs:// URI");
+ return false;
+ }
+
+ server = std::string(uri, slash);
+
+ uri = slash;
+ slash = strrchr(uri + 1, '/');
+ if (slash == nullptr || slash[1] == 0) {
+ error.Set(nfs_domain, "Malformed nfs:// URI");
+ return false;
+ }
+
+ export_name = std::string(uri, slash);
+ path = slash;
+
+ state = State::DEFER;
+ DeferredMonitor::Schedule();
+ return true;
+}
+
+bool
+NfsFileReader::Read(uint64_t offset, size_t size, Error &error)
+{
+ assert(state == State::IDLE);
+
+ if (!connection->Read(fh, offset, size, *this, error))
+ return false;
+
+ state = State::READ;
+ return true;
+}
+
+void
+NfsFileReader::CancelRead()
+{
+ if (state == State::READ) {
+ connection->Cancel(*this);
+ state = State::IDLE;
+ }
+}
+
+void
+NfsFileReader::OnNfsConnectionReady()
+{
+ assert(state == State::MOUNT);
+
+ Error error;
+ if (!connection->Open(path, O_RDONLY, *this, error)) {
+ OnNfsFileError(std::move(error));
+ return;
+ }
+
+ state = State::OPEN;
+}
+
+void
+NfsFileReader::OnNfsConnectionFailed(const Error &error)
+{
+ assert(state == State::MOUNT);
+
+ Error copy;
+ copy.Set(error);
+ OnNfsFileError(std::move(copy));
+}
+
+void
+NfsFileReader::OnNfsConnectionDisconnected(const Error &error)
+{
+ assert(state > State::MOUNT);
+
+ state = State::INITIAL;
+
+ Error copy;
+ copy.Set(error);
+ OnNfsFileError(std::move(copy));
+}
+
+inline void
+NfsFileReader::OpenCallback(nfsfh *_fh)
+{
+ assert(state == State::OPEN);
+ assert(connection != nullptr);
+ assert(_fh != nullptr);
+
+ fh = _fh;
+
+ Error error;
+ if (!connection->Stat(fh, *this, error)) {
+ OnNfsFileError(std::move(error));
+ return;
+ }
+
+ state = State::STAT;
+}
+
+inline void
+NfsFileReader::StatCallback(const struct stat *st)
+{
+ assert(state == State::STAT);
+ assert(connection != nullptr);
+ assert(fh != nullptr);
+ assert(st != nullptr);
+
+ if (!S_ISREG(st->st_mode)) {
+ OnNfsFileError(Error(nfs_domain, "Not a regular file"));
+ return;
+ }
+
+ state = State::IDLE;
+
+ OnNfsFileOpen(st->st_size);
+}
+
+void
+NfsFileReader::OnNfsCallback(unsigned status, void *data)
+{
+ switch (state) {
+ case State::INITIAL:
+ case State::DEFER:
+ case State::MOUNT:
+ case State::IDLE:
+ assert(false);
+ gcc_unreachable();
+
+ case State::OPEN:
+ OpenCallback((struct nfsfh *)data);
+ break;
+
+ case State::STAT:
+ StatCallback((const struct stat *)data);
+ break;
+
+ case State::READ:
+ state = State::IDLE;
+ OnNfsFileRead(data, status);
+ break;
+ }
+}
+
+void
+NfsFileReader::OnNfsError(Error &&error)
+{
+ OnNfsFileError(std::move(error));
+}
+
+void
+NfsFileReader::RunDeferred()
+{
+ assert(state == State::DEFER);
+
+ state = State::MOUNT;
+
+ connection = &nfs_get_connection(server.c_str(), export_name.c_str());
+ connection->AddLease(*this);
+}