diff options
Diffstat (limited to '')
-rw-r--r-- | src/storage/plugins/LocalStorage.cxx | 218 | ||||
-rw-r--r-- | src/storage/plugins/LocalStorage.hxx | 36 | ||||
-rw-r--r-- | src/storage/plugins/NfsStorage.cxx | 238 | ||||
-rw-r--r-- | src/storage/plugins/NfsStorage.hxx | 29 | ||||
-rw-r--r-- | src/storage/plugins/SmbclientStorage.cxx | 211 | ||||
-rw-r--r-- | src/storage/plugins/SmbclientStorage.hxx | 29 |
6 files changed, 761 insertions, 0 deletions
diff --git a/src/storage/plugins/LocalStorage.cxx b/src/storage/plugins/LocalStorage.cxx new file mode 100644 index 000000000..34d11569c --- /dev/null +++ b/src/storage/plugins/LocalStorage.cxx @@ -0,0 +1,218 @@ +/* + * 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 "LocalStorage.hxx" +#include "storage/StoragePlugin.hxx" +#include "storage/StorageInterface.hxx" +#include "storage/FileInfo.hxx" +#include "util/Error.hxx" +#include "fs/FileSystem.hxx" +#include "fs/AllocatedPath.hxx" +#include "fs/DirectoryReader.hxx" + +#include <string> + +class LocalDirectoryReader final : public StorageDirectoryReader { + AllocatedPath base_fs; + + DirectoryReader reader; + + std::string name_utf8; + +public: + LocalDirectoryReader(AllocatedPath &&_base_fs) + :base_fs(std::move(_base_fs)), reader(base_fs) {} + + bool HasFailed() { + return reader.HasFailed(); + } + + /* virtual methods from class StorageDirectoryReader */ + virtual const char *Read() override; + virtual bool GetInfo(bool follow, FileInfo &info, + Error &error) override; +}; + +class LocalStorage final : public Storage { + const AllocatedPath base_fs; + const std::string base_utf8; + +public: + explicit LocalStorage(Path _base_fs) + :base_fs(_base_fs), base_utf8(base_fs.ToUTF8()) { + assert(!base_fs.IsNull()); + assert(!base_utf8.empty()); + } + + /* virtual methods from class Storage */ + virtual bool GetInfo(const char *uri_utf8, bool follow, FileInfo &info, + Error &error) override; + + virtual StorageDirectoryReader *OpenDirectory(const char *uri_utf8, + Error &error) override; + + virtual std::string MapUTF8(const char *uri_utf8) const override; + + virtual AllocatedPath MapFS(const char *uri_utf8) const override; + + virtual const char *MapToRelativeUTF8(const char *uri_utf8) const override; + +private: + AllocatedPath MapFS(const char *uri_utf8, Error &error) const; +}; + +static bool +Stat(Path path, bool follow, FileInfo &info, Error &error) +{ + struct stat st; + if (!StatFile(path, st, follow)) { + error.SetErrno(); + + const auto path_utf8 = path.ToUTF8(); + error.FormatPrefix("Failed to stat %s: ", path_utf8.c_str()); + return false; + } + + if (S_ISREG(st.st_mode)) + info.type = FileInfo::Type::REGULAR; + else if (S_ISDIR(st.st_mode)) + info.type = FileInfo::Type::DIRECTORY; + else + info.type = FileInfo::Type::OTHER; + + info.size = st.st_size; + info.mtime = st.st_mtime; + info.device = st.st_dev; + info.inode = st.st_ino; + return true; +} + +std::string +LocalStorage::MapUTF8(const char *uri_utf8) const +{ + assert(uri_utf8 != nullptr); + + if (*uri_utf8 == 0) + return base_utf8; + + return PathTraitsUTF8::Build(base_utf8.c_str(), uri_utf8); +} + +AllocatedPath +LocalStorage::MapFS(const char *uri_utf8, Error &error) const +{ + assert(uri_utf8 != nullptr); + + if (*uri_utf8 == 0) + return base_fs; + + AllocatedPath path_fs = AllocatedPath::FromUTF8(uri_utf8, error); + if (!path_fs.IsNull()) + path_fs = AllocatedPath::Build(base_fs, path_fs); + + return path_fs; +} + +AllocatedPath +LocalStorage::MapFS(const char *uri_utf8) const +{ + return MapFS(uri_utf8, IgnoreError()); +} + +const char * +LocalStorage::MapToRelativeUTF8(const char *uri_utf8) const +{ + return PathTraitsUTF8::Relative(base_utf8.c_str(), uri_utf8); +} + +bool +LocalStorage::GetInfo(const char *uri_utf8, bool follow, FileInfo &info, + Error &error) +{ + AllocatedPath path_fs = MapFS(uri_utf8, error); + if (path_fs.IsNull()) + return false; + + return Stat(path_fs, follow, info, error); +} + +StorageDirectoryReader * +LocalStorage::OpenDirectory(const char *uri_utf8, Error &error) +{ + AllocatedPath path_fs = MapFS(uri_utf8, error); + if (path_fs.IsNull()) + return nullptr; + + LocalDirectoryReader *reader = + new LocalDirectoryReader(std::move(path_fs)); + if (reader->HasFailed()) { + error.FormatErrno("Failed to open '%s'", uri_utf8); + delete reader; + return nullptr; + } + + return reader; +} + +gcc_pure +static bool +SkipNameFS(const char *name_fs) +{ + return name_fs[0] == '.' && + (name_fs[1] == 0 || + (name_fs[1] == '.' && name_fs[2] == 0)); +} + +const char * +LocalDirectoryReader::Read() +{ + while (reader.ReadEntry()) { + const Path name_fs = reader.GetEntry(); + if (SkipNameFS(name_fs.c_str())) + continue; + + name_utf8 = name_fs.ToUTF8(); + if (name_utf8.empty()) + continue; + + return name_utf8.c_str(); + } + + return nullptr; +} + +bool +LocalDirectoryReader::GetInfo(bool follow, FileInfo &info, Error &error) +{ + const AllocatedPath path_fs = + AllocatedPath::Build(base_fs, reader.GetEntry()); + return Stat(path_fs, follow, info, error); +} + +Storage * +CreateLocalStorage(Path base_fs) +{ + return new LocalStorage(base_fs); +} + +const StoragePlugin local_storage_plugin = { + "local", + nullptr, +}; diff --git a/src/storage/plugins/LocalStorage.hxx b/src/storage/plugins/LocalStorage.hxx new file mode 100644 index 000000000..7295d38e7 --- /dev/null +++ b/src/storage/plugins/LocalStorage.hxx @@ -0,0 +1,36 @@ +/* + * 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. + */ + +#ifndef MPD_STORAGE_LOCAL_HXX +#define MPD_STORAGE_LOCAL_HXX + +#include "check.h" +#include "Compiler.h" + +struct StoragePlugin; +class Storage; +class Path; + +extern const StoragePlugin local_storage_plugin; + +gcc_malloc gcc_nonnull_all +Storage * +CreateLocalStorage(Path base_fs); + +#endif diff --git a/src/storage/plugins/NfsStorage.cxx b/src/storage/plugins/NfsStorage.cxx new file mode 100644 index 000000000..72c138feb --- /dev/null +++ b/src/storage/plugins/NfsStorage.cxx @@ -0,0 +1,238 @@ +/* + * 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 "NfsStorage.hxx" +#include "storage/StoragePlugin.hxx" +#include "storage/StorageInterface.hxx" +#include "storage/FileInfo.hxx" +#include "lib/nfs/Domain.hxx" +#include "util/Error.hxx" +#include "thread/Mutex.hxx" + +extern "C" { +#include <nfsc/libnfs.h> +#include <nfsc/libnfs-raw-nfs.h> +} + +#include <sys/stat.h> +#include <fcntl.h> + +class NfsDirectoryReader final : public StorageDirectoryReader { + const std::string base; + + nfs_context *ctx; + nfsdir *dir; + + nfsdirent *ent; + +public: + NfsDirectoryReader(const char *_base, nfs_context *_ctx, nfsdir *_dir) + :base(_base), ctx(_ctx), dir(_dir) {} + + virtual ~NfsDirectoryReader(); + + /* virtual methods from class StorageDirectoryReader */ + virtual const char *Read() override; + virtual bool GetInfo(bool follow, FileInfo &info, + Error &error) override; +}; + +class NfsStorage final : public Storage { + const std::string base; + + nfs_context *ctx; + +public: + NfsStorage(const char *_base, nfs_context *_ctx) + :base(_base), ctx(_ctx) {} + + virtual ~NfsStorage() { + nfs_destroy_context(ctx); + } + + /* virtual methods from class Storage */ + virtual bool GetInfo(const char *uri_utf8, bool follow, FileInfo &info, + Error &error) override; + + virtual StorageDirectoryReader *OpenDirectory(const char *uri_utf8, + Error &error) override; + + virtual std::string MapUTF8(const char *uri_utf8) const override; + + virtual const char *MapToRelativeUTF8(const char *uri_utf8) const override; +}; + +std::string +NfsStorage::MapUTF8(const char *uri_utf8) const +{ + assert(uri_utf8 != nullptr); + + if (*uri_utf8 == 0) + return base; + + return PathTraitsUTF8::Build(base.c_str(), uri_utf8); +} + +const char * +NfsStorage::MapToRelativeUTF8(const char *uri_utf8) const +{ + return PathTraitsUTF8::Relative(base.c_str(), uri_utf8); +} + +static bool +GetInfo(nfs_context *ctx, const char *path, FileInfo &info, Error &error) +{ + struct stat st; + int result = nfs_stat(ctx, path, &st); + if (result < 0) { + error.SetErrno(-result, "nfs_stat() failed"); + return false; + } + + if (S_ISREG(st.st_mode)) + info.type = FileInfo::Type::REGULAR; + else if (S_ISDIR(st.st_mode)) + info.type = FileInfo::Type::DIRECTORY; + else + info.type = FileInfo::Type::OTHER; + + info.size = st.st_size; + info.mtime = st.st_mtime; + info.device = st.st_dev; + info.inode = st.st_ino; + return true; +} + +bool +NfsStorage::GetInfo(const char *uri_utf8, gcc_unused bool follow, + FileInfo &info, Error &error) +{ + /* libnfs paths must begin with a slash */ + std::string path(uri_utf8); + path.insert(path.begin(), '/'); + + return ::GetInfo(ctx, path.c_str(), info, error); +} + +StorageDirectoryReader * +NfsStorage::OpenDirectory(const char *uri_utf8, Error &error) +{ + /* libnfs paths must begin with a slash */ + std::string path(uri_utf8); + path.insert(path.begin(), '/'); + + nfsdir *dir; + int result = nfs_opendir(ctx, path.c_str(), &dir); + if (result < 0) { + error.SetErrno(-result, "nfs_opendir() failed"); + return nullptr; + } + + return new NfsDirectoryReader(uri_utf8, ctx, dir); +} + +gcc_pure +static bool +SkipNameFS(const char *name) +{ + return name[0] == '.' && + (name[1] == 0 || + (name[1] == '.' && name[2] == 0)); +} + +NfsDirectoryReader::~NfsDirectoryReader() +{ + nfs_closedir(ctx, dir); +} + +const char * +NfsDirectoryReader::Read() +{ + while ((ent = nfs_readdir(ctx, dir)) != nullptr) { + if (!SkipNameFS(ent->name)) + return ent->name; + } + + return nullptr; +} + +bool +NfsDirectoryReader::GetInfo(gcc_unused bool follow, FileInfo &info, + gcc_unused Error &error) +{ + assert(ent != nullptr); + + switch (ent->type) { + case NF3REG: + info.type = FileInfo::Type::REGULAR; + break; + + case NF3DIR: + info.type = FileInfo::Type::DIRECTORY; + break; + + default: + info.type = FileInfo::Type::OTHER; + break; + } + + info.size = ent->size; + info.mtime = ent->mtime.tv_sec; + info.device = 0; + info.inode = ent->inode; + return true; +} + +static Storage * +CreateNfsStorageURI(const char *base, Error &error) +{ + if (memcmp(base, "nfs://", 6) != 0) + return nullptr; + + const char *p = base + 6; + + const char *mount = strchr(p, '/'); + if (mount == nullptr) { + error.Set(nfs_domain, "Malformed nfs:// URI"); + return nullptr; + } + + const std::string server(p, mount); + + nfs_context *ctx = nfs_init_context(); + if (ctx == nullptr) { + error.Set(nfs_domain, "nfs_init_context() failed"); + return nullptr; + } + + int result = nfs_mount(ctx, server.c_str(), mount); + if (result < 0) { + nfs_destroy_context(ctx); + error.SetErrno(-result, "nfs_mount() failed"); + return nullptr; + } + + return new NfsStorage(base, ctx); +} + +const StoragePlugin nfs_storage_plugin = { + "nfs", + CreateNfsStorageURI, +}; diff --git a/src/storage/plugins/NfsStorage.hxx b/src/storage/plugins/NfsStorage.hxx new file mode 100644 index 000000000..f7e18effc --- /dev/null +++ b/src/storage/plugins/NfsStorage.hxx @@ -0,0 +1,29 @@ +/* + * 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. + */ + +#ifndef MPD_STORAGE_NFS_HXX +#define MPD_STORAGE_NFS_HXX + +#include "check.h" + +struct StoragePlugin; + +extern const StoragePlugin nfs_storage_plugin; + +#endif diff --git a/src/storage/plugins/SmbclientStorage.cxx b/src/storage/plugins/SmbclientStorage.cxx new file mode 100644 index 000000000..a73c8d65c --- /dev/null +++ b/src/storage/plugins/SmbclientStorage.cxx @@ -0,0 +1,211 @@ +/* + * 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 "SmbclientStorage.hxx" +#include "storage/StoragePlugin.hxx" +#include "storage/StorageInterface.hxx" +#include "storage/FileInfo.hxx" +#include "lib/smbclient/Init.hxx" +#include "lib/smbclient/Mutex.hxx" +#include "util/Error.hxx" +#include "thread/Mutex.hxx" + +#include <libsmbclient.h> + +class SmbclientDirectoryReader final : public StorageDirectoryReader { + const std::string base; + const unsigned handle; + + const char *name; + +public: + SmbclientDirectoryReader(std::string &&_base, unsigned _handle) + :base(std::move(_base)), handle(_handle) {} + + virtual ~SmbclientDirectoryReader(); + + /* virtual methods from class StorageDirectoryReader */ + virtual const char *Read() override; + virtual bool GetInfo(bool follow, FileInfo &info, + Error &error) override; +}; + +class SmbclientStorage final : public Storage { + const std::string base; + + SMBCCTX *const ctx; + +public: + SmbclientStorage(const char *_base, SMBCCTX *_ctx) + :base(_base), ctx(_ctx) {} + + virtual ~SmbclientStorage() { + smbclient_mutex.lock(); + smbc_free_context(ctx, 1); + smbclient_mutex.unlock(); + } + + /* virtual methods from class Storage */ + virtual bool GetInfo(const char *uri_utf8, bool follow, FileInfo &info, + Error &error) override; + + virtual StorageDirectoryReader *OpenDirectory(const char *uri_utf8, + Error &error) override; + + virtual std::string MapUTF8(const char *uri_utf8) const override; + + virtual const char *MapToRelativeUTF8(const char *uri_utf8) const override; +}; + +std::string +SmbclientStorage::MapUTF8(const char *uri_utf8) const +{ + assert(uri_utf8 != nullptr); + + if (*uri_utf8 == 0) + return base; + + return PathTraitsUTF8::Build(base.c_str(), uri_utf8); +} + +const char * +SmbclientStorage::MapToRelativeUTF8(const char *uri_utf8) const +{ + return PathTraitsUTF8::Relative(base.c_str(), uri_utf8); +} + +static bool +GetInfo(const char *path, FileInfo &info, Error &error) +{ + struct stat st; + smbclient_mutex.lock(); + bool success = smbc_stat(path, &st) == 0; + smbclient_mutex.unlock(); + if (!success) { + error.SetErrno(); + return false; + } + + if (S_ISREG(st.st_mode)) + info.type = FileInfo::Type::REGULAR; + else if (S_ISDIR(st.st_mode)) + info.type = FileInfo::Type::DIRECTORY; + else + info.type = FileInfo::Type::OTHER; + + info.size = st.st_size; + info.mtime = st.st_mtime; + info.device = st.st_dev; + info.inode = st.st_ino; + return true; +} + +bool +SmbclientStorage::GetInfo(const char *uri_utf8, gcc_unused bool follow, + FileInfo &info, Error &error) +{ + const std::string mapped = MapUTF8(uri_utf8); + return ::GetInfo(mapped.c_str(), info, error); +} + +StorageDirectoryReader * +SmbclientStorage::OpenDirectory(const char *uri_utf8, Error &error) +{ + std::string mapped = MapUTF8(uri_utf8); + smbclient_mutex.lock(); + int handle = smbc_opendir(mapped.c_str()); + smbclient_mutex.unlock(); + if (handle < 0) { + error.SetErrno(); + return nullptr; + } + + return new SmbclientDirectoryReader(std::move(mapped.c_str()), handle); +} + +gcc_pure +static bool +SkipNameFS(const char *name) +{ + return name[0] == '.' && + (name[1] == 0 || + (name[1] == '.' && name[2] == 0)); +} + +SmbclientDirectoryReader::~SmbclientDirectoryReader() +{ + smbclient_mutex.lock(); + smbc_close(handle); + smbclient_mutex.unlock(); +} + +const char * +SmbclientDirectoryReader::Read() +{ + const ScopeLock protect(smbclient_mutex); + + struct smbc_dirent *e; + while ((e = smbc_readdir(handle)) != nullptr) { + name = e->name; + if (!SkipNameFS(name)) + return name; + } + + return nullptr; +} + +bool +SmbclientDirectoryReader::GetInfo(gcc_unused bool follow, FileInfo &info, + Error &error) +{ + const std::string path = PathTraitsUTF8::Build(base.c_str(), name); + return ::GetInfo(path.c_str(), info, error); +} + +static Storage * +CreateSmbclientStorageURI(const char *base, Error &error) +{ + if (memcmp(base, "smb://", 6) != 0) + return nullptr; + + if (!SmbclientInit(error)) + return nullptr; + + const ScopeLock protect(smbclient_mutex); + SMBCCTX *ctx = smbc_new_context(); + if (ctx == nullptr) { + error.SetErrno("smbc_new_context() failed"); + return nullptr; + } + + SMBCCTX *ctx2 = smbc_init_context(ctx); + if (ctx2 == nullptr) { + error.SetErrno("smbc_init_context() failed"); + smbc_free_context(ctx, 1); + return nullptr; + } + + return new SmbclientStorage(base, ctx2); +} + +const StoragePlugin smbclient_storage_plugin = { + "smbclient", + CreateSmbclientStorageURI, +}; diff --git a/src/storage/plugins/SmbclientStorage.hxx b/src/storage/plugins/SmbclientStorage.hxx new file mode 100644 index 000000000..7c198d920 --- /dev/null +++ b/src/storage/plugins/SmbclientStorage.hxx @@ -0,0 +1,29 @@ +/* + * 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. + */ + +#ifndef MPD_STORAGE_SMBCLIENT_HXX +#define MPD_STORAGE_SMBCLIENT_HXX + +#include "check.h" + +struct StoragePlugin; + +extern const StoragePlugin smbclient_storage_plugin; + +#endif |