SMB write cache improvements. patch by dimok.
[libogc.git] / libtinysmb / smb_devoptab.c
blob17e1645475eec9e72bc2951d06c1cc22b7b9de1e
1 /****************************************************************************
2 * TinySMB
3 * Nintendo Wii/GameCube SMB implementation
5 * SMB devoptab
6 ****************************************************************************/
8 #include <malloc.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <sys/iosupport.h>
15 #include <network.h>
16 #include <ogcsys.h>
17 #include <ogc/lwp_watchdog.h>
18 #include <ogc/mutex.h>
19 #include <sys/statvfs.h>
21 #include "smb.h"
23 #define MAX_SMB_MOUNTED 10
25 static lwp_t cache_thread = LWP_THREAD_NULL;
27 typedef struct
29 SMBFILE handle;
30 off_t offset;
31 size_t len;
32 char filename[SMB_MAXPATH];
33 unsigned short access;
34 int env;
35 } SMBFILESTRUCT;
37 typedef struct
39 SMBDIRENTRY smbdir;
40 int env;
41 } SMBDIRSTATESTRUCT;
43 static bool smbInited = false;
44 static unsigned short smbFlags = SMB_SRCH_DIRECTORY | SMB_SRCH_READONLY;
46 ///////////////////////////////////////////
47 // CACHE FUNCTION DEFINITIONS //
48 ///////////////////////////////////////////
49 #define SMB_READ_BUFFERSIZE 65472
50 #define SMB_WRITE_BUFFERSIZE (60*1024)
52 typedef struct
54 off_t offset;
55 u64 last_used;
56 SMBFILESTRUCT *file;
57 void *ptr;
58 } smb_cache_page;
60 typedef struct
62 u64 used;
63 size_t len;
64 SMBFILESTRUCT *file;
65 void *ptr;
66 } smb_write_cache;
68 static void DestroySMBReadAheadCache(const char *name);
69 static void SMBEnableReadAhead(const char *name, u32 pages);
70 static int ReadSMBFromCache(void *buf, size_t len, SMBFILESTRUCT *file);
72 ///////////////////////////////////////////
73 // END CACHE FUNCTION DEFINITIONS //
74 ///////////////////////////////////////////
76 // SMB Enviroment
77 typedef struct
79 char *name;
80 int pos;
81 devoptab_t *devoptab;
83 SMBCONN smbconn;
84 u8 SMBCONNECTED;
86 char currentpath[SMB_MAXPATH];
87 bool first_item_dir;
88 bool diropen_root;
90 smb_write_cache SMBWriteCache;
91 smb_cache_page *SMBReadAheadCache;
92 u32 SMB_RA_pages;
94 mutex_t _SMB_mutex;
95 } smb_env;
97 static smb_env SMBEnv[MAX_SMB_MOUNTED];
99 static inline void _SMB_lock(int i)
101 if(SMBEnv[i]._SMB_mutex!=LWP_MUTEX_NULL) LWP_MutexLock(SMBEnv[i]._SMB_mutex);
104 static inline void _SMB_unlock(int i)
106 if(SMBEnv[i]._SMB_mutex!=LWP_MUTEX_NULL) LWP_MutexUnlock(SMBEnv[i]._SMB_mutex);
109 ///////////////////////////////////////////
110 // CACHE FUNCTIONS //
111 ///////////////////////////////////////////
113 static smb_env* FindSMBEnv(const char *name)
115 int i;
116 char *aux;
118 aux=strdup(name);
119 i=strlen(aux);
120 if(aux[i-1]==':')aux[i-1]='\0';
121 for(i=0;i<MAX_SMB_MOUNTED ;i++)
123 if(SMBEnv[i].SMBCONNECTED && strcmp(aux,SMBEnv[i].name)==0)
125 free(aux);
126 return &SMBEnv[i];
129 free(aux);
130 return NULL;
133 static int FlushWriteSMBCache(char *name)
135 smb_env *env;
136 env=FindSMBEnv(name);
137 if(env==NULL) return -1;
139 if (env->SMBWriteCache.file == NULL || env->SMBWriteCache.len == 0)
141 return 0;
144 int ret;
145 int written = 0;
147 while(env->SMBWriteCache.len > 0)
149 ret = SMB_WriteFile(env->SMBWriteCache.ptr+written, env->SMBWriteCache.len,
150 env->SMBWriteCache.file->offset, env->SMBWriteCache.file->handle);
152 if (ret <= 0)
153 return -1;
155 written += ret;
156 env->SMBWriteCache.file->offset += ret;
157 if (env->SMBWriteCache.file->offset > env->SMBWriteCache.file->len)
158 env->SMBWriteCache.file->len = env->SMBWriteCache.file->offset;
160 env->SMBWriteCache.len-=ret;
161 if(env->SMBWriteCache.len==0) break;
163 env->SMBWriteCache.used = 0;
164 env->SMBWriteCache.file = NULL;
166 return 0;
169 static void DestroySMBReadAheadCache(const char *name)
171 smb_env *env;
172 env=FindSMBEnv(name);
173 if(env==NULL) return ;
175 int i;
176 if (env->SMBReadAheadCache != NULL)
178 for (i = 0; i < env->SMB_RA_pages; i++)
180 if(env->SMBReadAheadCache[i].ptr)
181 free(env->SMBReadAheadCache[i].ptr);
183 free(env->SMBReadAheadCache);
184 env->SMBReadAheadCache = NULL;
185 env->SMB_RA_pages = 0;
187 FlushWriteSMBCache(env->name);
189 if(env->SMBWriteCache.ptr)
190 free(env->SMBWriteCache.ptr);
192 env->SMBWriteCache.used = 0;
193 env->SMBWriteCache.len = 0;
194 env->SMBWriteCache.file = NULL;
195 env->SMBWriteCache.ptr = NULL;
198 static void *process_cache_thread(void *ptr)
200 int i;
201 while (1)
203 for(i=0;i<MAX_SMB_MOUNTED ;i++)
205 if(SMBEnv[i].SMBCONNECTED)
207 if (SMBEnv[i].SMBWriteCache.used > 0)
209 if (ticks_to_millisecs(gettime())-ticks_to_millisecs(SMBEnv[i].SMBWriteCache.used) > 500)
211 _SMB_lock(i);
212 FlushWriteSMBCache(SMBEnv[i].name);
213 _SMB_unlock(i);
218 usleep(10000);
220 return NULL;
223 static void SMBEnableReadAhead(const char *name, u32 pages)
225 int i, j;
227 smb_env *env;
228 env=FindSMBEnv(name);
229 if(env==NULL) return;
231 DestroySMBReadAheadCache(name);
233 if (pages == 0)
234 return;
236 //only 1 page for write
237 env->SMBWriteCache.ptr = memalign(32, SMB_WRITE_BUFFERSIZE);
238 env->SMBWriteCache.used = 0;
239 env->SMBWriteCache.len = 0;
240 env->SMBWriteCache.file = NULL;
242 env->SMB_RA_pages = pages;
243 env->SMBReadAheadCache = (smb_cache_page *) malloc(sizeof(smb_cache_page) * env->SMB_RA_pages);
244 if (env->SMBReadAheadCache == NULL)
245 return;
246 for (i = 0; i < env->SMB_RA_pages; i++)
248 env->SMBReadAheadCache[i].offset = 0;
249 env->SMBReadAheadCache[i].last_used = 0;
250 env->SMBReadAheadCache[i].file = NULL;
251 env->SMBReadAheadCache[i].ptr = memalign(32, SMB_READ_BUFFERSIZE);
252 if (env->SMBReadAheadCache[i].ptr == NULL)
254 for (j = i - 1; j >= 0; j--)
255 if (env->SMBReadAheadCache[j].ptr)
256 free(env->SMBReadAheadCache[j].ptr);
257 free(env->SMBReadAheadCache);
258 env->SMBReadAheadCache = NULL;
259 free(env->SMBWriteCache.ptr);
260 return;
262 memset(env->SMBReadAheadCache[i].ptr, 0, SMB_READ_BUFFERSIZE);
266 // clear cache from file
267 // called when file is closed
268 static void ClearSMBFileCache(SMBFILESTRUCT *file)
270 int i,j;
271 j=file->env;
273 for (i = 0; i < SMBEnv[j].SMB_RA_pages; i++)
275 if (SMBEnv[j].SMBReadAheadCache[i].file == file)
277 SMBEnv[j].SMBReadAheadCache[i].offset = 0;
278 SMBEnv[j].SMBReadAheadCache[i].last_used = 0;
279 SMBEnv[j].SMBReadAheadCache[i].file = NULL;
280 memset(SMBEnv[j].SMBReadAheadCache[i].ptr, 0, SMB_READ_BUFFERSIZE);
285 static int ReadSMBFromCache(void *buf, size_t len, SMBFILESTRUCT *file)
287 int i,j, leastUsed;
288 off_t new_offset, rest;
289 j=file->env;
291 if ( len == 0 ) return 0;
293 if (SMBEnv[j].SMBReadAheadCache == NULL)
295 if (SMB_ReadFile(buf, len, file->offset, file->handle) <= 0)
297 return -1;
299 return 0;
302 new_offset = file->offset;
303 rest = len;
305 continue_read:
307 for (i = 0; i < SMBEnv[j].SMB_RA_pages; i++)
309 if (SMBEnv[j].SMBReadAheadCache[i].file == file)
311 if ((new_offset >= SMBEnv[j].SMBReadAheadCache[i].offset) &&
312 (new_offset < (SMBEnv[j].SMBReadAheadCache[i].offset + SMB_READ_BUFFERSIZE)))
314 //we hit the page
315 //copy as much as we can
316 SMBEnv[j].SMBReadAheadCache[i].last_used = gettime();
318 off_t buffer_used = (SMBEnv[j].SMBReadAheadCache[i].offset + SMB_READ_BUFFERSIZE) - new_offset;
319 if (buffer_used > rest) buffer_used = rest;
320 memcpy(buf, SMBEnv[j].SMBReadAheadCache[i].ptr + (new_offset - SMBEnv[j].SMBReadAheadCache[i].offset), buffer_used);
321 buf += buffer_used;
322 rest -= buffer_used;
323 new_offset += buffer_used;
325 if (rest == 0)
327 return 0;
329 goto continue_read;
334 leastUsed = 0;
335 for ( i = 1; i < SMBEnv[j].SMB_RA_pages; i++)
337 if ((SMBEnv[j].SMBReadAheadCache[i].last_used < SMBEnv[j].SMBReadAheadCache[leastUsed].last_used))
338 leastUsed = i;
341 //fill least used page with new data
343 off_t cache_offset = new_offset;
345 //do not interset with existing pages
346 for (i = 0; i < SMBEnv[j].SMB_RA_pages; i++)
348 if ( i == leastUsed ) continue;
349 if ( SMBEnv[j].SMBReadAheadCache[i].file != file ) continue;
351 if ( (cache_offset < SMBEnv[j].SMBReadAheadCache[i].offset ) && (cache_offset + SMB_READ_BUFFERSIZE > SMBEnv[j].SMBReadAheadCache[i].offset) )
353 //tail of new page intersects with some cache block, clear page
354 SMBEnv[j].SMBReadAheadCache[i].file = NULL;
357 if ( (cache_offset >= SMBEnv[j].SMBReadAheadCache[i].offset ) && (cache_offset < SMBEnv[j].SMBReadAheadCache[i].offset + SMB_READ_BUFFERSIZE ) )
359 //head of new page intersects with some cache block, clear page
360 SMBEnv[j].SMBReadAheadCache[i].file = NULL;
364 off_t cache_to_read = file->len - cache_offset;
365 if ( cache_to_read > SMB_READ_BUFFERSIZE )
367 cache_to_read = SMB_READ_BUFFERSIZE;
370 int read=0;
371 while(read<cache_to_read)
373 if ( (read+=SMB_ReadFile(SMBEnv[j].SMBReadAheadCache[leastUsed].ptr+read, cache_to_read-read, cache_offset+read, file->handle)) <=0 )
375 SMBEnv[j].SMBReadAheadCache[leastUsed].file = NULL;
376 return -1;
379 SMBEnv[j].SMBReadAheadCache[leastUsed].last_used = gettime();
381 SMBEnv[j].SMBReadAheadCache[leastUsed].offset = cache_offset;
382 SMBEnv[j].SMBReadAheadCache[leastUsed].file = file;
384 goto continue_read;
387 static int WriteSMBUsingCache(const char *buf, size_t len, SMBFILESTRUCT *file)
389 size_t size=len;
390 if (file == NULL || buf == NULL)
391 return -1;
392 int j;
393 j=file->env;
394 if (SMBEnv[j].SMBWriteCache.file != NULL)
396 if (strcmp(SMBEnv[j].SMBWriteCache.file->filename, file->filename) != 0)
398 //Flush current buffer
399 if (FlushWriteSMBCache(SMBEnv[j].name) < 0)
401 goto failed;
403 SMBEnv[j].SMBWriteCache.file = file;
404 SMBEnv[j].SMBWriteCache.len = 0;
407 else
409 SMBEnv[j].SMBWriteCache.file = file;
410 SMBEnv[j].SMBWriteCache.len = 0;
412 int rest;
413 s32 written;
414 while(len > 0)
416 if(SMBEnv[j].SMBWriteCache.len+len >= SMB_WRITE_BUFFERSIZE)
418 rest = SMB_WRITE_BUFFERSIZE - SMBEnv[j].SMBWriteCache.len;
419 memcpy(SMBEnv[j].SMBWriteCache.ptr + SMBEnv[j].SMBWriteCache.len, buf, rest);
421 written = SMB_WriteFile(SMBEnv[j].SMBWriteCache.ptr, SMB_WRITE_BUFFERSIZE, file->offset, file->handle);
422 if(written<0)
424 goto failed;
426 file->offset += written;
427 if (file->offset > file->len)
428 file->len = file->offset;
430 buf += rest;
431 len -= rest;
432 SMBEnv[j].SMBWriteCache.used = gettime();
433 SMBEnv[j].SMBWriteCache.len = 0;
435 else
437 memcpy(SMBEnv[j].SMBWriteCache.ptr + SMBEnv[j].SMBWriteCache.len, buf, len);
438 SMBEnv[j].SMBWriteCache.len += len;
439 SMBEnv[j].SMBWriteCache.used = gettime();
440 break;
443 return size;
445 failed:
446 return -1;
449 ///////////////////////////////////////////
450 // END CACHE FUNCTIONS //
451 ///////////////////////////////////////////
453 static char *smb_absolute_path_no_device(const char *srcpath, char *destpath, int env)
455 char temp[SMB_MAXPATH];
456 int i=0,j=0;
458 if (strchr(srcpath, ':') != NULL)
460 srcpath = strchr(srcpath, ':') + 1;
462 if (strchr(srcpath, ':') != NULL)
464 return NULL;
467 temp[0]='\0';
469 if (srcpath[0] != '\\' && srcpath[0] != '/')
471 strcpy(temp, SMBEnv[env].currentpath);
473 if(srcpath[0]!='\0') //strlen(srcpath) > 0
475 if(srcpath[0]=='.' && (srcpath[1]=='\\' || srcpath[1]=='\\' || srcpath[1]=='\0')) // to fix opendir(".") or chdir(".")
476 strcat(temp, &srcpath[1]);
477 else
478 strcat(temp, srcpath);
481 while(temp[i]!='\0')
483 if(temp[i]=='/')
485 destpath[j++]='\\';
486 while(temp[i]!='\0' && (temp[i]=='/' || temp[i]=='\\'))i++;
488 else if(srcpath[i]=='\\')
490 destpath[j++]=temp[i++];
491 while(temp[i]!='\0' && (temp[i]=='/' || temp[i]=='\\'))i++;
493 else
495 destpath[j++]=temp[i++];
498 destpath[j]='\0';
500 return destpath;
503 static char *ExtractDevice(const char *path, char *device)
505 int i,l;
506 l=strlen(path);
508 for(i=0;i<l && path[i]!='\0' && path[i]!=':' && i < 20;i++)
509 device[i]=path[i];
510 if(path[i]!=':')device[0]='\0';
511 else device[i]='\0';
512 return device;
515 //FILE IO
516 static int __smb_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
518 SMBFILESTRUCT *file = (SMBFILESTRUCT*) fileStruct;
519 char fixedpath[SMB_MAXPATH];
520 smb_env *env;
522 ExtractDevice(path,fixedpath);
523 if(fixedpath[0]=='\0')
525 getcwd(fixedpath,SMB_MAXPATH);
526 ExtractDevice(fixedpath,fixedpath);
528 env=FindSMBEnv(fixedpath);
529 if(env==NULL)
531 r->_errno = ENODEV;
532 return -1;
534 file->env=env->pos;
536 if (!env->SMBCONNECTED)
538 r->_errno = ENODEV;
539 return -1;
542 if (smb_absolute_path_no_device(path, fixedpath, file->env) == NULL)
544 r->_errno = EINVAL;
545 return -1;
548 if(!smbCheckConnection(env->name))
550 r->_errno = ENODEV;
551 return -1;
554 SMBDIRENTRY dentry;
555 bool fileExists = true;
556 _SMB_lock(env->pos);
557 if (SMB_PathInfo(fixedpath, &dentry, env->smbconn) != SMB_SUCCESS)
558 fileExists = false;
560 // Determine which mode the file is open for
561 u8 smb_mode;
562 unsigned short access;
563 if ((flags & 0x03) == O_RDONLY)
565 // Open the file for read-only access
566 smb_mode = SMB_OF_OPEN;
567 access = SMB_OPEN_READING;
569 else if ((flags & 0x03) == O_WRONLY)
571 // Open file for write only access
572 if (fileExists)
573 smb_mode = SMB_OF_OPEN;
574 else
575 smb_mode = SMB_OF_CREATE;
576 access = SMB_OPEN_WRITING;
578 else if ((flags & 0x03) == O_RDWR)
580 // Open file for read/write access
581 access = SMB_OPEN_READWRITE;
582 if (fileExists)
583 smb_mode = SMB_OF_OPEN;
584 else
585 smb_mode = SMB_OF_CREATE;
587 else
589 r->_errno = EACCES;
590 _SMB_unlock(env->pos);
591 return -1;
594 if ((flags & O_CREAT) && !fileExists)
595 smb_mode = SMB_OF_CREATE;
596 if (!(flags & O_APPEND) && fileExists && ((flags & 0x03) != O_RDONLY))
597 smb_mode = SMB_OF_TRUNCATE;
599 file->handle = SMB_OpenFile(fixedpath, access, smb_mode, env->smbconn);
600 if (!file->handle)
602 r->_errno = ENOENT;
603 _SMB_unlock(env->pos);
604 return -1;
607 file->len = 0;
608 if (fileExists)
609 file->len = dentry.size;
611 if (flags & O_APPEND)
612 file->offset = file->len;
613 else
614 file->offset = 0;
616 file->access=access;
618 strcpy(file->filename, fixedpath);
619 _SMB_unlock(env->pos);
620 return 0;
623 static off_t __smb_seek(struct _reent *r, int fd, off_t pos, int dir)
625 SMBFILESTRUCT *file = (SMBFILESTRUCT*) fd;
626 off_t position;
628 if (file == NULL)
630 r->_errno = EBADF;
631 return -1;
634 //have to flush because SMBWriteCache.file->offset holds offset of cached block not yet written
635 _SMB_lock(file->env);
636 if (SMBEnv[file->env].SMBWriteCache.file == file)
638 FlushWriteSMBCache(SMBEnv[file->env].name);
641 switch (dir)
643 case SEEK_SET:
644 position = pos;
645 break;
646 case SEEK_CUR:
647 position = file->offset + pos;
648 break;
649 case SEEK_END:
650 position = file->len + pos;
651 break;
652 default:
653 r->_errno = EINVAL;
654 _SMB_unlock(file->env);
655 return -1;
658 if (pos > 0 && position < 0)
660 r->_errno = EOVERFLOW;
661 _SMB_unlock(file->env);
662 return -1;
664 if (position < 0)
666 r->_errno = EINVAL;
667 _SMB_unlock(file->env);
668 return -1;
671 // Save position
672 file->offset = position;
673 _SMB_unlock(file->env);
674 return position;
677 static ssize_t __smb_read(struct _reent *r, int fd, char *ptr, size_t len)
679 size_t offset = 0;
680 size_t readsize;
681 int ret = 0;
682 int cnt_retry=2;
683 SMBFILESTRUCT *file = (SMBFILESTRUCT*) fd;
685 if (file == NULL)
687 r->_errno = EBADF;
688 return -1;
691 //have to flush because SMBWriteCache.file->offset holds offset of cached block not yet writeln
692 //and file->len also may not have been updated yet
693 _SMB_lock(file->env);
694 if (SMBEnv[file->env].SMBWriteCache.file == file)
696 FlushWriteSMBCache(SMBEnv[file->env].name);
699 // Don't try to read if the read pointer is past the end of file
700 if (file->offset >= file->len)
702 r->_errno = EOVERFLOW;
703 _SMB_unlock(file->env);
704 return 0;
707 // Don't read past end of file
708 if (len + file->offset > file->len)
710 r->_errno = EOVERFLOW;
711 len = file->len - file->offset;
714 // Short circuit cases where len is 0 (or less)
715 if (len == 0)
717 _SMB_unlock(file->env);
718 return 0;
721 if (len < 0)
723 r->_errno = EOVERFLOW;
724 _SMB_unlock(file->env);
725 return -1;
728 retry_read:
729 while(offset < len)
731 readsize = len - offset;
732 if(readsize > SMB_READ_BUFFERSIZE) readsize = SMB_READ_BUFFERSIZE;
733 ret = ReadSMBFromCache(ptr+offset, readsize, file);
734 if(ret < 0) break;
735 offset += readsize;
736 file->offset += readsize;
739 if (ret < 0)
741 retry_reconnect:
742 cnt_retry--;
743 if(cnt_retry>=0)
745 if(smbCheckConnection(SMBEnv[file->env].name))
747 ClearSMBFileCache(file);
748 file->handle = SMB_OpenFile(file->filename, file->access, SMB_OF_OPEN, SMBEnv[file->env].smbconn);
749 if (!file->handle)
751 r->_errno = ENOENT;
752 _SMB_unlock(file->env);
753 return -1;
755 goto retry_read;
757 usleep(10000);
758 goto retry_reconnect;
760 r->_errno = EIO;
761 _SMB_unlock(file->env);
762 return -1;
764 _SMB_unlock(file->env);
765 return len;
768 static ssize_t __smb_write(struct _reent *r, int fd, const char *ptr, size_t len)
770 SMBFILESTRUCT *file = (SMBFILESTRUCT*) fd;
771 int written;
772 if (file == NULL)
774 r->_errno = EBADF;
775 return -1;
778 // Don't try to write if the pointer is past the end of file
779 if (file->offset > file->len)
781 r->_errno = EOVERFLOW;
782 return -1;
785 // Short circuit cases where len is 0 (or less)
786 if (len == 0)
787 return 0;
789 if (len < 0)
791 r->_errno = EOVERFLOW;
792 return -1;
795 _SMB_lock(file->env);
796 written = WriteSMBUsingCache(ptr, len, file);
797 _SMB_unlock(file->env);
799 if (written <= 0)
801 r->_errno = EIO;
802 return -1;
805 return written;
808 static int __smb_close(struct _reent *r, int fd)
810 SMBFILESTRUCT *file = (SMBFILESTRUCT*) fd;
811 int j;
812 j=file->env;
813 _SMB_lock(j);
814 if (SMBEnv[j].SMBWriteCache.file == file)
816 FlushWriteSMBCache(SMBEnv[j].name);
818 ClearSMBFileCache(file);
819 SMB_CloseFile(file->handle);
820 file->len = 0;
821 file->offset = 0;
822 file->filename[0] = '\0';
823 _SMB_unlock(j);
825 return 0;
828 static int __smb_chdir(struct _reent *r, const char *path)
830 char path_absolute[SMB_MAXPATH];
832 SMBDIRENTRY dentry;
833 int found;
835 ExtractDevice(path,path_absolute);
836 if(path_absolute[0]=='\0')
838 getcwd(path_absolute,SMB_MAXPATH);
839 ExtractDevice(path_absolute,path_absolute);
842 smb_env* env;
843 env=FindSMBEnv(path_absolute);
845 if(env == NULL)
847 r->_errno = ENODEV;
848 return -1;
851 if (!env->SMBCONNECTED)
853 r->_errno = ENODEV;
854 return -1;
857 if (smb_absolute_path_no_device(path, path_absolute,env->pos) == NULL)
859 r->_errno = EINVAL;
860 return -1;
863 if(!smbCheckConnection(env->name))
865 r->_errno = ENODEV;
866 return -1;
869 memset(&dentry, 0, sizeof(SMBDIRENTRY));
871 _SMB_lock(env->pos);
872 found = SMB_PathInfo(path_absolute, &dentry, env->smbconn);
874 if (found != SMB_SUCCESS)
876 r->_errno = ENOENT;
877 _SMB_unlock(env->pos);
878 return -1;
881 if (!(dentry.attributes & SMB_SRCH_DIRECTORY))
883 r->_errno = ENOTDIR;
884 _SMB_unlock(env->pos);
885 return -1;
888 strcpy(env->currentpath, path_absolute);
889 if (env->currentpath[0] != 0)
891 if (env->currentpath[strlen(env->currentpath) - 1] != '\\')
892 strcat(env->currentpath, "\\");
894 _SMB_unlock(env->pos);
895 return 0;
898 static int __smb_dirreset(struct _reent *r, DIR_ITER *dirState)
900 char path_abs[SMB_MAXPATH];
901 SMBDIRSTATESTRUCT* state = (SMBDIRSTATESTRUCT*) (dirState->dirStruct);
902 SMBDIRENTRY dentry;
904 memset(&dentry, 0, sizeof(SMBDIRENTRY));
906 _SMB_lock(state->env);
907 SMB_FindClose(&state->smbdir, SMBEnv[state->env].smbconn);
909 strcpy(path_abs,SMBEnv[state->env].currentpath);
910 strcat(path_abs,"*");
911 int found = SMB_FindFirst(path_abs, smbFlags, &dentry, SMBEnv[state->env].smbconn);
913 if (found != SMB_SUCCESS)
915 r->_errno = ENOENT;
916 _SMB_unlock(state->env);
917 return -1;
920 if (!(dentry.attributes & SMB_SRCH_DIRECTORY))
922 r->_errno = ENOTDIR;
923 _SMB_unlock(state->env);
924 return -1;
927 state->smbdir.size = dentry.size;
928 state->smbdir.ctime = dentry.ctime;
929 state->smbdir.atime = dentry.atime;
930 state->smbdir.mtime = dentry.mtime;
931 state->smbdir.attributes = dentry.attributes;
932 state->smbdir.sid = dentry.sid;
933 strcpy(state->smbdir.name, dentry.name);
935 SMBEnv[state->env].first_item_dir = true;
936 _SMB_unlock(state->env);
937 return 0;
940 static DIR_ITER* __smb_diropen(struct _reent *r, DIR_ITER *dirState, const char *path)
942 char path_absolute[SMB_MAXPATH];
943 int found;
944 SMBDIRSTATESTRUCT* state = (SMBDIRSTATESTRUCT*) (dirState->dirStruct);
945 SMBDIRENTRY dentry;
947 ExtractDevice(path,path_absolute);
948 if(path_absolute[0]=='\0')
950 getcwd(path_absolute,SMB_MAXPATH);
951 ExtractDevice(path_absolute,path_absolute);
954 smb_env* env;
955 env=FindSMBEnv(path_absolute);
957 if(env == NULL)
959 r->_errno = ENODEV;
960 return NULL;
963 if (smb_absolute_path_no_device(path, path_absolute, env->pos) == NULL)
965 r->_errno = EINVAL;
966 return NULL;
968 if (strlen(path_absolute) > 0 && path_absolute[strlen(path_absolute) - 1] != '\\')
969 strcat(path_absolute, "\\");
971 if(!strcmp(path_absolute,"\\"))
972 env->diropen_root=true;
973 else
974 env->diropen_root=false;
976 _SMB_lock(env->pos);
977 if(!env->diropen_root) // root must be valid - we don't need check it
979 memset(&dentry, 0, sizeof(SMBDIRENTRY));
980 found = SMB_PathInfo(path_absolute, &dentry, env->smbconn);
981 if (found != SMB_SUCCESS)
983 r->_errno = ENOENT;
984 _SMB_unlock(env->pos);
985 return NULL;
988 if (!(dentry.attributes & SMB_SRCH_DIRECTORY))
990 r->_errno = ENOTDIR;
991 _SMB_unlock(env->pos);
992 return NULL;
996 strcat(path_absolute, "*");
997 memset(&dentry, 0, sizeof(SMBDIRENTRY));
998 found = SMB_FindFirst(path_absolute, smbFlags, &dentry, env->smbconn);
1000 if (found != SMB_SUCCESS)
1002 r->_errno = ENOENT;
1003 _SMB_unlock(env->pos);
1004 return NULL;
1007 state->env=env->pos;
1008 state->smbdir.size = dentry.size;
1009 state->smbdir.ctime = dentry.ctime;
1010 state->smbdir.atime = dentry.atime;
1011 state->smbdir.mtime = dentry.mtime;
1012 state->smbdir.attributes = dentry.attributes;
1013 state->smbdir.sid = dentry.sid;
1014 strcpy(state->smbdir.name, dentry.name);
1015 env->first_item_dir = true;
1016 _SMB_unlock(env->pos);
1017 return dirState;
1020 static int dentry_to_stat(SMBDIRENTRY *dentry, struct stat *st)
1022 if (!st)
1023 return -1;
1024 if (!dentry)
1025 return -1;
1027 st->st_dev = 0;
1028 st->st_ino = 0;
1030 st->st_mode = ((dentry->attributes & SMB_SRCH_DIRECTORY) ? S_IFDIR
1031 : S_IFREG);
1032 st->st_nlink = 1;
1033 st->st_uid = 1; // Faked
1034 st->st_rdev = st->st_dev;
1035 st->st_gid = 2; // Faked
1036 st->st_size = dentry->size;
1037 st->st_atime = dentry->atime/10000000.0 - 11644473600LL;
1038 st->st_spare1 = 0;
1039 st->st_mtime = dentry->mtime/10000000.0 - 11644473600LL;
1040 st->st_spare2 = 0;
1041 st->st_ctime = dentry->ctime/10000000.0 - 11644473600LL;
1042 st->st_spare3 = 0;
1043 st->st_blksize = 1024;
1044 st->st_blocks = (st->st_size + st->st_blksize - 1) / st->st_blksize; // File size in blocks
1045 st->st_spare4[0] = 0;
1046 st->st_spare4[1] = 0;
1048 return 0;
1051 static int __smb_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename,
1052 struct stat *filestat)
1054 int ret;
1055 SMBDIRSTATESTRUCT* state = (SMBDIRSTATESTRUCT*) (dirState->dirStruct);
1056 SMBDIRENTRY dentry;
1058 if (SMBEnv[state->env].currentpath[0] == '\0' || filestat == NULL)
1060 r->_errno = ENOENT;
1061 return -1;
1064 memset(&dentry, 0, sizeof(SMBDIRENTRY));
1065 _SMB_lock(state->env);
1066 if (SMBEnv[state->env].first_item_dir)
1068 SMBEnv[state->env].first_item_dir = false;
1069 dentry.size = state->smbdir.size;
1070 dentry.ctime = state->smbdir.ctime;
1071 dentry.atime = state->smbdir.atime;
1072 dentry.mtime = state->smbdir.mtime;
1073 dentry.attributes = state->smbdir.attributes;
1074 strcpy(dentry.name, state->smbdir.name);
1075 strcpy(filename, dentry.name);
1076 dentry_to_stat(&dentry, filestat);
1077 _SMB_unlock(state->env);
1078 return 0;
1081 dentry.sid = state->smbdir.sid;
1083 ret = SMB_FindNext(&dentry, SMBEnv[state->env].smbconn);
1084 if(ret==SMB_SUCCESS && SMBEnv[state->env].diropen_root)
1086 if(strlen(dentry.name) == 2 && strcmp(dentry.name,"..") == 0)
1087 ret = SMB_FindNext(&dentry, SMBEnv[state->env].smbconn);
1089 if (ret == SMB_SUCCESS)
1091 state->smbdir.size = dentry.size;
1092 state->smbdir.ctime = dentry.ctime;
1093 state->smbdir.atime = dentry.atime;
1094 state->smbdir.mtime = dentry.mtime;
1095 state->smbdir.attributes = dentry.attributes;
1096 strcpy(state->smbdir.name, dentry.name);
1097 strcpy(filename, dentry.name);
1099 else
1101 r->_errno = ENOENT;
1102 _SMB_unlock(state->env);
1103 return -1;
1106 dentry_to_stat(&dentry, filestat);
1107 _SMB_unlock(state->env);
1108 return 0;
1111 static int __smb_dirclose(struct _reent *r, DIR_ITER *dirState)
1113 SMBDIRSTATESTRUCT* state = (SMBDIRSTATESTRUCT*) (dirState->dirStruct);
1115 int j = state->env;
1116 _SMB_lock(j);
1117 SMB_FindClose(&state->smbdir, SMBEnv[j].smbconn);
1118 memset(state, 0, sizeof(SMBDIRSTATESTRUCT));
1119 _SMB_unlock(j);
1120 return 0;
1123 static int __smb_stat(struct _reent *r, const char *path, struct stat *st)
1125 char path_absolute[SMB_MAXPATH];
1126 SMBDIRENTRY dentry;
1128 ExtractDevice(path,path_absolute);
1129 if(path_absolute[0]=='\0')
1131 getcwd(path_absolute,SMB_MAXPATH);
1132 ExtractDevice(path_absolute,path_absolute);
1135 smb_env* env;
1136 env=FindSMBEnv(path_absolute);
1138 if(env == NULL)
1140 r->_errno = ENODEV;
1141 return -1;
1144 if (smb_absolute_path_no_device(path, path_absolute, env->pos) == NULL)
1146 r->_errno = EINVAL;
1147 return -1;
1149 _SMB_lock(env->pos);
1150 if (SMB_PathInfo(path_absolute, &dentry, env->smbconn) != SMB_SUCCESS)
1152 r->_errno = ENOENT;
1153 _SMB_unlock(env->pos);
1154 return -1;
1157 if (dentry.name[0] == '\0')
1159 r->_errno = ENOENT;
1160 _SMB_unlock(env->pos);
1161 return -1;
1164 dentry_to_stat(&dentry, st);
1165 _SMB_unlock(env->pos);
1167 return 0;
1170 static int __smb_fstat(struct _reent *r, int fd, struct stat *st)
1172 SMBFILESTRUCT *filestate = (SMBFILESTRUCT *) fd;
1174 if (!filestate)
1176 r->_errno = EBADF;
1177 return -1;
1180 st->st_size = filestate->len;
1182 return 0;
1185 static int __smb_mkdir(struct _reent *r, const char *name, int mode)
1187 char fixedName[SMB_MAXPATH];
1188 smb_env *env;
1190 ExtractDevice(name,fixedName);
1191 if(fixedName[0]=='\0')
1193 getcwd(fixedName,SMB_MAXPATH);
1194 ExtractDevice(fixedName,fixedName);
1197 env=FindSMBEnv(fixedName);
1198 if(env==NULL)
1200 r->_errno = ENODEV;
1201 return -1;
1204 if (smb_absolute_path_no_device(name, fixedName, env->pos) == NULL)
1206 r->_errno = EINVAL;
1207 return -1;
1210 s32 ret = 0;
1211 _SMB_lock(env->pos);
1212 if(SMB_CreateDirectory(fixedName, env->smbconn) != SMB_SUCCESS)
1213 ret = -1;
1214 _SMB_unlock(env->pos);
1216 return ret;
1219 static int __smb_unlink(struct _reent *r, const char *name)
1221 char fixedName[SMB_MAXPATH];
1222 smb_env *env;
1223 bool isDir = false;
1225 DIR_ITER *dir = NULL;
1226 dir = diropen(name);
1227 if(dir)
1229 dirclose(dir);
1230 isDir = true;
1233 ExtractDevice(name,fixedName);
1234 if(fixedName[0]=='\0')
1236 getcwd(fixedName,SMB_MAXPATH);
1237 ExtractDevice(fixedName,fixedName);
1240 env=FindSMBEnv(fixedName);
1241 if(env==NULL)
1243 r->_errno = ENODEV;
1244 return -1;
1247 if (smb_absolute_path_no_device(name, fixedName, env->pos) == NULL)
1249 r->_errno = EINVAL;
1250 return -1;
1253 s32 ret = 0;
1254 _SMB_lock(env->pos);
1255 if(isDir)
1256 ret = SMB_DeleteDirectory(fixedName, env->smbconn);
1257 else
1258 ret = SMB_DeleteFile(fixedName, env->smbconn);
1259 _SMB_unlock(env->pos);
1261 if(ret != SMB_SUCCESS)
1262 return -1;
1264 return 0;
1267 static int __smb_rename(struct _reent *r, const char *oldName, const char *newName)
1269 char fixedOldName[SMB_MAXPATH];
1270 char fixedNewName[SMB_MAXPATH];
1271 smb_env *env;
1273 ExtractDevice(oldName,fixedOldName);
1274 if(fixedOldName[0]=='\0')
1276 getcwd(fixedOldName,SMB_MAXPATH);
1277 ExtractDevice(fixedOldName,fixedOldName);
1280 env=FindSMBEnv(fixedOldName);
1281 if(env==NULL)
1283 r->_errno = ENODEV;
1284 return -1;
1287 if (smb_absolute_path_no_device(oldName, fixedOldName, env->pos) == NULL)
1289 r->_errno = EINVAL;
1290 return -1;
1293 ExtractDevice(newName,fixedNewName);
1294 if(fixedNewName[0]=='\0')
1296 getcwd(fixedNewName,SMB_MAXPATH);
1297 ExtractDevice(fixedNewName,fixedNewName);
1300 if (smb_absolute_path_no_device(newName, fixedNewName, env->pos) == NULL)
1302 r->_errno = EINVAL;
1303 return -1;
1306 s32 ret = 0;
1307 _SMB_lock(env->pos);
1308 if (SMB_Rename(fixedOldName, fixedNewName, env->smbconn) != SMB_SUCCESS)
1309 ret = -1;
1310 _SMB_unlock(env->pos);
1312 return ret;
1315 static int __smb_statvfs_r(struct _reent *r, const char *name, struct statvfs *buf)
1317 char fixedName[SMB_MAXPATH];
1318 smb_env *env;
1320 ExtractDevice(name,fixedName);
1321 if(fixedName[0]=='\0')
1323 getcwd(fixedName,SMB_MAXPATH);
1324 ExtractDevice(fixedName,fixedName);
1327 env=FindSMBEnv(fixedName);
1328 if(env==NULL)
1330 r->_errno = ENODEV;
1331 return -1;
1334 if (smb_absolute_path_no_device(name, fixedName, env->pos) == NULL)
1336 r->_errno = EINVAL;
1337 return -1;
1340 s32 ret = 0;
1341 _SMB_lock(env->pos);
1342 if(SMB_DiskInformation(buf, env->smbconn) != SMB_SUCCESS)
1343 ret = -1;
1344 _SMB_unlock(env->pos);
1346 return ret;
1349 static void MountDevice(const char *name,SMBCONN smbconn, int env)
1351 devoptab_t *dotab_smb;
1352 char *aux;
1353 int l;
1355 aux=strdup(name);
1356 l=strlen(aux);
1357 if(aux[l-1]==':')aux[l-1]='\0';
1359 dotab_smb=(devoptab_t*)malloc(sizeof(devoptab_t));
1361 dotab_smb->name=strdup(aux);
1362 dotab_smb->structSize=sizeof(SMBFILESTRUCT); // size of file structure
1363 dotab_smb->open_r=__smb_open; // device open
1364 dotab_smb->close_r=__smb_close; // device close
1365 dotab_smb->write_r=__smb_write; // device write
1366 dotab_smb->read_r=__smb_read; // device read
1367 dotab_smb->seek_r=__smb_seek; // device seek
1368 dotab_smb->fstat_r=__smb_fstat; // device fstat
1369 dotab_smb->stat_r=__smb_stat; // device stat
1370 dotab_smb->link_r=NULL; // device link
1371 dotab_smb->unlink_r=__smb_unlink; // device unlink
1372 dotab_smb->chdir_r=__smb_chdir; // device chdir
1373 dotab_smb->rename_r=__smb_rename; // device rename
1374 dotab_smb->mkdir_r=__smb_mkdir; // device mkdir
1376 dotab_smb->dirStateSize=sizeof(SMBDIRSTATESTRUCT); // dirStateSize
1377 dotab_smb->diropen_r=__smb_diropen; // device diropen_r
1378 dotab_smb->dirreset_r=__smb_dirreset; // device dirreset_r
1379 dotab_smb->dirnext_r=__smb_dirnext; // device dirnext_r
1380 dotab_smb->dirclose_r=__smb_dirclose; // device dirclose_r
1381 dotab_smb->statvfs_r=__smb_statvfs_r; // device statvfs_r
1382 dotab_smb->ftruncate_r=NULL; // device ftruncate_r
1383 dotab_smb->fsync_r=NULL; // device fsync_r
1384 dotab_smb->deviceData=NULL; /* Device data */
1386 AddDevice(dotab_smb);
1388 SMBEnv[env].pos=env;
1389 SMBEnv[env].smbconn=smbconn;
1390 SMBEnv[env].first_item_dir=false;
1391 SMBEnv[env].diropen_root=false;
1392 SMBEnv[env].devoptab=dotab_smb;
1393 SMBEnv[env].SMBCONNECTED=true;
1394 SMBEnv[env].name=strdup(aux);
1396 SMBEnableReadAhead(aux,8);
1398 free(aux);
1401 bool smbInitDevice(const char* name, const char *user, const char *password, const char *share, const char *ip)
1403 int i;
1405 if(!name || strlen(name) > 9)
1406 return false;
1408 char devname[10];
1409 sprintf(devname, "%s:", name);
1410 if(FindDevice(devname) >= 0)
1411 return false;
1413 if(!smbInited)
1415 for(i=0;i<MAX_SMB_MOUNTED;i++)
1417 SMBEnv[i].SMBCONNECTED=false;
1418 SMBEnv[i].currentpath[0]='\\';
1419 SMBEnv[i].currentpath[1]='\0';
1420 SMBEnv[i].first_item_dir=false;
1421 SMBEnv[i].pos=i;
1422 SMBEnv[i].SMBReadAheadCache=NULL;
1423 LWP_MutexInit(&SMBEnv[i]._SMB_mutex, false);
1426 if(cache_thread == LWP_THREAD_NULL)
1427 if(LWP_CreateThread(&cache_thread, process_cache_thread, NULL, NULL, 0, 64) != 0)
1428 return false;
1430 smbInited = true;
1433 //root connect
1434 bool ret = true;
1435 SMBCONN smbconn;
1436 if(SMB_Connect(&smbconn, user, password, share, ip) != SMB_SUCCESS)
1437 return false;
1439 for(i=0;i<MAX_SMB_MOUNTED && SMBEnv[i].SMBCONNECTED;i++);
1441 if(i==MAX_SMB_MOUNTED)
1443 SMB_Close(smbconn);
1444 return false; // all samba connections in use
1447 SMBEnv[i].SMBCONNECTED=true; // reserved
1448 MountDevice(name,smbconn,i);
1449 return ret;
1452 bool smbInit(const char *user, const char *password, const char *share, const char *ip)
1454 return smbInitDevice("smb", user, password, share, ip);
1457 void smbClose(const char* name)
1459 smb_env *env = FindSMBEnv(name);
1460 if(env==NULL) return;
1462 _SMB_lock(env->pos);
1463 if(env->SMBCONNECTED)
1464 SMB_Close(env->smbconn);
1466 char device[11];
1467 sprintf(device, "%s:", env->name);
1468 RemoveDevice(device);
1469 env->SMBCONNECTED=false;
1470 _SMB_unlock(env->pos);
1473 bool smbCheckConnection(const char* name)
1475 char device[10];
1476 int i;
1477 bool ret;
1478 smb_env *env;
1480 for(i=0; i < 10 && name[i]!='\0' && name[i]!=':'; i++) device[i]=name[i];
1481 device[i]='\0';
1483 env=FindSMBEnv(device);
1484 if(env==NULL) return false;
1485 _SMB_lock(env->pos);
1486 ret=(SMB_Reconnect(&env->smbconn,true)==SMB_SUCCESS);
1487 _SMB_unlock(env->pos);
1488 return ret;
1491 void smbSetSearchFlags(unsigned short flags)
1493 smbFlags = flags;