diff options
Diffstat (limited to 'src/lib/nfs/FileReader.cxx')
-rw-r--r-- | src/lib/nfs/FileReader.cxx | 244 |
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); +} |