2014-03-15 15:29:10 +01:00
|
|
|
/*
|
2018-10-31 17:54:59 +01:00
|
|
|
* Copyright 2003-2018 The Music Player Daemon Project
|
2014-03-15 15:29:10 +01:00
|
|
|
* 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 "FileReader.hxx"
|
|
|
|
#include "Glue.hxx"
|
2014-10-05 07:41:50 +02:00
|
|
|
#include "Base.hxx"
|
2014-03-15 15:29:10 +01:00
|
|
|
#include "Connection.hxx"
|
|
|
|
#include "event/Call.hxx"
|
2018-08-02 10:38:20 +02:00
|
|
|
#include "util/ASCII.hxx"
|
2014-03-15 15:29:10 +01:00
|
|
|
|
|
|
|
#include <utility>
|
2019-12-16 22:52:50 +01:00
|
|
|
#include <stdexcept>
|
2014-03-15 15:29:10 +01:00
|
|
|
|
|
|
|
#include <assert.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::NfsFileReader() noexcept
|
2017-11-10 21:01:01 +01:00
|
|
|
:defer_open(nfs_get_event_loop(), BIND_THIS_METHOD(OnDeferredOpen))
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::~NfsFileReader() noexcept
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
assert(state == State::INITIAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::Close() noexcept
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
if (state == State::INITIAL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (state == State::DEFER) {
|
|
|
|
state = State::INITIAL;
|
2017-11-10 21:01:01 +01:00
|
|
|
defer_open.Cancel();
|
2014-03-15 15:29:10 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-11-25 14:00:32 +01:00
|
|
|
/* this cancels State::MOUNT */
|
2014-03-15 15:29:10 +01:00
|
|
|
connection->RemoveLease(*this);
|
|
|
|
|
2014-11-25 14:00:32 +01:00
|
|
|
CancelOrClose();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::CancelOrClose() noexcept
|
2014-11-25 14:00:32 +01:00
|
|
|
{
|
|
|
|
assert(state != State::INITIAL &&
|
|
|
|
state != State::DEFER);
|
|
|
|
|
2014-09-26 13:29:44 +02:00
|
|
|
if (state == State::IDLE)
|
|
|
|
/* no async operation in progress: can close
|
|
|
|
immediately */
|
2014-03-15 15:29:10 +01:00
|
|
|
connection->Close(fh);
|
2014-09-26 13:29:44 +02:00
|
|
|
else if (state > State::OPEN)
|
|
|
|
/* one async operation in progress: cancel it and
|
|
|
|
defer the nfs_close_async() call */
|
|
|
|
connection->CancelAndClose(fh, *this);
|
|
|
|
else if (state > State::MOUNT)
|
|
|
|
/* we don't have a file handle yet - just cancel the
|
|
|
|
async operation */
|
|
|
|
connection->Cancel(*this);
|
2014-03-15 15:29:10 +01:00
|
|
|
|
|
|
|
state = State::INITIAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::DeferClose() noexcept
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
2017-01-25 22:58:13 +01:00
|
|
|
BlockingCall(GetEventLoop(), [this](){ Close(); });
|
2014-03-15 15:29:10 +01:00
|
|
|
}
|
|
|
|
|
2016-09-09 15:37:06 +02:00
|
|
|
void
|
|
|
|
NfsFileReader::Open(const char *uri)
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
assert(state == State::INITIAL);
|
|
|
|
|
2018-08-02 10:38:20 +02:00
|
|
|
if (!StringStartsWithCaseASCII(uri, "nfs://"))
|
2016-09-09 15:37:06 +02:00
|
|
|
throw std::runtime_error("Malformed nfs:// URI");
|
2014-03-15 15:29:10 +01:00
|
|
|
|
|
|
|
uri += 6;
|
|
|
|
|
|
|
|
const char *slash = strchr(uri, '/');
|
2016-09-09 15:37:06 +02:00
|
|
|
if (slash == nullptr)
|
|
|
|
throw std::runtime_error("Malformed nfs:// URI");
|
2014-03-15 15:29:10 +01:00
|
|
|
|
|
|
|
server = std::string(uri, slash);
|
|
|
|
|
|
|
|
uri = slash;
|
|
|
|
|
2014-10-05 07:41:50 +02:00
|
|
|
const char *new_path = nfs_check_base(server.c_str(), uri);
|
|
|
|
if (new_path != nullptr) {
|
|
|
|
export_name = std::string(uri, new_path);
|
|
|
|
if (*new_path == 0)
|
|
|
|
new_path = "/";
|
|
|
|
path = new_path;
|
|
|
|
} else {
|
|
|
|
slash = strrchr(uri + 1, '/');
|
2016-09-09 15:37:06 +02:00
|
|
|
if (slash == nullptr || slash[1] == 0)
|
|
|
|
throw std::runtime_error("Malformed nfs:// URI");
|
2014-10-05 07:41:50 +02:00
|
|
|
|
|
|
|
export_name = std::string(uri, slash);
|
|
|
|
path = slash;
|
|
|
|
}
|
2014-03-15 15:29:10 +01:00
|
|
|
|
|
|
|
state = State::DEFER;
|
2017-11-10 21:01:01 +01:00
|
|
|
defer_open.Schedule();
|
2014-03-15 15:29:10 +01:00
|
|
|
}
|
|
|
|
|
2016-09-16 16:55:57 +02:00
|
|
|
void
|
|
|
|
NfsFileReader::Read(uint64_t offset, size_t size)
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
assert(state == State::IDLE);
|
|
|
|
|
2016-09-16 16:55:57 +02:00
|
|
|
connection->Read(fh, offset, size, *this);
|
2014-03-15 15:29:10 +01:00
|
|
|
state = State::READ;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::CancelRead() noexcept
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
if (state == State::READ) {
|
|
|
|
connection->Cancel(*this);
|
|
|
|
state = State::IDLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::OnNfsConnectionReady() noexcept
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
assert(state == State::MOUNT);
|
|
|
|
|
2016-09-16 16:55:57 +02:00
|
|
|
try {
|
|
|
|
connection->Open(path, O_RDONLY, *this);
|
|
|
|
} catch (...) {
|
|
|
|
OnNfsFileError(std::current_exception());
|
2014-03-15 15:29:10 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = State::OPEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::OnNfsConnectionFailed(std::exception_ptr e) noexcept
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
assert(state == State::MOUNT);
|
|
|
|
|
2014-11-25 13:50:36 +01:00
|
|
|
state = State::INITIAL;
|
|
|
|
|
2016-09-16 16:55:57 +02:00
|
|
|
OnNfsFileError(std::move(e));
|
2014-03-15 15:29:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::OnNfsConnectionDisconnected(std::exception_ptr e) noexcept
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
assert(state > State::MOUNT);
|
|
|
|
|
2014-11-25 12:29:55 +01:00
|
|
|
CancelOrClose();
|
2014-03-15 15:29:10 +01:00
|
|
|
|
2016-09-16 16:55:57 +02:00
|
|
|
OnNfsFileError(std::move(e));
|
2014-03-15 15:29:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::OpenCallback(nfsfh *_fh) noexcept
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
assert(state == State::OPEN);
|
|
|
|
assert(connection != nullptr);
|
|
|
|
assert(_fh != nullptr);
|
|
|
|
|
|
|
|
fh = _fh;
|
|
|
|
|
2020-04-23 14:51:09 +02:00
|
|
|
state = State::IDLE;
|
|
|
|
|
2016-09-16 16:55:57 +02:00
|
|
|
try {
|
|
|
|
connection->Stat(fh, *this);
|
|
|
|
} catch (...) {
|
|
|
|
OnNfsFileError(std::current_exception());
|
2014-03-15 15:29:10 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
state = State::STAT;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline void
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::StatCallback(const struct stat *st) noexcept
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
assert(state == State::STAT);
|
|
|
|
assert(connection != nullptr);
|
|
|
|
assert(fh != nullptr);
|
|
|
|
assert(st != nullptr);
|
|
|
|
|
2020-04-23 14:51:09 +02:00
|
|
|
state = State::IDLE;
|
|
|
|
|
2014-03-15 15:29:10 +01:00
|
|
|
if (!S_ISREG(st->st_mode)) {
|
2016-09-16 16:55:57 +02:00
|
|
|
OnNfsFileError(std::make_exception_ptr(std::runtime_error("Not a regular file")));
|
2014-03-15 15:29:10 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
OnNfsFileOpen(st->st_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::OnNfsCallback(unsigned status, void *data) noexcept
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
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
|
2017-11-12 18:09:07 +01:00
|
|
|
NfsFileReader::OnNfsError(std::exception_ptr &&e) noexcept
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
2014-11-25 13:03:09 +01:00
|
|
|
switch (state) {
|
|
|
|
case State::INITIAL:
|
|
|
|
case State::DEFER:
|
|
|
|
case State::MOUNT:
|
|
|
|
case State::IDLE:
|
|
|
|
assert(false);
|
|
|
|
gcc_unreachable();
|
|
|
|
|
|
|
|
case State::OPEN:
|
|
|
|
connection->RemoveLease(*this);
|
|
|
|
state = State::INITIAL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case State::STAT:
|
|
|
|
connection->RemoveLease(*this);
|
|
|
|
connection->Close(fh);
|
|
|
|
state = State::INITIAL;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case State::READ:
|
|
|
|
state = State::IDLE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2016-09-16 16:55:57 +02:00
|
|
|
OnNfsFileError(std::move(e));
|
2014-03-15 15:29:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2017-11-10 21:01:01 +01:00
|
|
|
NfsFileReader::OnDeferredOpen() noexcept
|
2014-03-15 15:29:10 +01:00
|
|
|
{
|
|
|
|
assert(state == State::DEFER);
|
|
|
|
|
|
|
|
state = State::MOUNT;
|
|
|
|
|
|
|
|
connection = &nfs_get_connection(server.c_str(), export_name.c_str());
|
|
|
|
connection->AddLease(*this);
|
|
|
|
}
|