SmbclientStorage.cxx 5.04 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 * 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"
22
#include "storage/StoragePlugin.hxx"
23 24 25
#include "storage/StorageInterface.hxx"
#include "storage/FileInfo.hxx"
#include "lib/smbclient/Init.hxx"
26
#include "lib/smbclient/Mutex.hxx"
27
#include "util/Error.hxx"
28
#include "thread/Mutex.hxx"
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59

#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() {
60
		smbclient_mutex.lock();
61
		smbc_free_context(ctx, 1);
62
		smbclient_mutex.unlock();
63 64 65 66 67 68 69 70 71 72
	}

	/* 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;
73 74

	virtual const char *MapToRelativeUTF8(const char *uri_utf8) const override;
75 76 77 78 79 80 81 82 83 84 85 86 87
};

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);
}

88 89 90 91 92 93
const char *
SmbclientStorage::MapToRelativeUTF8(const char *uri_utf8) const
{
	return PathTraitsUTF8::Relative(base.c_str(), uri_utf8);
}

94 95 96 97
static bool
GetInfo(const char *path, FileInfo &info, Error &error)
{
	struct stat st;
98 99 100 101
	smbclient_mutex.lock();
	bool success = smbc_stat(path, &st) == 0;
	smbclient_mutex.unlock();
	if (!success) {
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
		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);
132
	smbclient_mutex.lock();
133
	int handle = smbc_opendir(mapped.c_str());
134
	smbclient_mutex.unlock();
135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153
	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()
{
154
	smbclient_mutex.lock();
155
	smbc_close(handle);
156
	smbclient_mutex.unlock();
157 158 159 160 161
}

const char *
SmbclientDirectoryReader::Read()
{
162 163
	const ScopeLock protect(smbclient_mutex);

164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
	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);
}

182 183
static Storage *
CreateSmbclientStorageURI(const char *base, Error &error)
184
{
185 186 187
	if (memcmp(base, "smb://", 6) != 0)
		return nullptr;

188 189 190
	if (!SmbclientInit(error))
		return nullptr;

191
	const ScopeLock protect(smbclient_mutex);
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
	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);
}
207 208 209 210 211

const StoragePlugin smbclient_storage_plugin = {
	"smbclient",
	CreateSmbclientStorageURI,
};