input/nfs: use the asynchronous libnfs API
More robust and cancellable.
This commit is contained in:
244
src/lib/nfs/FileReader.cxx
Normal file
244
src/lib/nfs/FileReader.cxx
Normal file
@@ -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);
|
||||
}
|
Reference in New Issue
Block a user