/**************************************************************************** * TinySMB * Nintendo Wii/GameCube SMB implementation * * SMB devoptab ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #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;iSMBWriteCache.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 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(readhandle); 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_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;ipos); 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; }