*/smbclient: use the new API with SMBCCTX parameter

As a side effect, the input plugin closes the SMB/CIFS connection
after closing the file.

This solves one part of
https://github.com/MusicPlayerDaemon/MPD/issues/916
This commit is contained in:
Max Kellermann 2020-07-20 21:10:58 +02:00
parent 697531a948
commit f6dc9bcad6
5 changed files with 93 additions and 42 deletions

1
NEWS
View File

@ -12,6 +12,7 @@ ver 0.22 (not yet released)
- curl: support "charset" parameter in URI fragment - curl: support "charset" parameter in URI fragment
- ffmpeg: allow partial reads - ffmpeg: allow partial reads
- io_uring: new plugin for local files on Linux (using liburing) - io_uring: new plugin for local files on Linux (using liburing)
- smbclient: close unused SMB/CIFS connections
* archive * archive
- iso9660: support seeking - iso9660: support seeking
* database * database

View File

@ -31,15 +31,15 @@
class SmbclientInputStream final : public InputStream { class SmbclientInputStream final : public InputStream {
SmbclientContext ctx; SmbclientContext ctx;
int fd; SMBCFILE *const handle;
public: public:
SmbclientInputStream(const char *_uri, SmbclientInputStream(const char *_uri,
Mutex &_mutex, Mutex &_mutex,
SmbclientContext &&_ctx, SmbclientContext &&_ctx,
int _fd, const struct stat &st) SMBCFILE *_handle, const struct stat &st)
:InputStream(_uri, _mutex), :InputStream(_uri, _mutex),
ctx(std::move(_ctx)), fd(_fd) ctx(std::move(_ctx)), handle(_handle)
{ {
seekable = true; seekable = true;
size = st.st_size; size = st.st_size;
@ -48,7 +48,7 @@ public:
~SmbclientInputStream() override { ~SmbclientInputStream() override {
const std::lock_guard<Mutex> lock(smbclient_mutex); const std::lock_guard<Mutex> lock(smbclient_mutex);
smbc_close(fd); ctx.Close(handle);
} }
/* virtual methods from InputStream */ /* virtual methods from InputStream */
@ -89,18 +89,18 @@ input_smbclient_open(const char *uri,
const std::lock_guard<Mutex> protect(smbclient_mutex); const std::lock_guard<Mutex> protect(smbclient_mutex);
int fd = smbc_open(uri, O_RDONLY, 0); SMBCFILE *handle = ctx.OpenReadOnly(uri);
if (fd < 0) if (handle == nullptr)
throw MakeErrno("smbc_open() failed"); throw MakeErrno("smbc_open() failed");
struct stat st; struct stat st;
if (smbc_fstat(fd, &st) < 0) if (ctx.Stat(handle, st) < 0)
throw MakeErrno("smbc_fstat() failed"); throw MakeErrno("smbc_fstat() failed");
return std::make_unique<MaybeBufferedInputStream> return std::make_unique<MaybeBufferedInputStream>
(std::make_unique<SmbclientInputStream>(uri, mutex, (std::make_unique<SmbclientInputStream>(uri, mutex,
std::move(ctx), std::move(ctx),
fd, st)); handle, st));
} }
size_t size_t
@ -112,7 +112,7 @@ SmbclientInputStream::Read(std::unique_lock<Mutex> &,
{ {
const ScopeUnlock unlock(mutex); const ScopeUnlock unlock(mutex);
const std::lock_guard<Mutex> lock(smbclient_mutex); const std::lock_guard<Mutex> lock(smbclient_mutex);
nbytes = smbc_read(fd, ptr, read_size); nbytes = ctx.Read(handle, ptr, read_size);
} }
if (nbytes < 0) if (nbytes < 0)
@ -131,7 +131,7 @@ SmbclientInputStream::Seek(std::unique_lock<Mutex> &,
{ {
const ScopeUnlock unlock(mutex); const ScopeUnlock unlock(mutex);
const std::lock_guard<Mutex> lock(smbclient_mutex); const std::lock_guard<Mutex> lock(smbclient_mutex);
result = smbc_lseek(fd, new_offset, SEEK_SET); result = ctx.Seek(handle, new_offset);
} }
if (result < 0) if (result < 0)

View File

@ -54,6 +54,46 @@ public:
* Throws on error. * Throws on error.
*/ */
static SmbclientContext New(); static SmbclientContext New();
SMBCFILE *Open(const char *fname, int flags, mode_t mode) noexcept {
return smbc_getFunctionOpen(ctx)(ctx, fname, flags, mode);
}
SMBCFILE *OpenReadOnly(const char *fname) noexcept {
return Open(fname, O_RDONLY, 0);
}
ssize_t Read(SMBCFILE *file, void *buf, size_t count) noexcept {
return smbc_getFunctionRead(ctx)(ctx, file, buf, count);
}
off_t Seek(SMBCFILE *file, off_t offset, int whence=SEEK_SET) noexcept {
return smbc_getFunctionLseek(ctx)(ctx, file, offset, whence);
}
int Stat(const char *fname, struct stat &st) noexcept {
return smbc_getFunctionStat(ctx)(ctx, fname, &st);
}
int Stat(SMBCFILE *file, struct stat &st) noexcept {
return smbc_getFunctionFstat(ctx)(ctx, file, &st);
}
void Close(SMBCFILE *file) noexcept {
smbc_getFunctionClose(ctx)(ctx, file);
}
SMBCFILE *OpenDirectory(const char *fname) noexcept {
return smbc_getFunctionOpendir(ctx)(ctx, fname);
}
void CloseDirectory(SMBCFILE *dir) noexcept {
smbc_getFunctionClosedir(ctx)(ctx, dir);
}
const struct smbc_dirent *ReadDirectory(SMBCFILE *dir) noexcept {
return smbc_getFunctionReaddir(ctx)(ctx, dir);
}
}; };
#endif #endif

View File

@ -19,6 +19,7 @@
#include "SmbclientNeighborPlugin.hxx" #include "SmbclientNeighborPlugin.hxx"
#include "lib/smbclient/Init.hxx" #include "lib/smbclient/Init.hxx"
#include "lib/smbclient/Context.hxx"
#include "lib/smbclient/Domain.hxx" #include "lib/smbclient/Domain.hxx"
#include "lib/smbclient/Mutex.hxx" #include "lib/smbclient/Mutex.hxx"
#include "neighbor/NeighborPlugin.hxx" #include "neighbor/NeighborPlugin.hxx"
@ -56,6 +57,8 @@ class SmbclientNeighborExplorer final : public NeighborExplorer {
} }
}; };
SmbclientContext ctx = SmbclientContext::New();
Thread thread; Thread thread;
mutable Mutex mutex; mutable Mutex mutex;
@ -66,7 +69,7 @@ class SmbclientNeighborExplorer final : public NeighborExplorer {
bool quit; bool quit;
public: public:
explicit SmbclientNeighborExplorer(NeighborListener &_listener) noexcept explicit SmbclientNeighborExplorer(NeighborListener &_listener)
:NeighborExplorer(_listener), :NeighborExplorer(_listener),
thread(BIND_THIS_METHOD(ThreadFunc)) {} thread(BIND_THIS_METHOD(ThreadFunc)) {}
@ -125,21 +128,24 @@ ReadServer(NeighborExplorer::List &list, const smbc_dirent &e) noexcept
} }
static void static void
ReadServers(NeighborExplorer::List &list, const char *uri) noexcept; ReadServers(SmbclientContext &ctx, const char *uri,
NeighborExplorer::List &list) noexcept;
static void static void
ReadWorkgroup(NeighborExplorer::List &list, const std::string &name) noexcept ReadWorkgroup(SmbclientContext &ctx, const std::string &name,
NeighborExplorer::List &list) noexcept
{ {
std::string uri = "smb://" + name; std::string uri = "smb://" + name;
ReadServers(list, uri.c_str()); ReadServers(ctx, uri.c_str(), list);
} }
static void static void
ReadEntry(NeighborExplorer::List &list, const smbc_dirent &e) noexcept ReadEntry(SmbclientContext &ctx, const smbc_dirent &e,
NeighborExplorer::List &list) noexcept
{ {
switch (e.smbc_type) { switch (e.smbc_type) {
case SMBC_WORKGROUP: case SMBC_WORKGROUP:
ReadWorkgroup(list, std::string(e.name, e.namelen)); ReadWorkgroup(ctx, std::string(e.name, e.namelen), list);
break; break;
case SMBC_SERVER: case SMBC_SERVER:
@ -149,20 +155,21 @@ ReadEntry(NeighborExplorer::List &list, const smbc_dirent &e) noexcept
} }
static void static void
ReadServers(NeighborExplorer::List &list, int fd) noexcept ReadServers(SmbclientContext &ctx, SMBCFILE *handle,
NeighborExplorer::List &list) noexcept
{ {
smbc_dirent *e; while (auto e = ctx.ReadDirectory(handle))
while ((e = smbc_readdir(fd)) != nullptr) ReadEntry(ctx, *e, list);
ReadEntry(list, *e);
} }
static void static void
ReadServers(NeighborExplorer::List &list, const char *uri) noexcept ReadServers(SmbclientContext &ctx, const char *uri,
NeighborExplorer::List &list) noexcept
{ {
int fd = smbc_opendir(uri); SMBCFILE *handle = ctx.OpenDirectory(uri);
if (fd >= 0) { if (handle != nullptr) {
ReadServers(list, fd); ReadServers(ctx, handle, list);
smbc_closedir(fd); ctx.CloseDirectory(handle);
} else } else
FormatErrno(smbclient_domain, "smbc_opendir('%s') failed", FormatErrno(smbclient_domain, "smbc_opendir('%s') failed",
uri); uri);
@ -170,11 +177,11 @@ ReadServers(NeighborExplorer::List &list, const char *uri) noexcept
gcc_pure gcc_pure
static NeighborExplorer::List static NeighborExplorer::List
DetectServers() noexcept DetectServers(SmbclientContext &ctx) noexcept
{ {
NeighborExplorer::List list; NeighborExplorer::List list;
const std::lock_guard<Mutex> protect(smbclient_mutex); const std::lock_guard<Mutex> protect(smbclient_mutex);
ReadServers(list, "smb://"); ReadServers(ctx, "smb://", list);
return list; return list;
} }
@ -198,7 +205,7 @@ SmbclientNeighborExplorer::Run() noexcept
{ {
const ScopeUnlock unlock(mutex); const ScopeUnlock unlock(mutex);
found = DetectServers(); found = DetectServers(ctx);
} }
const auto found_before_begin = found.before_begin(); const auto found_before_begin = found.before_begin();

View File

@ -34,14 +34,17 @@
#include <libsmbclient.h> #include <libsmbclient.h>
class SmbclientDirectoryReader final : public StorageDirectoryReader { class SmbclientDirectoryReader final : public StorageDirectoryReader {
SmbclientContext &ctx;
const std::string base; const std::string base;
const unsigned handle; SMBCFILE *const handle;
const char *name; const char *name;
public: public:
SmbclientDirectoryReader(std::string &&_base, unsigned _handle) SmbclientDirectoryReader(SmbclientContext &_ctx,
:base(std::move(_base)), handle(_handle) {} std::string &&_base,
SMBCFILE *_handle) noexcept
:ctx(_ctx), base(std::move(_base)), handle(_handle) {}
~SmbclientDirectoryReader() override; ~SmbclientDirectoryReader() override;
@ -85,13 +88,13 @@ SmbclientStorage::MapToRelativeUTF8(std::string_view uri_utf8) const noexcept
} }
static StorageFileInfo static StorageFileInfo
GetInfo(const char *path) GetInfo(SmbclientContext &ctx, const char *path)
{ {
struct stat st; struct stat st;
{ {
const std::lock_guard<Mutex> protect(smbclient_mutex); const std::lock_guard<Mutex> protect(smbclient_mutex);
if (smbc_stat(path, &st) != 0) if (ctx.Stat(path, st) != 0)
throw MakeErrno("Failed to access file"); throw MakeErrno("Failed to access file");
} }
@ -114,7 +117,7 @@ StorageFileInfo
SmbclientStorage::GetInfo(std::string_view uri_utf8, [[maybe_unused]] bool follow) SmbclientStorage::GetInfo(std::string_view uri_utf8, [[maybe_unused]] bool follow)
{ {
const std::string mapped = MapUTF8(uri_utf8); const std::string mapped = MapUTF8(uri_utf8);
return ::GetInfo(mapped.c_str()); return ::GetInfo(ctx, mapped.c_str());
} }
std::unique_ptr<StorageDirectoryReader> std::unique_ptr<StorageDirectoryReader>
@ -122,16 +125,17 @@ SmbclientStorage::OpenDirectory(std::string_view uri_utf8)
{ {
std::string mapped = MapUTF8(uri_utf8); std::string mapped = MapUTF8(uri_utf8);
int handle; SMBCFILE *handle;
{ {
const std::lock_guard<Mutex> protect(smbclient_mutex); const std::lock_guard<Mutex> protect(smbclient_mutex);
handle = smbc_opendir(mapped.c_str()); handle = ctx.OpenDirectory(mapped.c_str());
if (handle < 0) if (handle == nullptr)
throw MakeErrno("Failed to open directory"); throw MakeErrno("Failed to open directory");
} }
return std::make_unique<SmbclientDirectoryReader>(std::move(mapped), return std::make_unique<SmbclientDirectoryReader>(ctx,
std::move(mapped),
handle); handle);
} }
@ -147,7 +151,7 @@ SkipNameFS(const char *name) noexcept
SmbclientDirectoryReader::~SmbclientDirectoryReader() SmbclientDirectoryReader::~SmbclientDirectoryReader()
{ {
const std::lock_guard<Mutex> lock(smbclient_mutex); const std::lock_guard<Mutex> lock(smbclient_mutex);
smbc_close(handle); ctx.CloseDirectory(handle);
} }
const char * const char *
@ -155,8 +159,7 @@ SmbclientDirectoryReader::Read() noexcept
{ {
const std::lock_guard<Mutex> protect(smbclient_mutex); const std::lock_guard<Mutex> protect(smbclient_mutex);
struct smbc_dirent *e; while (auto e = ctx.ReadDirectory(handle)) {
while ((e = smbc_readdir(handle)) != nullptr) {
name = e->name; name = e->name;
if (!SkipNameFS(name)) if (!SkipNameFS(name))
return name; return name;
@ -169,7 +172,7 @@ StorageFileInfo
SmbclientDirectoryReader::GetInfo([[maybe_unused]] bool follow) SmbclientDirectoryReader::GetInfo([[maybe_unused]] bool follow)
{ {
const std::string path = PathTraitsUTF8::Build(base, name); const std::string path = PathTraitsUTF8::Build(base, name);
return ::GetInfo(path.c_str()); return ::GetInfo(ctx, path.c_str());
} }
static std::unique_ptr<Storage> static std::unique_ptr<Storage>