20#include <sys/iosupport.h>
23#include <smb2/libsmb2.h>
26#include "WutSmbDriver.h"
29smb2_context * WutSmbDriver::ctx =
nullptr;
32static const char *
const kSmbDeviceName =
"smb";
37static char g_lastError[256] =
"";
39static void CaptureSmb2Error(
const char * context)
43 if(detail && detail[0])
44 snprintf(g_lastError,
sizeof(g_lastError),
"%s: %s", context, detail);
46 snprintf(g_lastError,
sizeof(g_lastError),
"%s", context);
52static const char * RelativePath(
const char * path)
57 const char * colon = strchr(path,
':');
66static int SetErrnoFromSmb2(
int smb2Result)
69 errno = (smb2Result < 0) ? -smb2Result : EIO;
73static void FillStatFromSmb2(
struct stat * st,
const smb2_stat_64 & src)
75 memset(st, 0,
sizeof(
struct stat));
76 st->st_size = (off_t)src.smb2_size;
77 st->st_mode = (src.smb2_type == SMB2_TYPE_DIRECTORY) ? (S_IFDIR | 0777) : (S_IFREG | 0666);
78 st->st_nlink = src.smb2_nlink ? src.smb2_nlink : 1;
79 st->st_mtime = (time_t)src.smb2_mtime;
80 st->st_atime = (time_t)src.smb2_atime;
81 st->st_ctime = (time_t)src.smb2_ctime;
97static int smb_open_r(
struct _reent *,
void * fileStruct,
const char * path,
int flags,
int)
105 bool append = (flags & O_APPEND) != 0;
109 CaptureSmb2Error(
"open");
110 return SetErrnoFromSmb2(-EIO);
120 state->
offset = st.smb2_size;
126static int smb_close_r(
struct _reent *,
void * fileStruct)
133static ssize_t smb_write_r(
struct _reent *,
void * fileStruct,
const char * ptr,
size_t len)
138 return SetErrnoFromSmb2(written);
144static ssize_t smb_read_r(
struct _reent *,
void * fileStruct,
char * ptr,
size_t len)
149 return SetErrnoFromSmb2(bytesRead);
151 state->
offset += bytesRead;
155static off_t smb_seek_r(
struct _reent *,
void * fileStruct, off_t pos,
int dir)
162 else if(dir == SEEK_END)
166 return SetErrnoFromSmb2(-EIO);
171 state->
offset = base + pos;
172 return (off_t)state->
offset;
175static int smb_fstat_r(
struct _reent *,
void * fileStruct,
struct stat * st)
182 CaptureSmb2Error(
"fstat");
183 return SetErrnoFromSmb2(result);
186 FillStatFromSmb2(st, s);
190static int smb_stat_r(
struct _reent *,
const char * path,
struct stat * st)
196 CaptureSmb2Error(
"stat");
197 return SetErrnoFromSmb2(result);
200 FillStatFromSmb2(st, s);
204static int smb_unlink_r(
struct _reent *,
const char * path)
207 return (result < 0) ? SetErrnoFromSmb2(result) : 0;
210static int smb_mkdir_r(
struct _reent *,
const char * path,
int)
213 return (result < 0) ? SetErrnoFromSmb2(result) : 0;
216static int smb_rmdir_r(
struct _reent *,
const char * path)
219 return (result < 0) ? SetErrnoFromSmb2(result) : 0;
231static DIR_ITER * smb_diropen_r(
struct _reent *, DIR_ITER * dirState,
const char * path)
233 const char * relPath = RelativePath(path);
238 snprintf(context,
sizeof(context),
"opendir '%s'", relPath[0] ? relPath :
"(root)");
239 CaptureSmb2Error(context);
240 SetErrnoFromSmb2(-ENOENT);
248static int smb_dirreset_r(
struct _reent *, DIR_ITER * dirState)
254static int smb_dirnext_r(
struct _reent *, DIR_ITER * dirState,
char * filename,
struct stat * filestat)
256 smb2dir * dir = ((
SmbDirState *)dirState->dirStruct)->dir;
264 strncpy(filename, entry->name, NAME_MAX);
265 filename[NAME_MAX] =
'\0';
266 FillStatFromSmb2(filestat, entry->st);
270static int smb_dirclose_r(
struct _reent *, DIR_ITER * dirState)
279static devoptab_t BuildSmbDevoptab()
281 devoptab_t dotab = {};
283 dotab.name = kSmbDeviceName;
285 dotab.open_r = smb_open_r;
286 dotab.close_r = smb_close_r;
287 dotab.write_r = smb_write_r;
288 dotab.read_r = smb_read_r;
289 dotab.seek_r = smb_seek_r;
290 dotab.fstat_r = smb_fstat_r;
291 dotab.stat_r = smb_stat_r;
292 dotab.unlink_r = smb_unlink_r;
293 dotab.mkdir_r = smb_mkdir_r;
294 dotab.rmdir_r = smb_rmdir_r;
296 dotab.diropen_r = smb_diropen_r;
297 dotab.dirreset_r = smb_dirreset_r;
298 dotab.dirnext_r = smb_dirnext_r;
299 dotab.dirclose_r = smb_dirclose_r;
311 NNResult result = ACInitialize();
312 acInitialized = NNResult_IsSuccess(result);
329 acInitialized =
false;
339 NNResult result = ACIsApplicationConnected(&
isConnected);
347 snprintf(g_lastError,
sizeof(g_lastError),
"AC not initialized");
355 NNResult result = ACIsApplicationConnected(&
isConnected);
359 result = ACConnect();
360 if(NNResult_IsFailure(result))
362 snprintf(g_lastError,
sizeof(g_lastError),
"ACConnect failed (0x%08X)", (
unsigned)result.value);
372 g_lastError[0] =
'\0';
374 if(info.
host[0] ==
'\0' || info.
share[0] ==
'\0')
377 if(ctx && strcmp(current.host, info.
host) == 0 && strcmp(current.share, info.
share) == 0 && strcmp(current.user, info.
user) == 0)
378 return SmbConnectResult::Success;
385 ctx = smb2_init_context();
388 snprintf(g_lastError,
sizeof(g_lastError),
"smb2_init_context() failed");
392 smb2_set_security_mode(ctx, SMB2_NEGOTIATE_SIGNING_ENABLED);
393 if(info.
user[0] !=
'\0')
394 smb2_set_user(ctx, info.
user);
395 smb2_set_password(ctx, info.
password);
397 if(smb2_connect_share(ctx, info.
host, info.
share, info.
user[0] ? info.
user :
nullptr) != 0)
399 CaptureSmb2Error(
"connect_share");
400 smb2_destroy_context(ctx);
405 static devoptab_t smbDevoptab = BuildSmbDevoptab();
406 int devnum = AddDevice(&smbDevoptab);
410 snprintf(g_lastError,
sizeof(g_lastError),
"AddDevice(\"%s\") failed", smbDevoptab.name);
411 smb2_disconnect_share(ctx);
412 smb2_destroy_context(ctx);
416 devoptabAdded =
true;
419 return SmbConnectResult::Success;
426 RemoveDevice(
"smb:");
427 devoptabAdded =
false;
432 smb2_disconnect_share(ctx);
433 smb2_destroy_context(ctx);
437 memset(¤t, 0,
sizeof(current));
444 case SmbConnectResult::Success:
return "Connected.";
448 default:
return "Unknown network share error.";
void disconnect() override
Unmounts and drops the connection.
SmbConnectResult connect(const SmbShareInfo &info) override
Attempts to connect and mount in one call.
const char * connectResultMessage(SmbConnectResult result) const override
Short, user-displayable reason for a non-Success SmbConnectResult.
void init() override
One-time setup.
const char * getLastError() const override
Raw, implementation-specific detail behind the last failure.
bool isNetworkUp() const override
True if the network is already up, i.e.
bool ensureNetworkUp() override
Brings the Wii U network connection up if it isn't already, via nn::ac (ACConnect()).
void shutdown() override
Disconnects if connected and releases whatever init() set up.
bool isConnected() const override
static smb2_context * getContext()
The active context, used by the free-function devoptab callbacks in WutSmbDriver.cpp.
SmbConnectResult
Result of a single connect() attempt.
@ ConnectFailed
network's up but the server/share/credentials didn't work
@ InvalidSettings
host or share is empty - nothing to try
@ NetworkUnavailable
no usable network connection at all
Per-open-directory state for the smb: devoptab directory callbacks.
smb2dir * dir
libsmb2 handle of the directory being iterated
Per-open-file state for the smb: devoptab file callbacks.
smb2fh * fh
libsmb2 handle of the open file
uint64_t offset
Current read/write position, passed explicitly to smb2_pread/smb2_pwrite.
Credentials/target for a single SMB share.
char share[64]
share name, eg.
char password[32]
"" for guest/anonymous
char user[32]
"" for guest/anonymous
char host[64]
server IP or hostname