mirror of
https://github.com/cshepherd/gscifs.git
synced 2025-01-14 11:29:45 +00:00
1552 lines
33 KiB
C
1552 lines
33 KiB
C
/****************************************************************************
|
|
* TinySMB
|
|
* Nintendo Wii/GameCube SMB implementation
|
|
*
|
|
* SMB devoptab
|
|
****************************************************************************/
|
|
#include <malloc.h>
|
|
#include <sys/iosupport.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <dirent.h>
|
|
|
|
#include <ogc/lwp.h>
|
|
#include <ogc/lwp_watchdog.h>
|
|
#include <ogc/mutex.h>
|
|
|
|
#include "smb.h"
|
|
|
|
#define MAX_SMB_MOUNTED 10
|
|
|
|
static lwp_t cache_thread = LWP_THREAD_NULL;
|
|
static SMBDIRENTRY last_dentry;
|
|
static int last_env=-1;
|
|
static char last_path[SMB_MAXPATH];
|
|
|
|
|
|
typedef struct
|
|
{
|
|
SMBFILE handle;
|
|
off_t offset;
|
|
off_t len;
|
|
char filename[SMB_MAXPATH];
|
|
unsigned short access;
|
|
int env;
|
|
u32 attributes;
|
|
} SMBFILESTRUCT;
|
|
|
|
typedef struct
|
|
{
|
|
SMBDIRENTRY smbdir;
|
|
int env;
|
|
char dir[SMB_MAXPATH];
|
|
} SMBDIRSTATESTRUCT;
|
|
|
|
static bool smbInited = false;
|
|
static unsigned short smbFlags = SMB_SRCH_DIRECTORY | SMB_SRCH_READONLY;
|
|
|
|
///////////////////////////////////////////
|
|
// CACHE FUNCTION DEFINITIONS //
|
|
///////////////////////////////////////////
|
|
#define SMB_READ_BUFFERSIZE 65472
|
|
#define SMB_WRITE_BUFFERSIZE (60*1024)
|
|
|
|
typedef struct
|
|
{
|
|
off_t offset;
|
|
u64 last_used;
|
|
SMBFILESTRUCT *file;
|
|
void *ptr;
|
|
} smb_cache_page;
|
|
|
|
typedef struct
|
|
{
|
|
u64 used;
|
|
size_t len;
|
|
SMBFILESTRUCT *file;
|
|
void *ptr;
|
|
} smb_write_cache;
|
|
|
|
static void DestroySMBReadAheadCache(const char *name);
|
|
static void SMBEnableReadAhead(const char *name, u32 pages);
|
|
static int ReadSMBFromCache(void *buf, size_t len, SMBFILESTRUCT *file);
|
|
|
|
///////////////////////////////////////////
|
|
// END CACHE FUNCTION DEFINITIONS //
|
|
///////////////////////////////////////////
|
|
|
|
// SMB Enviroment
|
|
typedef struct
|
|
{
|
|
char *name;
|
|
int pos;
|
|
devoptab_t *devoptab;
|
|
|
|
SMBCONN smbconn;
|
|
u8 SMBCONNECTED;
|
|
|
|
char currentpath[SMB_MAXPATH];
|
|
bool first_item_dir;
|
|
bool diropen_root;
|
|
|
|
smb_write_cache SMBWriteCache;
|
|
smb_cache_page *SMBReadAheadCache;
|
|
int SMB_RA_pages;
|
|
|
|
mutex_t _SMB_mutex;
|
|
} smb_env;
|
|
|
|
static smb_env SMBEnv[MAX_SMB_MOUNTED];
|
|
|
|
static inline void _SMB_lock(int i)
|
|
{
|
|
if(SMBEnv[i]._SMB_mutex!=LWP_MUTEX_NULL) LWP_MutexLock(SMBEnv[i]._SMB_mutex);
|
|
}
|
|
|
|
static inline void _SMB_unlock(int i)
|
|
{
|
|
if(SMBEnv[i]._SMB_mutex!=LWP_MUTEX_NULL) LWP_MutexUnlock(SMBEnv[i]._SMB_mutex);
|
|
}
|
|
|
|
///////////////////////////////////////////
|
|
// CACHE FUNCTIONS //
|
|
///////////////////////////////////////////
|
|
|
|
static smb_env* FindSMBEnv(const char *name)
|
|
{
|
|
int i;
|
|
char *aux;
|
|
|
|
aux=strdup(name);
|
|
i=strlen(aux);
|
|
if(aux[i-1]==':')aux[i-1]='\0';
|
|
for(i=0;i<MAX_SMB_MOUNTED ;i++)
|
|
{
|
|
if(SMBEnv[i].SMBCONNECTED && strcmp(aux,SMBEnv[i].name)==0)
|
|
{
|
|
free(aux);
|
|
return &SMBEnv[i];
|
|
}
|
|
}
|
|
free(aux);
|
|
return NULL;
|
|
}
|
|
|
|
static int FlushWriteSMBCache(char *name)
|
|
{
|
|
smb_env *env;
|
|
env=FindSMBEnv(name);
|
|
if(env==NULL) return -1;
|
|
|
|
if (env->SMBWriteCache.file == NULL || env->SMBWriteCache.len == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int ret;
|
|
int written = 0;
|
|
|
|
while(env->SMBWriteCache.len > 0)
|
|
{
|
|
ret = SMB_WriteFile(env->SMBWriteCache.ptr+written, env->SMBWriteCache.len,
|
|
env->SMBWriteCache.file->offset, env->SMBWriteCache.file->handle);
|
|
|
|
if (ret <= 0)
|
|
return -1;
|
|
|
|
written += ret;
|
|
env->SMBWriteCache.file->offset += ret;
|
|
if (env->SMBWriteCache.file->offset > env->SMBWriteCache.file->len)
|
|
env->SMBWriteCache.file->len = env->SMBWriteCache.file->offset;
|
|
|
|
env->SMBWriteCache.len-=ret;
|
|
if(env->SMBWriteCache.len==0) break;
|
|
}
|
|
env->SMBWriteCache.used = 0;
|
|
env->SMBWriteCache.file = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void DestroySMBReadAheadCache(const char *name)
|
|
{
|
|
smb_env *env;
|
|
env=FindSMBEnv(name);
|
|
if(env==NULL) return ;
|
|
|
|
int i;
|
|
if (env->SMBReadAheadCache != NULL)
|
|
{
|
|
for (i = 0; i < env->SMB_RA_pages; i++)
|
|
{
|
|
if(env->SMBReadAheadCache[i].ptr)
|
|
free(env->SMBReadAheadCache[i].ptr);
|
|
}
|
|
free(env->SMBReadAheadCache);
|
|
env->SMBReadAheadCache = NULL;
|
|
env->SMB_RA_pages = 0;
|
|
}
|
|
FlushWriteSMBCache(env->name);
|
|
|
|
if(env->SMBWriteCache.ptr)
|
|
free(env->SMBWriteCache.ptr);
|
|
|
|
env->SMBWriteCache.used = 0;
|
|
env->SMBWriteCache.len = 0;
|
|
env->SMBWriteCache.file = NULL;
|
|
env->SMBWriteCache.ptr = NULL;
|
|
}
|
|
|
|
static void *process_cache_thread(void *ptr)
|
|
{
|
|
int i;
|
|
while (1)
|
|
{
|
|
for(i=0;i<MAX_SMB_MOUNTED ;i++)
|
|
{
|
|
if(SMBEnv[i].SMBCONNECTED)
|
|
{
|
|
if (SMBEnv[i].SMBWriteCache.used > 0)
|
|
{
|
|
if (ticks_to_millisecs(gettime())-ticks_to_millisecs(SMBEnv[i].SMBWriteCache.used) > 500)
|
|
{
|
|
_SMB_lock(i);
|
|
FlushWriteSMBCache(SMBEnv[i].name);
|
|
_SMB_unlock(i);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
usleep(10000);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
static void SMBEnableReadAhead(const char *name, u32 pages)
|
|
{
|
|
s32 i, j;
|
|
|
|
smb_env *env;
|
|
env=FindSMBEnv(name);
|
|
if(env==NULL) return;
|
|
|
|
DestroySMBReadAheadCache(name);
|
|
|
|
if (pages == 0)
|
|
return;
|
|
|
|
//only 1 page for write
|
|
env->SMBWriteCache.ptr = memalign(32, SMB_WRITE_BUFFERSIZE);
|
|
env->SMBWriteCache.used = 0;
|
|
env->SMBWriteCache.len = 0;
|
|
env->SMBWriteCache.file = NULL;
|
|
|
|
env->SMB_RA_pages = pages;
|
|
env->SMBReadAheadCache = (smb_cache_page *) malloc(sizeof(smb_cache_page) * env->SMB_RA_pages);
|
|
if (env->SMBReadAheadCache == NULL)
|
|
return;
|
|
for (i = 0; i < env->SMB_RA_pages; i++)
|
|
{
|
|
env->SMBReadAheadCache[i].offset = 0;
|
|
env->SMBReadAheadCache[i].last_used = 0;
|
|
env->SMBReadAheadCache[i].file = NULL;
|
|
env->SMBReadAheadCache[i].ptr = memalign(32, SMB_READ_BUFFERSIZE);
|
|
if (env->SMBReadAheadCache[i].ptr == NULL)
|
|
{
|
|
for (j = i - 1; j >= 0; j--)
|
|
if (env->SMBReadAheadCache[j].ptr)
|
|
free(env->SMBReadAheadCache[j].ptr);
|
|
free(env->SMBReadAheadCache);
|
|
env->SMBReadAheadCache = NULL;
|
|
free(env->SMBWriteCache.ptr);
|
|
return;
|
|
}
|
|
memset(env->SMBReadAheadCache[i].ptr, 0, SMB_READ_BUFFERSIZE);
|
|
}
|
|
}
|
|
|
|
// clear cache from file
|
|
// called when file is closed
|
|
static void ClearSMBFileCache(SMBFILESTRUCT *file)
|
|
{
|
|
int i,j;
|
|
j=file->env;
|
|
|
|
for (i = 0; i < SMBEnv[j].SMB_RA_pages; i++)
|
|
{
|
|
if (SMBEnv[j].SMBReadAheadCache[i].file == file)
|
|
{
|
|
SMBEnv[j].SMBReadAheadCache[i].offset = 0;
|
|
SMBEnv[j].SMBReadAheadCache[i].last_used = 0;
|
|
SMBEnv[j].SMBReadAheadCache[i].file = NULL;
|
|
memset(SMBEnv[j].SMBReadAheadCache[i].ptr, 0, SMB_READ_BUFFERSIZE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int ReadSMBFromCache(void *buf, size_t len, SMBFILESTRUCT *file)
|
|
{
|
|
int i,j, leastUsed;
|
|
off_t new_offset, rest;
|
|
j=file->env;
|
|
|
|
if ( len == 0 ) return 0;
|
|
|
|
if (SMBEnv[j].SMBReadAheadCache == NULL)
|
|
{
|
|
if (SMB_ReadFile(buf, len, file->offset, file->handle) <= 0)
|
|
{
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
new_offset = file->offset;
|
|
rest = len;
|
|
|
|
continue_read:
|
|
|
|
for (i = 0; i < SMBEnv[j].SMB_RA_pages; i++)
|
|
{
|
|
if (SMBEnv[j].SMBReadAheadCache[i].file == file)
|
|
{
|
|
if ((new_offset >= SMBEnv[j].SMBReadAheadCache[i].offset) &&
|
|
(new_offset < (SMBEnv[j].SMBReadAheadCache[i].offset + SMB_READ_BUFFERSIZE)))
|
|
{
|
|
//we hit the page
|
|
//copy as much as we can
|
|
SMBEnv[j].SMBReadAheadCache[i].last_used = gettime();
|
|
|
|
off_t buffer_used = (SMBEnv[j].SMBReadAheadCache[i].offset + SMB_READ_BUFFERSIZE) - new_offset;
|
|
if (buffer_used > rest) buffer_used = rest;
|
|
memcpy(buf, SMBEnv[j].SMBReadAheadCache[i].ptr + (new_offset - SMBEnv[j].SMBReadAheadCache[i].offset), buffer_used);
|
|
buf += buffer_used;
|
|
rest -= buffer_used;
|
|
new_offset += buffer_used;
|
|
|
|
if (rest == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
goto continue_read;
|
|
}
|
|
}
|
|
}
|
|
|
|
leastUsed = 0;
|
|
for ( i = 1; i < SMBEnv[j].SMB_RA_pages; i++)
|
|
{
|
|
if ((SMBEnv[j].SMBReadAheadCache[i].last_used < SMBEnv[j].SMBReadAheadCache[leastUsed].last_used))
|
|
leastUsed = i;
|
|
}
|
|
|
|
//fill least used page with new data
|
|
|
|
off_t cache_offset = new_offset;
|
|
|
|
//do not interset with existing pages
|
|
for (i = 0; i < SMBEnv[j].SMB_RA_pages; i++)
|
|
{
|
|
if ( i == leastUsed ) continue;
|
|
if ( SMBEnv[j].SMBReadAheadCache[i].file != file ) continue;
|
|
|
|
if ( (cache_offset < SMBEnv[j].SMBReadAheadCache[i].offset ) && (cache_offset + SMB_READ_BUFFERSIZE > SMBEnv[j].SMBReadAheadCache[i].offset) )
|
|
{
|
|
//tail of new page intersects with some cache block, clear page
|
|
SMBEnv[j].SMBReadAheadCache[i].file = NULL;
|
|
}
|
|
|
|
if ( (cache_offset >= SMBEnv[j].SMBReadAheadCache[i].offset ) && (cache_offset < SMBEnv[j].SMBReadAheadCache[i].offset + SMB_READ_BUFFERSIZE ) )
|
|
{
|
|
//head of new page intersects with some cache block, clear page
|
|
SMBEnv[j].SMBReadAheadCache[i].file = NULL;
|
|
}
|
|
}
|
|
|
|
off_t cache_to_read = file->len - cache_offset;
|
|
if ( cache_to_read > SMB_READ_BUFFERSIZE )
|
|
{
|
|
cache_to_read = SMB_READ_BUFFERSIZE;
|
|
}
|
|
|
|
int read=0, readed;
|
|
while(read<cache_to_read)
|
|
{
|
|
readed = SMB_ReadFile(SMBEnv[j].SMBReadAheadCache[leastUsed].ptr+read, cache_to_read-read, cache_offset+read, file->handle);
|
|
if ( readed <=0 )
|
|
{
|
|
SMBEnv[j].SMBReadAheadCache[leastUsed].file = NULL;
|
|
return -1;
|
|
}
|
|
read += readed;
|
|
}
|
|
SMBEnv[j].SMBReadAheadCache[leastUsed].last_used = gettime();
|
|
|
|
SMBEnv[j].SMBReadAheadCache[leastUsed].offset = cache_offset;
|
|
SMBEnv[j].SMBReadAheadCache[leastUsed].file = file;
|
|
|
|
goto continue_read;
|
|
}
|
|
|
|
static int WriteSMBUsingCache(const char *buf, size_t len, SMBFILESTRUCT *file)
|
|
{
|
|
size_t size=len;
|
|
if (file == NULL || buf == NULL)
|
|
return -1;
|
|
int j;
|
|
j=file->env;
|
|
if (SMBEnv[j].SMBWriteCache.file != NULL)
|
|
{
|
|
if (strcmp(SMBEnv[j].SMBWriteCache.file->filename, file->filename) != 0)
|
|
{
|
|
//Flush current buffer
|
|
if (FlushWriteSMBCache(SMBEnv[j].name) < 0)
|
|
{
|
|
goto failed;
|
|
}
|
|
SMBEnv[j].SMBWriteCache.file = file;
|
|
SMBEnv[j].SMBWriteCache.len = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SMBEnv[j].SMBWriteCache.file = file;
|
|
SMBEnv[j].SMBWriteCache.len = 0;
|
|
}
|
|
int rest;
|
|
s32 written;
|
|
while(len > 0)
|
|
{
|
|
if(SMBEnv[j].SMBWriteCache.len+len >= SMB_WRITE_BUFFERSIZE)
|
|
{
|
|
rest = SMB_WRITE_BUFFERSIZE - SMBEnv[j].SMBWriteCache.len;
|
|
memcpy(SMBEnv[j].SMBWriteCache.ptr + SMBEnv[j].SMBWriteCache.len, buf, rest);
|
|
|
|
written = SMB_WriteFile(SMBEnv[j].SMBWriteCache.ptr, SMB_WRITE_BUFFERSIZE, file->offset, file->handle);
|
|
if(written<0)
|
|
{
|
|
goto failed;
|
|
}
|
|
file->offset += written;
|
|
if (file->offset > file->len)
|
|
file->len = file->offset;
|
|
|
|
buf += rest;
|
|
len -= rest;
|
|
SMBEnv[j].SMBWriteCache.used = gettime();
|
|
SMBEnv[j].SMBWriteCache.len = 0;
|
|
}
|
|
else
|
|
{
|
|
memcpy(SMBEnv[j].SMBWriteCache.ptr + SMBEnv[j].SMBWriteCache.len, buf, len);
|
|
SMBEnv[j].SMBWriteCache.len += len;
|
|
SMBEnv[j].SMBWriteCache.used = gettime();
|
|
break;
|
|
}
|
|
}
|
|
return size;
|
|
|
|
failed:
|
|
return -1;
|
|
}
|
|
|
|
///////////////////////////////////////////
|
|
// END CACHE FUNCTIONS //
|
|
///////////////////////////////////////////
|
|
|
|
static char *smb_absolute_path_no_device(const char *srcpath, char *destpath, int env)
|
|
{
|
|
char temp[SMB_MAXPATH];
|
|
int i=0,j=0;
|
|
|
|
if (strchr(srcpath, ':') != NULL)
|
|
{
|
|
srcpath = strchr(srcpath, ':') + 1;
|
|
}
|
|
if (strchr(srcpath, ':') != NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
memset(temp,0,SMB_MAXPATH);
|
|
|
|
if (srcpath[0] != '\\' && srcpath[0] != '/')
|
|
{
|
|
strcpy(temp, SMBEnv[env].currentpath);
|
|
}
|
|
if(srcpath[0]!='\0') //strlen(srcpath) > 0
|
|
{
|
|
if(srcpath[0]=='.' && (srcpath[1]=='\\' || srcpath[1]=='\0')) // to fix opendir(".") or chdir(".")
|
|
strcat(temp, &srcpath[1]);
|
|
else
|
|
strcat(temp, srcpath);
|
|
}
|
|
|
|
while(temp[i]!='\0' && i < SMB_MAXPATH)
|
|
{
|
|
if(temp[i]=='/')
|
|
{
|
|
destpath[j++]='\\';
|
|
while(temp[i]!='\0' && i < SMB_MAXPATH && (temp[i]=='/' || temp[i]=='\\'))i++;
|
|
}
|
|
else if(srcpath[i]=='\\')
|
|
{
|
|
destpath[j++]=temp[i++];
|
|
while(temp[i]!='\0' && i < SMB_MAXPATH && (temp[i]=='/' || temp[i]=='\\'))i++;
|
|
}
|
|
else
|
|
{
|
|
destpath[j++]=temp[i++];
|
|
}
|
|
}
|
|
destpath[j]='\0';
|
|
|
|
return destpath;
|
|
}
|
|
|
|
static char *ExtractDevice(const char *path, char *device)
|
|
{
|
|
int i,l;
|
|
l=strlen(path);
|
|
|
|
for(i=0;i<l && path[i]!='\0' && path[i]!=':' && i < 20;i++)
|
|
device[i]=path[i];
|
|
if(path[i]!=':')device[0]='\0';
|
|
else device[i]='\0';
|
|
return device;
|
|
}
|
|
|
|
//FILE IO
|
|
static int __smb_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
|
|
{
|
|
SMBFILESTRUCT *file = (SMBFILESTRUCT*) fileStruct;
|
|
char fixedpath[SMB_MAXPATH];
|
|
smb_env *env;
|
|
|
|
ExtractDevice(path,fixedpath);
|
|
if(fixedpath[0]=='\0')
|
|
{
|
|
getcwd(fixedpath,SMB_MAXPATH);
|
|
ExtractDevice(fixedpath,fixedpath);
|
|
}
|
|
env=FindSMBEnv(fixedpath);
|
|
if(env==NULL)
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
file->env=env->pos;
|
|
|
|
if (!env->SMBCONNECTED)
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
if (smb_absolute_path_no_device(path, fixedpath, file->env) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if(!smbCheckConnection(env->name))
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
SMBDIRENTRY dentry;
|
|
bool fileExists = true;
|
|
_SMB_lock(env->pos);
|
|
if (SMB_PathInfo(fixedpath, &dentry, env->smbconn) != SMB_SUCCESS)
|
|
fileExists = false;
|
|
|
|
|
|
// Determine which mode the file is open for
|
|
u8 smb_mode;
|
|
unsigned short access;
|
|
if ((flags & 0x03) == O_RDONLY)
|
|
{
|
|
// Open the file for read-only access
|
|
smb_mode = SMB_OF_OPEN;
|
|
access = SMB_OPEN_READING;
|
|
}
|
|
else if ((flags & 0x03) == O_WRONLY)
|
|
{
|
|
// Open file for write only access
|
|
if (fileExists)
|
|
smb_mode = SMB_OF_OPEN;
|
|
else
|
|
smb_mode = SMB_OF_CREATE;
|
|
access = SMB_OPEN_WRITING;
|
|
}
|
|
else if ((flags & 0x03) == O_RDWR)
|
|
{
|
|
// Open file for read/write access
|
|
access = SMB_OPEN_READWRITE;
|
|
if (fileExists)
|
|
smb_mode = SMB_OF_OPEN;
|
|
else
|
|
smb_mode = SMB_OF_CREATE;
|
|
}
|
|
else
|
|
{
|
|
r->_errno = EACCES;
|
|
_SMB_unlock(env->pos);
|
|
return -1;
|
|
}
|
|
|
|
if ((flags & O_CREAT) && !fileExists)
|
|
smb_mode = SMB_OF_CREATE;
|
|
if (!(flags & O_APPEND) && fileExists && ((flags & 0x03) != O_RDONLY))
|
|
smb_mode = SMB_OF_TRUNCATE;
|
|
file->handle = SMB_OpenFile(fixedpath, access, smb_mode, env->smbconn);
|
|
if (!file->handle)
|
|
{
|
|
r->_errno = ENOENT;
|
|
_SMB_unlock(env->pos);
|
|
return -1;
|
|
}
|
|
|
|
file->len = 0;
|
|
file->attributes = 0;
|
|
if (fileExists)
|
|
{
|
|
file->len = dentry.size;
|
|
file->attributes = dentry.attributes;
|
|
}
|
|
|
|
if (flags & O_APPEND)
|
|
file->offset = file->len;
|
|
else
|
|
file->offset = 0;
|
|
|
|
file->access=access;
|
|
|
|
strcpy(file->filename, fixedpath);
|
|
_SMB_unlock(env->pos);
|
|
return 0;
|
|
}
|
|
|
|
static off_t __smb_seek(struct _reent *r, int fd, off_t pos, int dir)
|
|
{
|
|
SMBFILESTRUCT *file = (SMBFILESTRUCT*) fd;
|
|
off_t position;
|
|
|
|
if (file == NULL)
|
|
{
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
//have to flush because SMBWriteCache.file->offset holds offset of cached block not yet written
|
|
_SMB_lock(file->env);
|
|
if (SMBEnv[file->env].SMBWriteCache.file == file)
|
|
{
|
|
FlushWriteSMBCache(SMBEnv[file->env].name);
|
|
}
|
|
|
|
switch (dir)
|
|
{
|
|
case SEEK_SET:
|
|
position = pos;
|
|
break;
|
|
case SEEK_CUR:
|
|
position = file->offset + pos;
|
|
break;
|
|
case SEEK_END:
|
|
position = file->len + pos;
|
|
break;
|
|
default:
|
|
r->_errno = EINVAL;
|
|
_SMB_unlock(file->env);
|
|
return -1;
|
|
}
|
|
|
|
if (pos > 0 && position < 0)
|
|
{
|
|
r->_errno = EOVERFLOW;
|
|
_SMB_unlock(file->env);
|
|
return -1;
|
|
}
|
|
if (position < 0)
|
|
{
|
|
r->_errno = EINVAL;
|
|
_SMB_unlock(file->env);
|
|
return -1;
|
|
}
|
|
|
|
// Save position
|
|
file->offset = position;
|
|
_SMB_unlock(file->env);
|
|
return position;
|
|
}
|
|
|
|
static ssize_t __smb_read(struct _reent *r, int fd, char *ptr, size_t len)
|
|
{
|
|
size_t offset = 0;
|
|
size_t readsize;
|
|
int ret = 0;
|
|
int cnt_retry=2;
|
|
SMBFILESTRUCT *file = (SMBFILESTRUCT*) fd;
|
|
|
|
if (file == NULL)
|
|
{
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
if (len <= 0)
|
|
{
|
|
return 0;
|
|
}
|
|
//have to flush because SMBWriteCache.file->offset holds offset of cached block not yet writeln
|
|
//and file->len also may not have been updated yet
|
|
_SMB_lock(file->env);
|
|
if (SMBEnv[file->env].SMBWriteCache.file == file)
|
|
{
|
|
FlushWriteSMBCache(SMBEnv[file->env].name);
|
|
}
|
|
|
|
// Don't try to read if the read pointer is past the end of file
|
|
if (file->offset >= file->len)
|
|
{
|
|
r->_errno = EOVERFLOW;
|
|
_SMB_unlock(file->env);
|
|
return 0;
|
|
}
|
|
|
|
// Don't read past end of file
|
|
if (len + file->offset > file->len)
|
|
{
|
|
r->_errno = EOVERFLOW;
|
|
len = file->len - file->offset;
|
|
}
|
|
|
|
// Short circuit cases where len is 0 (or less)
|
|
if (len <= 0)
|
|
{
|
|
_SMB_unlock(file->env);
|
|
return 0;
|
|
}
|
|
retry_read:
|
|
while(offset < len)
|
|
{
|
|
readsize = len - offset;
|
|
if(readsize > SMB_READ_BUFFERSIZE) readsize = SMB_READ_BUFFERSIZE;
|
|
ret = ReadSMBFromCache(ptr+offset, readsize, file);
|
|
if(ret < 0) break;
|
|
offset += readsize;
|
|
file->offset += readsize;
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
retry_reconnect:
|
|
cnt_retry--;
|
|
if(cnt_retry>=0)
|
|
{
|
|
if(smbCheckConnection(SMBEnv[file->env].name))
|
|
{
|
|
ClearSMBFileCache(file);
|
|
file->handle = SMB_OpenFile(file->filename, file->access, SMB_OF_OPEN, SMBEnv[file->env].smbconn);
|
|
if (!file->handle)
|
|
{
|
|
r->_errno = ENOENT;
|
|
_SMB_unlock(file->env);
|
|
return -1;
|
|
}
|
|
goto retry_read;
|
|
}
|
|
usleep(10000);
|
|
goto retry_reconnect;
|
|
}
|
|
r->_errno = EIO;
|
|
_SMB_unlock(file->env);
|
|
return -1;
|
|
}
|
|
_SMB_unlock(file->env);
|
|
return len;
|
|
}
|
|
|
|
static ssize_t __smb_write(struct _reent *r, int fd, const char *ptr, size_t len)
|
|
{
|
|
SMBFILESTRUCT *file = (SMBFILESTRUCT*) fd;
|
|
int written;
|
|
if (file == NULL)
|
|
{
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
// Don't try to write if the pointer is past the end of file
|
|
if (file->offset > file->len)
|
|
{
|
|
r->_errno = EOVERFLOW;
|
|
return -1;
|
|
}
|
|
|
|
// Short circuit cases where len is 0 (or less)
|
|
if (len == 0)
|
|
return 0;
|
|
|
|
_SMB_lock(file->env);
|
|
written = WriteSMBUsingCache(ptr, len, file);
|
|
_SMB_unlock(file->env);
|
|
|
|
if (written <= 0)
|
|
{
|
|
r->_errno = EIO;
|
|
return -1;
|
|
}
|
|
|
|
return written;
|
|
}
|
|
|
|
static int __smb_close(struct _reent *r, int fd)
|
|
{
|
|
SMBFILESTRUCT *file = (SMBFILESTRUCT*) fd;
|
|
int j;
|
|
j=file->env;
|
|
_SMB_lock(j);
|
|
if (SMBEnv[j].SMBWriteCache.file == file)
|
|
{
|
|
FlushWriteSMBCache(SMBEnv[j].name);
|
|
}
|
|
ClearSMBFileCache(file);
|
|
SMB_CloseFile(file->handle);
|
|
file->len = 0;
|
|
file->offset = 0;
|
|
file->filename[0] = '\0';
|
|
_SMB_unlock(j);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __smb_chdir(struct _reent *r, const char *path)
|
|
{
|
|
char path_absolute[SMB_MAXPATH];
|
|
|
|
SMBDIRENTRY dentry;
|
|
int found;
|
|
|
|
ExtractDevice(path,path_absolute);
|
|
if(path_absolute[0]=='\0')
|
|
{
|
|
getcwd(path_absolute,SMB_MAXPATH);
|
|
ExtractDevice(path_absolute,path_absolute);
|
|
}
|
|
|
|
smb_env* env;
|
|
env=FindSMBEnv(path_absolute);
|
|
|
|
if(env == NULL)
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
if (!env->SMBCONNECTED)
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
if (smb_absolute_path_no_device(path, path_absolute,env->pos) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if(!smbCheckConnection(env->name))
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
memset(&dentry, 0, sizeof(SMBDIRENTRY));
|
|
|
|
_SMB_lock(env->pos);
|
|
found = SMB_PathInfo(path_absolute, &dentry, env->smbconn);
|
|
|
|
if (found != SMB_SUCCESS)
|
|
{
|
|
r->_errno = ENOENT;
|
|
_SMB_unlock(env->pos);
|
|
return -1;
|
|
}
|
|
|
|
if (!(dentry.attributes & SMB_SRCH_DIRECTORY))
|
|
{
|
|
r->_errno = ENOTDIR;
|
|
_SMB_unlock(env->pos);
|
|
return -1;
|
|
}
|
|
|
|
strcpy(env->currentpath, path_absolute);
|
|
if (env->currentpath[0] != 0)
|
|
{
|
|
if (env->currentpath[strlen(env->currentpath) - 1] != '\\')
|
|
strcat(env->currentpath, "\\");
|
|
}
|
|
_SMB_unlock(env->pos);
|
|
return 0;
|
|
}
|
|
|
|
static int __smb_dirreset(struct _reent *r, DIR_ITER *dirState)
|
|
{
|
|
char path_abs[SMB_MAXPATH];
|
|
SMBDIRSTATESTRUCT* state = (SMBDIRSTATESTRUCT*) (dirState->dirStruct);
|
|
SMBDIRENTRY dentry;
|
|
|
|
memset(&dentry, 0, sizeof(SMBDIRENTRY));
|
|
|
|
_SMB_lock(state->env);
|
|
SMB_FindClose(&state->smbdir, SMBEnv[state->env].smbconn);
|
|
|
|
strcpy(path_abs,SMBEnv[state->env].currentpath);
|
|
strcat(path_abs,"*");
|
|
int found = SMB_FindFirst(path_abs, smbFlags, &dentry, SMBEnv[state->env].smbconn);
|
|
|
|
if (found != SMB_SUCCESS)
|
|
{
|
|
r->_errno = ENOENT;
|
|
_SMB_unlock(state->env);
|
|
return -1;
|
|
}
|
|
|
|
if (!(dentry.attributes & SMB_SRCH_DIRECTORY))
|
|
{
|
|
r->_errno = ENOTDIR;
|
|
_SMB_unlock(state->env);
|
|
return -1;
|
|
}
|
|
|
|
state->smbdir.size = dentry.size;
|
|
state->smbdir.ctime = dentry.ctime;
|
|
state->smbdir.atime = dentry.atime;
|
|
state->smbdir.mtime = dentry.mtime;
|
|
state->smbdir.attributes = dentry.attributes;
|
|
state->smbdir.sid = dentry.sid;
|
|
strcpy(state->smbdir.name, dentry.name);
|
|
|
|
SMBEnv[state->env].first_item_dir = true;
|
|
_SMB_unlock(state->env);
|
|
return 0;
|
|
}
|
|
|
|
static DIR_ITER* __smb_diropen(struct _reent *r, DIR_ITER *dirState, const char *path)
|
|
{
|
|
char path_absolute[SMB_MAXPATH];
|
|
int found;
|
|
SMBDIRSTATESTRUCT* state = (SMBDIRSTATESTRUCT*) (dirState->dirStruct);
|
|
SMBDIRENTRY dentry;
|
|
|
|
ExtractDevice(path,path_absolute);
|
|
if(path_absolute[0]=='\0')
|
|
{
|
|
getcwd(path_absolute,SMB_MAXPATH);
|
|
ExtractDevice(path_absolute,path_absolute);
|
|
}
|
|
|
|
smb_env* env;
|
|
env=FindSMBEnv(path_absolute);
|
|
|
|
if(env == NULL)
|
|
{
|
|
r->_errno = ENODEV;
|
|
return NULL;
|
|
}
|
|
|
|
if (smb_absolute_path_no_device(path, path_absolute, env->pos) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return NULL;
|
|
}
|
|
if (strlen(path_absolute) > 0 && path_absolute[strlen(path_absolute) - 1] != '\\')
|
|
strcat(path_absolute, "\\");
|
|
|
|
if(!strcmp(path_absolute,"\\"))
|
|
env->diropen_root=true;
|
|
else
|
|
env->diropen_root=false;
|
|
|
|
if(!smbCheckConnection(env->name))
|
|
{
|
|
r->_errno = ENODEV;
|
|
return NULL;
|
|
}
|
|
|
|
_SMB_lock(env->pos);
|
|
if(!env->diropen_root) // root must be valid - we don't need check it
|
|
{
|
|
memset(&dentry, 0, sizeof(SMBDIRENTRY));
|
|
found = SMB_PathInfo(path_absolute, &dentry, env->smbconn);
|
|
if (found != SMB_SUCCESS)
|
|
{
|
|
r->_errno = ENOENT;
|
|
_SMB_unlock(env->pos);
|
|
return NULL;
|
|
}
|
|
|
|
if (!(dentry.attributes & SMB_SRCH_DIRECTORY))
|
|
{
|
|
r->_errno = ENOTDIR;
|
|
_SMB_unlock(env->pos);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
strcpy(state->dir,path_absolute);
|
|
|
|
strcat(path_absolute, "*");
|
|
memset(&dentry, 0, sizeof(SMBDIRENTRY));
|
|
found = SMB_FindFirst(path_absolute, smbFlags, &dentry, env->smbconn);
|
|
|
|
if (found != SMB_SUCCESS)
|
|
{
|
|
r->_errno = ENOENT;
|
|
_SMB_unlock(env->pos);
|
|
return NULL;
|
|
}
|
|
|
|
state->env=env->pos;
|
|
state->smbdir.size = dentry.size;
|
|
state->smbdir.ctime = dentry.ctime;
|
|
state->smbdir.atime = dentry.atime;
|
|
state->smbdir.mtime = dentry.mtime;
|
|
state->smbdir.attributes = dentry.attributes;
|
|
state->smbdir.sid = dentry.sid;
|
|
strcpy(state->smbdir.name, dentry.name);
|
|
env->first_item_dir = true;
|
|
_SMB_unlock(env->pos);
|
|
return dirState;
|
|
}
|
|
|
|
static int dentry_to_stat(SMBDIRENTRY *dentry, struct stat *st)
|
|
{
|
|
if (!st)
|
|
return -1;
|
|
if (!dentry)
|
|
return -1;
|
|
|
|
st->st_dev = 0;
|
|
st->st_ino = 0;
|
|
|
|
st->st_mode = ((dentry->attributes & SMB_SRCH_DIRECTORY) ? S_IFDIR : S_IFREG);
|
|
st->st_nlink = 1;
|
|
st->st_uid = 1; // Faked
|
|
st->st_rdev = st->st_dev;
|
|
st->st_gid = 2; // Faked
|
|
st->st_size = dentry->size;
|
|
st->st_atime = dentry->atime/10000000.0 - 11644473600LL;
|
|
st->st_spare1 = 0;
|
|
st->st_mtime = dentry->mtime/10000000.0 - 11644473600LL;
|
|
st->st_spare2 = 0;
|
|
st->st_ctime = dentry->ctime/10000000.0 - 11644473600LL;
|
|
st->st_spare3 = 0;
|
|
st->st_blksize = 1024;
|
|
st->st_blocks = (st->st_size + st->st_blksize - 1) / st->st_blksize; // File size in blocks
|
|
st->st_spare4[0] = 0;
|
|
st->st_spare4[1] = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cpy_dentry(SMBDIRENTRY *dest, SMBDIRENTRY *source)
|
|
{
|
|
if (!dest || !source)
|
|
return -1;
|
|
|
|
dest->attributes=source->attributes;
|
|
dest->size=source->size;
|
|
dest->atime=source->atime;
|
|
dest->mtime=source->mtime;
|
|
dest->ctime=source->ctime;
|
|
strcpy(dest->name,source->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __smb_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename,
|
|
struct stat *filestat)
|
|
{
|
|
int ret;
|
|
SMBDIRSTATESTRUCT* state = (SMBDIRSTATESTRUCT*) (dirState->dirStruct);
|
|
SMBDIRENTRY dentry;
|
|
|
|
if (SMBEnv[state->env].currentpath[0] == '\0' || filestat == NULL)
|
|
{
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
memset(&dentry, 0, sizeof(SMBDIRENTRY));
|
|
_SMB_lock(state->env);
|
|
if (SMBEnv[state->env].first_item_dir)
|
|
{
|
|
SMBEnv[state->env].first_item_dir = false;
|
|
dentry.size = state->smbdir.size;
|
|
dentry.ctime = state->smbdir.ctime;
|
|
dentry.atime = state->smbdir.atime;
|
|
dentry.mtime = state->smbdir.mtime;
|
|
dentry.attributes = state->smbdir.attributes;
|
|
strcpy(dentry.name, state->smbdir.name);
|
|
strcpy(filename, dentry.name);
|
|
dentry_to_stat(&dentry, filestat);
|
|
cpy_dentry(&last_dentry,&dentry);
|
|
last_env=state->env;
|
|
strcpy(last_path,state->dir);
|
|
_SMB_unlock(state->env);
|
|
return 0;
|
|
}
|
|
|
|
dentry.sid = state->smbdir.sid;
|
|
|
|
ret = SMB_FindNext(&dentry, SMBEnv[state->env].smbconn);
|
|
if(ret==SMB_SUCCESS && SMBEnv[state->env].diropen_root)
|
|
{
|
|
if(strlen(dentry.name) == 2 && strcmp(dentry.name,"..") == 0)
|
|
ret = SMB_FindNext(&dentry, SMBEnv[state->env].smbconn);
|
|
}
|
|
if (ret == SMB_SUCCESS)
|
|
{
|
|
state->smbdir.size = dentry.size;
|
|
state->smbdir.ctime = dentry.ctime;
|
|
state->smbdir.atime = dentry.atime;
|
|
state->smbdir.mtime = dentry.mtime;
|
|
state->smbdir.attributes = dentry.attributes;
|
|
strcpy(state->smbdir.name, dentry.name);
|
|
strcpy(filename, dentry.name);
|
|
}
|
|
else
|
|
{
|
|
r->_errno = ENOENT;
|
|
_SMB_unlock(state->env);
|
|
return -1;
|
|
}
|
|
|
|
dentry_to_stat(&dentry, filestat);
|
|
cpy_dentry(&last_dentry,&dentry);
|
|
last_env=state->env;
|
|
strcpy(last_path,state->dir);
|
|
_SMB_unlock(state->env);
|
|
return 0;
|
|
}
|
|
|
|
static int __smb_dirclose(struct _reent *r, DIR_ITER *dirState)
|
|
{
|
|
SMBDIRSTATESTRUCT* state = (SMBDIRSTATESTRUCT*) (dirState->dirStruct);
|
|
|
|
int j = state->env;
|
|
_SMB_lock(j);
|
|
SMB_FindClose(&state->smbdir, SMBEnv[j].smbconn);
|
|
memset(state, 0, sizeof(SMBDIRSTATESTRUCT));
|
|
_SMB_unlock(j);
|
|
return 0;
|
|
}
|
|
|
|
static int __smb_stat(struct _reent *r, const char *path, struct stat *st)
|
|
{
|
|
char path_absolute[SMB_MAXPATH];
|
|
SMBDIRENTRY dentry;
|
|
|
|
if(path == NULL)
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
if(strcmp(path,".")==0 || strcmp(path,"..")==0)
|
|
{
|
|
memset(st,0,sizeof(struct stat));
|
|
st->st_mode = S_IFDIR;
|
|
return 0;
|
|
}
|
|
|
|
ExtractDevice(path,path_absolute);
|
|
if(path_absolute[0]=='\0')
|
|
{
|
|
getcwd(path_absolute,SMB_MAXPATH);
|
|
ExtractDevice(path_absolute,path_absolute);
|
|
}
|
|
|
|
smb_env* env;
|
|
env=FindSMBEnv(path_absolute);
|
|
|
|
if(env == NULL)
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
if (smb_absolute_path_no_device(path, path_absolute, env->pos) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if(env->pos==last_env) //optimization, usually after a dirnext we do stat
|
|
{
|
|
char file[SMB_MAXPATH];
|
|
strcpy(file,last_path);
|
|
strcat(file,last_dentry.name);
|
|
if(strcmp(file,path_absolute)==0)
|
|
{
|
|
dentry_to_stat(&last_dentry, st);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
_SMB_lock(env->pos);
|
|
if (SMB_PathInfo(path_absolute, &dentry, env->smbconn) != SMB_SUCCESS)
|
|
{
|
|
r->_errno = ENOENT;
|
|
_SMB_unlock(env->pos);
|
|
return -1;
|
|
}
|
|
|
|
if (dentry.name[0] == '\0')
|
|
{
|
|
|
|
r->_errno = ENOENT;
|
|
_SMB_unlock(env->pos);
|
|
return -1;
|
|
}
|
|
|
|
dentry_to_stat(&dentry, st);
|
|
_SMB_unlock(env->pos);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __smb_fstat(struct _reent *r, int fd, struct stat *st)
|
|
{
|
|
SMBFILESTRUCT *filestate = (SMBFILESTRUCT *) fd;
|
|
|
|
if (!filestate)
|
|
{
|
|
r->_errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
st->st_size = filestate->len;
|
|
st->st_mode = ((filestate->attributes & SMB_SRCH_DIRECTORY) ? S_IFDIR : S_IFREG);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __smb_mkdir(struct _reent *r, const char *name, int mode)
|
|
{
|
|
char fixedName[SMB_MAXPATH];
|
|
smb_env *env;
|
|
|
|
ExtractDevice(name,fixedName);
|
|
if(fixedName[0]=='\0')
|
|
{
|
|
getcwd(fixedName,SMB_MAXPATH);
|
|
ExtractDevice(fixedName,fixedName);
|
|
}
|
|
|
|
env=FindSMBEnv(fixedName);
|
|
if(env==NULL)
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
if (smb_absolute_path_no_device(name, fixedName, env->pos) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
s32 ret = 0;
|
|
_SMB_lock(env->pos);
|
|
if(SMB_CreateDirectory(fixedName, env->smbconn) != SMB_SUCCESS)
|
|
ret = -1;
|
|
_SMB_unlock(env->pos);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int __smb_unlink(struct _reent *r, const char *name)
|
|
{
|
|
char fixedName[SMB_MAXPATH];
|
|
smb_env *env;
|
|
bool isDir = false;
|
|
|
|
DIR *dir = NULL;
|
|
dir = opendir(name);
|
|
if(dir)
|
|
{
|
|
closedir(dir);
|
|
isDir = true;
|
|
}
|
|
|
|
ExtractDevice(name,fixedName);
|
|
if(fixedName[0]=='\0')
|
|
{
|
|
getcwd(fixedName,SMB_MAXPATH);
|
|
ExtractDevice(fixedName,fixedName);
|
|
}
|
|
|
|
env=FindSMBEnv(fixedName);
|
|
if(env==NULL)
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
if (smb_absolute_path_no_device(name, fixedName, env->pos) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
s32 ret = 0;
|
|
_SMB_lock(env->pos);
|
|
if(isDir)
|
|
ret = SMB_DeleteDirectory(fixedName, env->smbconn);
|
|
else
|
|
ret = SMB_DeleteFile(fixedName, env->smbconn);
|
|
_SMB_unlock(env->pos);
|
|
|
|
if(ret != SMB_SUCCESS)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int __smb_rename(struct _reent *r, const char *oldName, const char *newName)
|
|
{
|
|
char fixedOldName[SMB_MAXPATH];
|
|
char fixedNewName[SMB_MAXPATH];
|
|
smb_env *env;
|
|
|
|
ExtractDevice(oldName,fixedOldName);
|
|
if(fixedOldName[0]=='\0')
|
|
{
|
|
getcwd(fixedOldName,SMB_MAXPATH);
|
|
ExtractDevice(fixedOldName,fixedOldName);
|
|
}
|
|
|
|
env=FindSMBEnv(fixedOldName);
|
|
if(env==NULL)
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
if (smb_absolute_path_no_device(oldName, fixedOldName, env->pos) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
ExtractDevice(newName,fixedNewName);
|
|
if(fixedNewName[0]=='\0')
|
|
{
|
|
getcwd(fixedNewName,SMB_MAXPATH);
|
|
ExtractDevice(fixedNewName,fixedNewName);
|
|
}
|
|
|
|
if (smb_absolute_path_no_device(newName, fixedNewName, env->pos) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
s32 ret = 0;
|
|
_SMB_lock(env->pos);
|
|
if (SMB_Rename(fixedOldName, fixedNewName, env->smbconn) != SMB_SUCCESS)
|
|
ret = -1;
|
|
_SMB_unlock(env->pos);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int __smb_statvfs_r(struct _reent *r, const char *name, struct statvfs *buf)
|
|
{
|
|
char fixedName[SMB_MAXPATH];
|
|
smb_env *env;
|
|
|
|
ExtractDevice(name,fixedName);
|
|
if(fixedName[0]=='\0')
|
|
{
|
|
getcwd(fixedName,SMB_MAXPATH);
|
|
ExtractDevice(fixedName,fixedName);
|
|
}
|
|
|
|
env=FindSMBEnv(fixedName);
|
|
if(env==NULL)
|
|
{
|
|
r->_errno = ENODEV;
|
|
return -1;
|
|
}
|
|
|
|
if (smb_absolute_path_no_device(name, fixedName, env->pos) == NULL)
|
|
{
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
s32 ret = 0;
|
|
_SMB_lock(env->pos);
|
|
if(SMB_DiskInformation(buf, env->smbconn) != SMB_SUCCESS)
|
|
ret = -1;
|
|
_SMB_unlock(env->pos);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void MountDevice(const char *name,SMBCONN smbconn, int env)
|
|
{
|
|
devoptab_t *dotab_smb;
|
|
char *aux;
|
|
int l;
|
|
|
|
aux=strdup(name);
|
|
l=strlen(aux);
|
|
if(aux[l-1]==':')aux[l-1]='\0';
|
|
|
|
dotab_smb=(devoptab_t*)malloc(sizeof(devoptab_t));
|
|
|
|
dotab_smb->name=strdup(aux);
|
|
dotab_smb->structSize=sizeof(SMBFILESTRUCT); // size of file structure
|
|
dotab_smb->open_r=__smb_open; // device open
|
|
dotab_smb->close_r=__smb_close; // device close
|
|
dotab_smb->write_r=__smb_write; // device write
|
|
dotab_smb->read_r=__smb_read; // device read
|
|
dotab_smb->seek_r=__smb_seek; // device seek
|
|
dotab_smb->fstat_r=__smb_fstat; // device fstat
|
|
dotab_smb->stat_r=__smb_stat; // device stat
|
|
dotab_smb->link_r=NULL; // device link
|
|
dotab_smb->unlink_r=__smb_unlink; // device unlink
|
|
dotab_smb->chdir_r=__smb_chdir; // device chdir
|
|
dotab_smb->rename_r=__smb_rename; // device rename
|
|
dotab_smb->mkdir_r=__smb_mkdir; // device mkdir
|
|
|
|
dotab_smb->dirStateSize=sizeof(SMBDIRSTATESTRUCT); // dirStateSize
|
|
dotab_smb->diropen_r=__smb_diropen; // device diropen_r
|
|
dotab_smb->dirreset_r=__smb_dirreset; // device dirreset_r
|
|
dotab_smb->dirnext_r=__smb_dirnext; // device dirnext_r
|
|
dotab_smb->dirclose_r=__smb_dirclose; // device dirclose_r
|
|
dotab_smb->statvfs_r=__smb_statvfs_r; // device statvfs_r
|
|
dotab_smb->ftruncate_r=NULL; // device ftruncate_r
|
|
dotab_smb->fsync_r=NULL; // device fsync_r
|
|
dotab_smb->deviceData=NULL; /* Device data */
|
|
|
|
AddDevice(dotab_smb);
|
|
|
|
SMBEnv[env].pos=env;
|
|
SMBEnv[env].smbconn=smbconn;
|
|
SMBEnv[env].first_item_dir=false;
|
|
SMBEnv[env].diropen_root=false;
|
|
SMBEnv[env].devoptab=dotab_smb;
|
|
SMBEnv[env].SMBCONNECTED=true;
|
|
SMBEnv[env].name=strdup(aux);
|
|
|
|
SMBEnableReadAhead(aux,8);
|
|
|
|
free(aux);
|
|
}
|
|
|
|
bool smbInitDevice(const char* name, const char *user, const char *password, const char *share, const char *ip)
|
|
{
|
|
int i;
|
|
|
|
if(!name || strlen(name) > 9)
|
|
return false;
|
|
|
|
char devname[10];
|
|
sprintf(devname, "%s:", name);
|
|
if(FindDevice(devname) >= 0)
|
|
return false;
|
|
|
|
if(!smbInited)
|
|
{
|
|
for(i=0;i<MAX_SMB_MOUNTED;i++)
|
|
{
|
|
SMBEnv[i].SMBCONNECTED=false;
|
|
SMBEnv[i].currentpath[0]='\\';
|
|
SMBEnv[i].currentpath[1]='\0';
|
|
SMBEnv[i].first_item_dir=false;
|
|
SMBEnv[i].pos=i;
|
|
SMBEnv[i].SMBReadAheadCache=NULL;
|
|
LWP_MutexInit(&SMBEnv[i]._SMB_mutex, false);
|
|
}
|
|
|
|
if(cache_thread == LWP_THREAD_NULL)
|
|
if(LWP_CreateThread(&cache_thread, process_cache_thread, NULL, NULL, 0, 64) != 0)
|
|
return false;
|
|
|
|
smbInited = true;
|
|
}
|
|
|
|
//root connect
|
|
bool ret = true;
|
|
SMBCONN smbconn;
|
|
if(SMB_Connect(&smbconn, user, password, share, ip) != SMB_SUCCESS)
|
|
return false;
|
|
|
|
for(i=0;i<MAX_SMB_MOUNTED && SMBEnv[i].SMBCONNECTED;i++);
|
|
|
|
if(i==MAX_SMB_MOUNTED)
|
|
{
|
|
SMB_Close(smbconn);
|
|
return false; // all samba connections in use
|
|
}
|
|
|
|
SMBEnv[i].SMBCONNECTED=true; // reserved
|
|
MountDevice(name,smbconn,i);
|
|
return ret;
|
|
}
|
|
|
|
bool smbInit(const char *user, const char *password, const char *share, const char *ip)
|
|
{
|
|
return smbInitDevice("smb", user, password, share, ip);
|
|
}
|
|
|
|
void smbClose(const char* name)
|
|
{
|
|
smb_env *env = FindSMBEnv(name);
|
|
if(env==NULL) return;
|
|
|
|
_SMB_lock(env->pos);
|
|
if(env->SMBCONNECTED)
|
|
SMB_Close(env->smbconn);
|
|
|
|
char device[11];
|
|
sprintf(device, "%s:", env->name);
|
|
RemoveDevice(device);
|
|
env->SMBCONNECTED=false;
|
|
_SMB_unlock(env->pos);
|
|
}
|
|
|
|
bool smbCheckConnection(const char* name)
|
|
{
|
|
char device[10];
|
|
int i;
|
|
bool ret;
|
|
smb_env *env;
|
|
|
|
for(i=0; i < 10 && name[i]!='\0' && name[i]!=':'; i++) device[i]=name[i];
|
|
device[i]='\0';
|
|
|
|
env=FindSMBEnv(device);
|
|
if(env==NULL) return false;
|
|
_SMB_lock(env->pos);
|
|
ret=(SMB_Reconnect(&env->smbconn,true)==SMB_SUCCESS);
|
|
_SMB_unlock(env->pos);
|
|
return ret;
|
|
}
|
|
|
|
void smbSetSearchFlags(unsigned short flags)
|
|
{
|
|
smbFlags = flags;
|
|
}
|