change PAD_ScanPads()s behaviour. the return value now contains a bitmask of the...
[libogc.git] / libtinysmb / smb_devoptab.c
blob81f71590a17897f67d6a039036c350a63d7a71c1
1 /****************************************************************************
2 * TinySMB
3 * Nintendo Wii/GameCube SMB implementation
5 * SMB devoptab
6 ****************************************************************************/
7 #include <malloc.h>
8 #include <sys/iosupport.h>
9 #include <fcntl.h>
10 #include <unistd.h>
11 #include <errno.h>
12 #include <string.h>
13 #include <stdio.h>
14 #include <dirent.h>
16 #include <ogc/lwp.h>
17 #include <ogc/lwp_watchdog.h>
18 #include <ogc/mutex.h>
20 #include "smb.h"
22 #define MAX_SMB_MOUNTED 10
24 static lwp_t cache_thread = LWP_THREAD_NULL;
25 static SMBDIRENTRY last_dentry;
26 static int last_env=-1;
27 static char last_path[SMB_MAXPATH];
30 typedef struct
32 SMBFILE handle;
33 off_t offset;
34 off_t len;
35 char filename[SMB_MAXPATH];
36 unsigned short access;
37 int env;
38 u32 attributes;
39 } SMBFILESTRUCT;
41 typedef struct
43 SMBDIRENTRY smbdir;
44 int env;
45 char dir[SMB_MAXPATH];
46 } SMBDIRSTATESTRUCT;
48 static bool smbInited = false;
49 static unsigned short smbFlags = SMB_SRCH_DIRECTORY | SMB_SRCH_READONLY;
51 ///////////////////////////////////////////
52 // CACHE FUNCTION DEFINITIONS //
53 ///////////////////////////////////////////
54 #define SMB_READ_BUFFERSIZE 65472
55 #define SMB_WRITE_BUFFERSIZE (60*1024)
57 typedef struct
59 off_t offset;
60 u64 last_used;
61 SMBFILESTRUCT *file;
62 void *ptr;
63 } smb_cache_page;
65 typedef struct
67 u64 used;
68 size_t len;
69 SMBFILESTRUCT *file;
70 void *ptr;
71 } smb_write_cache;
73 static void DestroySMBReadAheadCache(const char *name);
74 static void SMBEnableReadAhead(const char *name, u32 pages);
75 static int ReadSMBFromCache(void *buf, size_t len, SMBFILESTRUCT *file);
77 ///////////////////////////////////////////
78 // END CACHE FUNCTION DEFINITIONS //
79 ///////////////////////////////////////////
81 // SMB Enviroment
82 typedef struct
84 char *name;
85 int pos;
86 devoptab_t *devoptab;
88 SMBCONN smbconn;
89 u8 SMBCONNECTED;
91 char currentpath[SMB_MAXPATH];
92 bool first_item_dir;
93 bool diropen_root;
95 smb_write_cache SMBWriteCache;
96 smb_cache_page *SMBReadAheadCache;
97 int SMB_RA_pages;
99 mutex_t _SMB_mutex;
100 } smb_env;
102 static smb_env SMBEnv[MAX_SMB_MOUNTED];
104 static inline void _SMB_lock(int i)
106 if(SMBEnv[i]._SMB_mutex!=LWP_MUTEX_NULL) LWP_MutexLock(SMBEnv[i]._SMB_mutex);
109 static inline void _SMB_unlock(int i)
111 if(SMBEnv[i]._SMB_mutex!=LWP_MUTEX_NULL) LWP_MutexUnlock(SMBEnv[i]._SMB_mutex);
114 ///////////////////////////////////////////
115 // CACHE FUNCTIONS //
116 ///////////////////////////////////////////
118 static smb_env* FindSMBEnv(const char *name)
120 int i;
121 char *aux;
123 aux=strdup(name);
124 i=strlen(aux);
125 if(aux[i-1]==':')aux[i-1]='\0';
126 for(i=0;i<MAX_SMB_MOUNTED ;i++)
128 if(SMBEnv[i].SMBCONNECTED && strcmp(aux,SMBEnv[i].name)==0)
130 free(aux);
131 return &SMBEnv[i];
134 free(aux);
135 return NULL;
138 static int FlushWriteSMBCache(char *name)
140 smb_env *env;
141 env=FindSMBEnv(name);
142 if(env==NULL) return -1;
144 if (env->SMBWriteCache.file == NULL || env->SMBWriteCache.len == 0)
146 return 0;
149 int ret;
150 int written = 0;
152 while(env->SMBWriteCache.len > 0)
154 ret = SMB_WriteFile(env->SMBWriteCache.ptr+written, env->SMBWriteCache.len,
155 env->SMBWriteCache.file->offset, env->SMBWriteCache.file->handle);
157 if (ret <= 0)
158 return -1;
160 written += ret;
161 env->SMBWriteCache.file->offset += ret;
162 if (env->SMBWriteCache.file->offset > env->SMBWriteCache.file->len)
163 env->SMBWriteCache.file->len = env->SMBWriteCache.file->offset;
165 env->SMBWriteCache.len-=ret;
166 if(env->SMBWriteCache.len==0) break;
168 env->SMBWriteCache.used = 0;
169 env->SMBWriteCache.file = NULL;
171 return 0;
174 static void DestroySMBReadAheadCache(const char *name)
176 smb_env *env;
177 env=FindSMBEnv(name);
178 if(env==NULL) return ;
180 int i;
181 if (env->SMBReadAheadCache != NULL)
183 for (i = 0; i < env->SMB_RA_pages; i++)
185 if(env->SMBReadAheadCache[i].ptr)
186 free(env->SMBReadAheadCache[i].ptr);
188 free(env->SMBReadAheadCache);
189 env->SMBReadAheadCache = NULL;
190 env->SMB_RA_pages = 0;
192 FlushWriteSMBCache(env->name);
194 if(env->SMBWriteCache.ptr)
195 free(env->SMBWriteCache.ptr);
197 env->SMBWriteCache.used = 0;
198 env->SMBWriteCache.len = 0;
199 env->SMBWriteCache.file = NULL;
200 env->SMBWriteCache.ptr = NULL;
203 static void *process_cache_thread(void *ptr)
205 int i;
206 while (1)
208 for(i=0;i<MAX_SMB_MOUNTED ;i++)
210 if(SMBEnv[i].SMBCONNECTED)
212 if (SMBEnv[i].SMBWriteCache.used > 0)
214 if (ticks_to_millisecs(gettime())-ticks_to_millisecs(SMBEnv[i].SMBWriteCache.used) > 500)
216 _SMB_lock(i);
217 FlushWriteSMBCache(SMBEnv[i].name);
218 _SMB_unlock(i);
223 usleep(10000);
225 return NULL;
228 static void SMBEnableReadAhead(const char *name, u32 pages)
230 s32 i, j;
232 smb_env *env;
233 env=FindSMBEnv(name);
234 if(env==NULL) return;
236 DestroySMBReadAheadCache(name);
238 if (pages == 0)
239 return;
241 //only 1 page for write
242 env->SMBWriteCache.ptr = memalign(32, SMB_WRITE_BUFFERSIZE);
243 env->SMBWriteCache.used = 0;
244 env->SMBWriteCache.len = 0;
245 env->SMBWriteCache.file = NULL;
247 env->SMB_RA_pages = pages;
248 env->SMBReadAheadCache = (smb_cache_page *) malloc(sizeof(smb_cache_page) * env->SMB_RA_pages);
249 if (env->SMBReadAheadCache == NULL)
250 return;
251 for (i = 0; i < env->SMB_RA_pages; i++)
253 env->SMBReadAheadCache[i].offset = 0;
254 env->SMBReadAheadCache[i].last_used = 0;
255 env->SMBReadAheadCache[i].file = NULL;
256 env->SMBReadAheadCache[i].ptr = memalign(32, SMB_READ_BUFFERSIZE);
257 if (env->SMBReadAheadCache[i].ptr == NULL)
259 for (j = i - 1; j >= 0; j--)
260 if (env->SMBReadAheadCache[j].ptr)
261 free(env->SMBReadAheadCache[j].ptr);
262 free(env->SMBReadAheadCache);
263 env->SMBReadAheadCache = NULL;
264 free(env->SMBWriteCache.ptr);
265 return;
267 memset(env->SMBReadAheadCache[i].ptr, 0, SMB_READ_BUFFERSIZE);
271 // clear cache from file
272 // called when file is closed
273 static void ClearSMBFileCache(SMBFILESTRUCT *file)
275 int i,j;
276 j=file->env;
278 for (i = 0; i < SMBEnv[j].SMB_RA_pages; i++)
280 if (SMBEnv[j].SMBReadAheadCache[i].file == file)
282 SMBEnv[j].SMBReadAheadCache[i].offset = 0;
283 SMBEnv[j].SMBReadAheadCache[i].last_used = 0;
284 SMBEnv[j].SMBReadAheadCache[i].file = NULL;
285 memset(SMBEnv[j].SMBReadAheadCache[i].ptr, 0, SMB_READ_BUFFERSIZE);
290 static int ReadSMBFromCache(void *buf, size_t len, SMBFILESTRUCT *file)
292 int i,j, leastUsed;
293 off_t new_offset, rest;
294 j=file->env;
296 if ( len == 0 ) return 0;
298 if (SMBEnv[j].SMBReadAheadCache == NULL)
300 if (SMB_ReadFile(buf, len, file->offset, file->handle) <= 0)
302 return -1;
304 return 0;
307 new_offset = file->offset;
308 rest = len;
310 continue_read:
312 for (i = 0; i < SMBEnv[j].SMB_RA_pages; i++)
314 if (SMBEnv[j].SMBReadAheadCache[i].file == file)
316 if ((new_offset >= SMBEnv[j].SMBReadAheadCache[i].offset) &&
317 (new_offset < (SMBEnv[j].SMBReadAheadCache[i].offset + SMB_READ_BUFFERSIZE)))
319 //we hit the page
320 //copy as much as we can
321 SMBEnv[j].SMBReadAheadCache[i].last_used = gettime();
323 off_t buffer_used = (SMBEnv[j].SMBReadAheadCache[i].offset + SMB_READ_BUFFERSIZE) - new_offset;
324 if (buffer_used > rest) buffer_used = rest;
325 memcpy(buf, SMBEnv[j].SMBReadAheadCache[i].ptr + (new_offset - SMBEnv[j].SMBReadAheadCache[i].offset), buffer_used);
326 buf += buffer_used;
327 rest -= buffer_used;
328 new_offset += buffer_used;
330 if (rest == 0)
332 return 0;
334 goto continue_read;
339 leastUsed = 0;
340 for ( i = 1; i < SMBEnv[j].SMB_RA_pages; i++)
342 if ((SMBEnv[j].SMBReadAheadCache[i].last_used < SMBEnv[j].SMBReadAheadCache[leastUsed].last_used))
343 leastUsed = i;
346 //fill least used page with new data
348 off_t cache_offset = new_offset;
350 //do not interset with existing pages
351 for (i = 0; i < SMBEnv[j].SMB_RA_pages; i++)
353 if ( i == leastUsed ) continue;
354 if ( SMBEnv[j].SMBReadAheadCache[i].file != file ) continue;
356 if ( (cache_offset < SMBEnv[j].SMBReadAheadCache[i].offset ) && (cache_offset + SMB_READ_BUFFERSIZE > SMBEnv[j].SMBReadAheadCache[i].offset) )
358 //tail of new page intersects with some cache block, clear page
359 SMBEnv[j].SMBReadAheadCache[i].file = NULL;
362 if ( (cache_offset >= SMBEnv[j].SMBReadAheadCache[i].offset ) && (cache_offset < SMBEnv[j].SMBReadAheadCache[i].offset + SMB_READ_BUFFERSIZE ) )
364 //head of new page intersects with some cache block, clear page
365 SMBEnv[j].SMBReadAheadCache[i].file = NULL;
369 off_t cache_to_read = file->len - cache_offset;
370 if ( cache_to_read > SMB_READ_BUFFERSIZE )
372 cache_to_read = SMB_READ_BUFFERSIZE;
375 int read=0, readed;
376 while(read<cache_to_read)
378 readed = SMB_ReadFile(SMBEnv[j].SMBReadAheadCache[leastUsed].ptr+read, cache_to_read-read, cache_offset+read, file->handle);
379 if ( readed <=0 )
381 SMBEnv[j].SMBReadAheadCache[leastUsed].file = NULL;
382 return -1;
384 read += readed;
386 SMBEnv[j].SMBReadAheadCache[leastUsed].last_used = gettime();
388 SMBEnv[j].SMBReadAheadCache[leastUsed].offset = cache_offset;
389 SMBEnv[j].SMBReadAheadCache[leastUsed].file = file;
391 goto continue_read;
394 static int WriteSMBUsingCache(const char *buf, size_t len, SMBFILESTRUCT *file)
396 size_t size=len;
397 if (file == NULL || buf == NULL)
398 return -1;
399 int j;
400 j=file->env;
401 if (SMBEnv[j].SMBWriteCache.file != NULL)
403 if (strcmp(SMBEnv[j].SMBWriteCache.file->filename, file->filename) != 0)
405 //Flush current buffer
406 if (FlushWriteSMBCache(SMBEnv[j].name) < 0)
408 goto failed;
410 SMBEnv[j].SMBWriteCache.file = file;
411 SMBEnv[j].SMBWriteCache.len = 0;
414 else
416 SMBEnv[j].SMBWriteCache.file = file;
417 SMBEnv[j].SMBWriteCache.len = 0;
419 int rest;
420 s32 written;
421 while(len > 0)
423 if(SMBEnv[j].SMBWriteCache.len+len >= SMB_WRITE_BUFFERSIZE)
425 rest = SMB_WRITE_BUFFERSIZE - SMBEnv[j].SMBWriteCache.len;
426 memcpy(SMBEnv[j].SMBWriteCache.ptr + SMBEnv[j].SMBWriteCache.len, buf, rest);
428 written = SMB_WriteFile(SMBEnv[j].SMBWriteCache.ptr, SMB_WRITE_BUFFERSIZE, file->offset, file->handle);
429 if(written<0)
431 goto failed;
433 file->offset += written;
434 if (file->offset > file->len)
435 file->len = file->offset;
437 buf += rest;
438 len -= rest;
439 SMBEnv[j].SMBWriteCache.used = gettime();
440 SMBEnv[j].SMBWriteCache.len = 0;
442 else
444 memcpy(SMBEnv[j].SMBWriteCache.ptr + SMBEnv[j].SMBWriteCache.len, buf, len);
445 SMBEnv[j].SMBWriteCache.len += len;
446 SMBEnv[j].SMBWriteCache.used = gettime();
447 break;
450 return size;
452 failed:
453 return -1;
456 ///////////////////////////////////////////
457 // END CACHE FUNCTIONS //
458 ///////////////////////////////////////////
460 static char *smb_absolute_path_no_device(const char *srcpath, char *destpath, int env)
462 char temp[SMB_MAXPATH];
463 int i=0,j=0;
465 if (strchr(srcpath, ':') != NULL)
467 srcpath = strchr(srcpath, ':') + 1;
469 if (strchr(srcpath, ':') != NULL)
471 return NULL;
474 memset(temp,0,SMB_MAXPATH);
476 if (srcpath[0] != '\\' && srcpath[0] != '/')
478 strcpy(temp, SMBEnv[env].currentpath);
480 if(srcpath[0]!='\0') //strlen(srcpath) > 0
482 if(srcpath[0]=='.' && (srcpath[1]=='\\' || srcpath[1]=='\0')) // to fix opendir(".") or chdir(".")
483 strcat(temp, &srcpath[1]);
484 else
485 strcat(temp, srcpath);
488 while(temp[i]!='\0' && i < SMB_MAXPATH)
490 if(temp[i]=='/')
492 destpath[j++]='\\';
493 while(temp[i]!='\0' && i < SMB_MAXPATH && (temp[i]=='/' || temp[i]=='\\'))i++;
495 else if(srcpath[i]=='\\')
497 destpath[j++]=temp[i++];
498 while(temp[i]!='\0' && i < SMB_MAXPATH && (temp[i]=='/' || temp[i]=='\\'))i++;
500 else
502 destpath[j++]=temp[i++];
505 destpath[j]='\0';
507 return destpath;
510 static char *ExtractDevice(const char *path, char *device)
512 int i,l;
513 l=strlen(path);
515 for(i=0;i<l && path[i]!='\0' && path[i]!=':' && i < 20;i++)
516 device[i]=path[i];
517 if(path[i]!=':')device[0]='\0';
518 else device[i]='\0';
519 return device;
522 //FILE IO
523 static int __smb_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode)
525 SMBFILESTRUCT *file = (SMBFILESTRUCT*) fileStruct;
526 char fixedpath[SMB_MAXPATH];
527 smb_env *env;
529 ExtractDevice(path,fixedpath);
530 if(fixedpath[0]=='\0')
532 getcwd(fixedpath,SMB_MAXPATH);
533 ExtractDevice(fixedpath,fixedpath);
535 env=FindSMBEnv(fixedpath);
536 if(env==NULL)
538 r->_errno = ENODEV;
539 return -1;
541 file->env=env->pos;
543 if (!env->SMBCONNECTED)
545 r->_errno = ENODEV;
546 return -1;
549 if (smb_absolute_path_no_device(path, fixedpath, file->env) == NULL)
551 r->_errno = EINVAL;
552 return -1;
555 if(!smbCheckConnection(env->name))
557 r->_errno = ENODEV;
558 return -1;
561 SMBDIRENTRY dentry;
562 bool fileExists = true;
563 _SMB_lock(env->pos);
564 if (SMB_PathInfo(fixedpath, &dentry, env->smbconn) != SMB_SUCCESS)
565 fileExists = false;
568 // Determine which mode the file is open for
569 u8 smb_mode;
570 unsigned short access;
571 if ((flags & 0x03) == O_RDONLY)
573 // Open the file for read-only access
574 smb_mode = SMB_OF_OPEN;
575 access = SMB_OPEN_READING;
577 else if ((flags & 0x03) == O_WRONLY)
579 // Open file for write only access
580 if (fileExists)
581 smb_mode = SMB_OF_OPEN;
582 else
583 smb_mode = SMB_OF_CREATE;
584 access = SMB_OPEN_WRITING;
586 else if ((flags & 0x03) == O_RDWR)
588 // Open file for read/write access
589 access = SMB_OPEN_READWRITE;
590 if (fileExists)
591 smb_mode = SMB_OF_OPEN;
592 else
593 smb_mode = SMB_OF_CREATE;
595 else
597 r->_errno = EACCES;
598 _SMB_unlock(env->pos);
599 return -1;
602 if ((flags & O_CREAT) && !fileExists)
603 smb_mode = SMB_OF_CREATE;
604 if (!(flags & O_APPEND) && fileExists && ((flags & 0x03) != O_RDONLY))
605 smb_mode = SMB_OF_TRUNCATE;
606 file->handle = SMB_OpenFile(fixedpath, access, smb_mode, env->smbconn);
607 if (!file->handle)
609 r->_errno = ENOENT;
610 _SMB_unlock(env->pos);
611 return -1;
614 file->len = 0;
615 file->attributes = 0;
616 if (fileExists)
618 file->len = dentry.size;
619 file->attributes = dentry.attributes;
622 if (flags & O_APPEND)
623 file->offset = file->len;
624 else
625 file->offset = 0;
627 file->access=access;
629 strcpy(file->filename, fixedpath);
630 _SMB_unlock(env->pos);
631 return 0;
634 static off_t __smb_seek(struct _reent *r, int fd, off_t pos, int dir)
636 SMBFILESTRUCT *file = (SMBFILESTRUCT*) fd;
637 off_t position;
639 if (file == NULL)
641 r->_errno = EBADF;
642 return -1;
645 //have to flush because SMBWriteCache.file->offset holds offset of cached block not yet written
646 _SMB_lock(file->env);
647 if (SMBEnv[file->env].SMBWriteCache.file == file)
649 FlushWriteSMBCache(SMBEnv[file->env].name);
652 switch (dir)
654 case SEEK_SET:
655 position = pos;
656 break;
657 case SEEK_CUR:
658 position = file->offset + pos;
659 break;
660 case SEEK_END:
661 position = file->len + pos;
662 break;
663 default:
664 r->_errno = EINVAL;
665 _SMB_unlock(file->env);
666 return -1;
669 if (pos > 0 && position < 0)
671 r->_errno = EOVERFLOW;
672 _SMB_unlock(file->env);
673 return -1;
675 if (position < 0)
677 r->_errno = EINVAL;
678 _SMB_unlock(file->env);
679 return -1;
682 // Save position
683 file->offset = position;
684 _SMB_unlock(file->env);
685 return position;
688 static ssize_t __smb_read(struct _reent *r, int fd, char *ptr, size_t len)
690 size_t offset = 0;
691 size_t readsize;
692 int ret = 0;
693 int cnt_retry=2;
694 SMBFILESTRUCT *file = (SMBFILESTRUCT*) fd;
696 if (file == NULL)
698 r->_errno = EBADF;
699 return -1;
702 if (len <= 0)
704 return 0;
706 //have to flush because SMBWriteCache.file->offset holds offset of cached block not yet writeln
707 //and file->len also may not have been updated yet
708 _SMB_lock(file->env);
709 if (SMBEnv[file->env].SMBWriteCache.file == file)
711 FlushWriteSMBCache(SMBEnv[file->env].name);
714 // Don't try to read if the read pointer is past the end of file
715 if (file->offset >= file->len)
717 r->_errno = EOVERFLOW;
718 _SMB_unlock(file->env);
719 return 0;
722 // Don't read past end of file
723 if (len + file->offset > file->len)
725 r->_errno = EOVERFLOW;
726 len = file->len - file->offset;
729 // Short circuit cases where len is 0 (or less)
730 if (len <= 0)
732 _SMB_unlock(file->env);
733 return 0;
735 retry_read:
736 while(offset < len)
738 readsize = len - offset;
739 if(readsize > SMB_READ_BUFFERSIZE) readsize = SMB_READ_BUFFERSIZE;
740 ret = ReadSMBFromCache(ptr+offset, readsize, file);
741 if(ret < 0) break;
742 offset += readsize;
743 file->offset += readsize;
746 if (ret < 0)
748 retry_reconnect:
749 cnt_retry--;
750 if(cnt_retry>=0)
752 if(smbCheckConnection(SMBEnv[file->env].name))
754 ClearSMBFileCache(file);
755 file->handle = SMB_OpenFile(file->filename, file->access, SMB_OF_OPEN, SMBEnv[file->env].smbconn);
756 if (!file->handle)
758 r->_errno = ENOENT;
759 _SMB_unlock(file->env);
760 return -1;
762 goto retry_read;
764 usleep(10000);
765 goto retry_reconnect;
767 r->_errno = EIO;
768 _SMB_unlock(file->env);
769 return -1;
771 _SMB_unlock(file->env);
772 return len;
775 static ssize_t __smb_write(struct _reent *r, int fd, const char *ptr, size_t len)
777 SMBFILESTRUCT *file = (SMBFILESTRUCT*) fd;
778 int written;
779 if (file == NULL)
781 r->_errno = EBADF;
782 return -1;
785 // Don't try to write if the pointer is past the end of file
786 if (file->offset > file->len)
788 r->_errno = EOVERFLOW;
789 return -1;
792 // Short circuit cases where len is 0 (or less)
793 if (len == 0)
794 return 0;
796 _SMB_lock(file->env);
797 written = WriteSMBUsingCache(ptr, len, file);
798 _SMB_unlock(file->env);
800 if (written <= 0)
802 r->_errno = EIO;
803 return -1;
806 return written;
809 static int __smb_close(struct _reent *r, int fd)
811 SMBFILESTRUCT *file = (SMBFILESTRUCT*) fd;
812 int j;
813 j=file->env;
814 _SMB_lock(j);
815 if (SMBEnv[j].SMBWriteCache.file == file)
817 FlushWriteSMBCache(SMBEnv[j].name);
819 ClearSMBFileCache(file);
820 SMB_CloseFile(file->handle);
821 file->len = 0;
822 file->offset = 0;
823 file->filename[0] = '\0';
824 _SMB_unlock(j);
826 return 0;
829 static int __smb_chdir(struct _reent *r, const char *path)
831 char path_absolute[SMB_MAXPATH];
833 SMBDIRENTRY dentry;
834 int found;
836 ExtractDevice(path,path_absolute);
837 if(path_absolute[0]=='\0')
839 getcwd(path_absolute,SMB_MAXPATH);
840 ExtractDevice(path_absolute,path_absolute);
843 smb_env* env;
844 env=FindSMBEnv(path_absolute);
846 if(env == NULL)
848 r->_errno = ENODEV;
849 return -1;
852 if (!env->SMBCONNECTED)
854 r->_errno = ENODEV;
855 return -1;
858 if (smb_absolute_path_no_device(path, path_absolute,env->pos) == NULL)
860 r->_errno = EINVAL;
861 return -1;
864 if(!smbCheckConnection(env->name))
866 r->_errno = ENODEV;
867 return -1;
870 memset(&dentry, 0, sizeof(SMBDIRENTRY));
872 _SMB_lock(env->pos);
873 found = SMB_PathInfo(path_absolute, &dentry, env->smbconn);
875 if (found != SMB_SUCCESS)
877 r->_errno = ENOENT;
878 _SMB_unlock(env->pos);
879 return -1;
882 if (!(dentry.attributes & SMB_SRCH_DIRECTORY))
884 r->_errno = ENOTDIR;
885 _SMB_unlock(env->pos);
886 return -1;
889 strcpy(env->currentpath, path_absolute);
890 if (env->currentpath[0] != 0)
892 if (env->currentpath[strlen(env->currentpath) - 1] != '\\')
893 strcat(env->currentpath, "\\");
895 _SMB_unlock(env->pos);
896 return 0;
899 static int __smb_dirreset(struct _reent *r, DIR_ITER *dirState)
901 char path_abs[SMB_MAXPATH];
902 SMBDIRSTATESTRUCT* state = (SMBDIRSTATESTRUCT*) (dirState->dirStruct);
903 SMBDIRENTRY dentry;
905 memset(&dentry, 0, sizeof(SMBDIRENTRY));
907 _SMB_lock(state->env);
908 SMB_FindClose(&state->smbdir, SMBEnv[state->env].smbconn);
910 strcpy(path_abs,SMBEnv[state->env].currentpath);
911 strcat(path_abs,"*");
912 int found = SMB_FindFirst(path_abs, smbFlags, &dentry, SMBEnv[state->env].smbconn);
914 if (found != SMB_SUCCESS)
916 r->_errno = ENOENT;
917 _SMB_unlock(state->env);
918 return -1;
921 if (!(dentry.attributes & SMB_SRCH_DIRECTORY))
923 r->_errno = ENOTDIR;
924 _SMB_unlock(state->env);
925 return -1;
928 state->smbdir.size = dentry.size;
929 state->smbdir.ctime = dentry.ctime;
930 state->smbdir.atime = dentry.atime;
931 state->smbdir.mtime = dentry.mtime;
932 state->smbdir.attributes = dentry.attributes;
933 state->smbdir.sid = dentry.sid;
934 strcpy(state->smbdir.name, dentry.name);
936 SMBEnv[state->env].first_item_dir = true;
937 _SMB_unlock(state->env);
938 return 0;
941 static DIR_ITER* __smb_diropen(struct _reent *r, DIR_ITER *dirState, const char *path)
943 char path_absolute[SMB_MAXPATH];
944 int found;
945 SMBDIRSTATESTRUCT* state = (SMBDIRSTATESTRUCT*) (dirState->dirStruct);
946 SMBDIRENTRY dentry;
948 ExtractDevice(path,path_absolute);
949 if(path_absolute[0]=='\0')
951 getcwd(path_absolute,SMB_MAXPATH);
952 ExtractDevice(path_absolute,path_absolute);
955 smb_env* env;
956 env=FindSMBEnv(path_absolute);
958 if(env == NULL)
960 r->_errno = ENODEV;
961 return NULL;
964 if (smb_absolute_path_no_device(path, path_absolute, env->pos) == NULL)
966 r->_errno = EINVAL;
967 return NULL;
969 if (strlen(path_absolute) > 0 && path_absolute[strlen(path_absolute) - 1] != '\\')
970 strcat(path_absolute, "\\");
972 if(!strcmp(path_absolute,"\\"))
973 env->diropen_root=true;
974 else
975 env->diropen_root=false;
977 if(!smbCheckConnection(env->name))
979 r->_errno = ENODEV;
980 return NULL;
983 _SMB_lock(env->pos);
984 if(!env->diropen_root) // root must be valid - we don't need check it
986 memset(&dentry, 0, sizeof(SMBDIRENTRY));
987 found = SMB_PathInfo(path_absolute, &dentry, env->smbconn);
988 if (found != SMB_SUCCESS)
990 r->_errno = ENOENT;
991 _SMB_unlock(env->pos);
992 return NULL;
995 if (!(dentry.attributes & SMB_SRCH_DIRECTORY))
997 r->_errno = ENOTDIR;
998 _SMB_unlock(env->pos);
999 return NULL;
1003 strcpy(state->dir,path_absolute);
1005 strcat(path_absolute, "*");
1006 memset(&dentry, 0, sizeof(SMBDIRENTRY));
1007 found = SMB_FindFirst(path_absolute, smbFlags, &dentry, env->smbconn);
1009 if (found != SMB_SUCCESS)
1011 r->_errno = ENOENT;
1012 _SMB_unlock(env->pos);
1013 return NULL;
1016 state->env=env->pos;
1017 state->smbdir.size = dentry.size;
1018 state->smbdir.ctime = dentry.ctime;
1019 state->smbdir.atime = dentry.atime;
1020 state->smbdir.mtime = dentry.mtime;
1021 state->smbdir.attributes = dentry.attributes;
1022 state->smbdir.sid = dentry.sid;
1023 strcpy(state->smbdir.name, dentry.name);
1024 env->first_item_dir = true;
1025 _SMB_unlock(env->pos);
1026 return dirState;
1029 static int dentry_to_stat(SMBDIRENTRY *dentry, struct stat *st)
1031 if (!st)
1032 return -1;
1033 if (!dentry)
1034 return -1;
1036 st->st_dev = 0;
1037 st->st_ino = 0;
1039 st->st_mode = ((dentry->attributes & SMB_SRCH_DIRECTORY) ? S_IFDIR : S_IFREG);
1040 st->st_nlink = 1;
1041 st->st_uid = 1; // Faked
1042 st->st_rdev = st->st_dev;
1043 st->st_gid = 2; // Faked
1044 st->st_size = dentry->size;
1045 st->st_atime = dentry->atime/10000000.0 - 11644473600LL;
1046 st->st_spare1 = 0;
1047 st->st_mtime = dentry->mtime/10000000.0 - 11644473600LL;
1048 st->st_spare2 = 0;
1049 st->st_ctime = dentry->ctime/10000000.0 - 11644473600LL;
1050 st->st_spare3 = 0;
1051 st->st_blksize = 1024;
1052 st->st_blocks = (st->st_size + st->st_blksize - 1) / st->st_blksize; // File size in blocks
1053 st->st_spare4[0] = 0;
1054 st->st_spare4[1] = 0;
1056 return 0;
1059 static int cpy_dentry(SMBDIRENTRY *dest, SMBDIRENTRY *source)
1061 if (!dest || !source)
1062 return -1;
1064 dest->attributes=source->attributes;
1065 dest->size=source->size;
1066 dest->atime=source->atime;
1067 dest->mtime=source->mtime;
1068 dest->ctime=source->ctime;
1069 strcpy(dest->name,source->name);
1071 return 0;
1074 static int __smb_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename,
1075 struct stat *filestat)
1077 int ret;
1078 SMBDIRSTATESTRUCT* state = (SMBDIRSTATESTRUCT*) (dirState->dirStruct);
1079 SMBDIRENTRY dentry;
1081 if (SMBEnv[state->env].currentpath[0] == '\0' || filestat == NULL)
1083 r->_errno = ENOENT;
1084 return -1;
1087 memset(&dentry, 0, sizeof(SMBDIRENTRY));
1088 _SMB_lock(state->env);
1089 if (SMBEnv[state->env].first_item_dir)
1091 SMBEnv[state->env].first_item_dir = false;
1092 dentry.size = state->smbdir.size;
1093 dentry.ctime = state->smbdir.ctime;
1094 dentry.atime = state->smbdir.atime;
1095 dentry.mtime = state->smbdir.mtime;
1096 dentry.attributes = state->smbdir.attributes;
1097 strcpy(dentry.name, state->smbdir.name);
1098 strcpy(filename, dentry.name);
1099 dentry_to_stat(&dentry, filestat);
1100 cpy_dentry(&last_dentry,&dentry);
1101 last_env=state->env;
1102 strcpy(last_path,state->dir);
1103 _SMB_unlock(state->env);
1104 return 0;
1107 dentry.sid = state->smbdir.sid;
1109 ret = SMB_FindNext(&dentry, SMBEnv[state->env].smbconn);
1110 if(ret==SMB_SUCCESS && SMBEnv[state->env].diropen_root)
1112 if(strlen(dentry.name) == 2 && strcmp(dentry.name,"..") == 0)
1113 ret = SMB_FindNext(&dentry, SMBEnv[state->env].smbconn);
1115 if (ret == SMB_SUCCESS)
1117 state->smbdir.size = dentry.size;
1118 state->smbdir.ctime = dentry.ctime;
1119 state->smbdir.atime = dentry.atime;
1120 state->smbdir.mtime = dentry.mtime;
1121 state->smbdir.attributes = dentry.attributes;
1122 strcpy(state->smbdir.name, dentry.name);
1123 strcpy(filename, dentry.name);
1125 else
1127 r->_errno = ENOENT;
1128 _SMB_unlock(state->env);
1129 return -1;
1132 dentry_to_stat(&dentry, filestat);
1133 cpy_dentry(&last_dentry,&dentry);
1134 last_env=state->env;
1135 strcpy(last_path,state->dir);
1136 _SMB_unlock(state->env);
1137 return 0;
1140 static int __smb_dirclose(struct _reent *r, DIR_ITER *dirState)
1142 SMBDIRSTATESTRUCT* state = (SMBDIRSTATESTRUCT*) (dirState->dirStruct);
1144 int j = state->env;
1145 _SMB_lock(j);
1146 SMB_FindClose(&state->smbdir, SMBEnv[j].smbconn);
1147 memset(state, 0, sizeof(SMBDIRSTATESTRUCT));
1148 _SMB_unlock(j);
1149 return 0;
1152 static int __smb_stat(struct _reent *r, const char *path, struct stat *st)
1154 char path_absolute[SMB_MAXPATH];
1155 SMBDIRENTRY dentry;
1157 if(path == NULL)
1159 r->_errno = ENODEV;
1160 return -1;
1163 if(strcmp(path,".")==0 || strcmp(path,"..")==0)
1165 memset(st,0,sizeof(struct stat));
1166 st->st_mode = S_IFDIR;
1167 return 0;
1170 ExtractDevice(path,path_absolute);
1171 if(path_absolute[0]=='\0')
1173 getcwd(path_absolute,SMB_MAXPATH);
1174 ExtractDevice(path_absolute,path_absolute);
1177 smb_env* env;
1178 env=FindSMBEnv(path_absolute);
1180 if(env == NULL)
1182 r->_errno = ENODEV;
1183 return -1;
1186 if (smb_absolute_path_no_device(path, path_absolute, env->pos) == NULL)
1188 r->_errno = EINVAL;
1189 return -1;
1192 if(env->pos==last_env) //optimization, usually after a dirnext we do stat
1194 char file[SMB_MAXPATH];
1195 strcpy(file,last_path);
1196 strcat(file,last_dentry.name);
1197 if(strcmp(file,path_absolute)==0)
1199 dentry_to_stat(&last_dentry, st);
1200 return 0;
1204 _SMB_lock(env->pos);
1205 if (SMB_PathInfo(path_absolute, &dentry, env->smbconn) != SMB_SUCCESS)
1207 r->_errno = ENOENT;
1208 _SMB_unlock(env->pos);
1209 return -1;
1212 if (dentry.name[0] == '\0')
1215 r->_errno = ENOENT;
1216 _SMB_unlock(env->pos);
1217 return -1;
1220 dentry_to_stat(&dentry, st);
1221 _SMB_unlock(env->pos);
1223 return 0;
1226 static int __smb_fstat(struct _reent *r, int fd, struct stat *st)
1228 SMBFILESTRUCT *filestate = (SMBFILESTRUCT *) fd;
1230 if (!filestate)
1232 r->_errno = EBADF;
1233 return -1;
1236 st->st_size = filestate->len;
1237 st->st_mode = ((filestate->attributes & SMB_SRCH_DIRECTORY) ? S_IFDIR : S_IFREG);
1239 return 0;
1242 static int __smb_mkdir(struct _reent *r, const char *name, int mode)
1244 char fixedName[SMB_MAXPATH];
1245 smb_env *env;
1247 ExtractDevice(name,fixedName);
1248 if(fixedName[0]=='\0')
1250 getcwd(fixedName,SMB_MAXPATH);
1251 ExtractDevice(fixedName,fixedName);
1254 env=FindSMBEnv(fixedName);
1255 if(env==NULL)
1257 r->_errno = ENODEV;
1258 return -1;
1261 if (smb_absolute_path_no_device(name, fixedName, env->pos) == NULL)
1263 r->_errno = EINVAL;
1264 return -1;
1267 s32 ret = 0;
1268 _SMB_lock(env->pos);
1269 if(SMB_CreateDirectory(fixedName, env->smbconn) != SMB_SUCCESS)
1270 ret = -1;
1271 _SMB_unlock(env->pos);
1273 return ret;
1276 static int __smb_unlink(struct _reent *r, const char *name)
1278 char fixedName[SMB_MAXPATH];
1279 smb_env *env;
1280 bool isDir = false;
1282 DIR *dir = NULL;
1283 dir = opendir(name);
1284 if(dir)
1286 closedir(dir);
1287 isDir = true;
1290 ExtractDevice(name,fixedName);
1291 if(fixedName[0]=='\0')
1293 getcwd(fixedName,SMB_MAXPATH);
1294 ExtractDevice(fixedName,fixedName);
1297 env=FindSMBEnv(fixedName);
1298 if(env==NULL)
1300 r->_errno = ENODEV;
1301 return -1;
1304 if (smb_absolute_path_no_device(name, fixedName, env->pos) == NULL)
1306 r->_errno = EINVAL;
1307 return -1;
1310 s32 ret = 0;
1311 _SMB_lock(env->pos);
1312 if(isDir)
1313 ret = SMB_DeleteDirectory(fixedName, env->smbconn);
1314 else
1315 ret = SMB_DeleteFile(fixedName, env->smbconn);
1316 _SMB_unlock(env->pos);
1318 if(ret != SMB_SUCCESS)
1319 return -1;
1321 return 0;
1324 static int __smb_rename(struct _reent *r, const char *oldName, const char *newName)
1326 char fixedOldName[SMB_MAXPATH];
1327 char fixedNewName[SMB_MAXPATH];
1328 smb_env *env;
1330 ExtractDevice(oldName,fixedOldName);
1331 if(fixedOldName[0]=='\0')
1333 getcwd(fixedOldName,SMB_MAXPATH);
1334 ExtractDevice(fixedOldName,fixedOldName);
1337 env=FindSMBEnv(fixedOldName);
1338 if(env==NULL)
1340 r->_errno = ENODEV;
1341 return -1;
1344 if (smb_absolute_path_no_device(oldName, fixedOldName, env->pos) == NULL)
1346 r->_errno = EINVAL;
1347 return -1;
1350 ExtractDevice(newName,fixedNewName);
1351 if(fixedNewName[0]=='\0')
1353 getcwd(fixedNewName,SMB_MAXPATH);
1354 ExtractDevice(fixedNewName,fixedNewName);
1357 if (smb_absolute_path_no_device(newName, fixedNewName, env->pos) == NULL)
1359 r->_errno = EINVAL;
1360 return -1;
1363 s32 ret = 0;
1364 _SMB_lock(env->pos);
1365 if (SMB_Rename(fixedOldName, fixedNewName, env->smbconn) != SMB_SUCCESS)
1366 ret = -1;
1367 _SMB_unlock(env->pos);
1369 return ret;
1372 static int __smb_statvfs_r(struct _reent *r, const char *name, struct statvfs *buf)
1374 char fixedName[SMB_MAXPATH];
1375 smb_env *env;
1377 ExtractDevice(name,fixedName);
1378 if(fixedName[0]=='\0')
1380 getcwd(fixedName,SMB_MAXPATH);
1381 ExtractDevice(fixedName,fixedName);
1384 env=FindSMBEnv(fixedName);
1385 if(env==NULL)
1387 r->_errno = ENODEV;
1388 return -1;
1391 if (smb_absolute_path_no_device(name, fixedName, env->pos) == NULL)
1393 r->_errno = EINVAL;
1394 return -1;
1397 s32 ret = 0;
1398 _SMB_lock(env->pos);
1399 if(SMB_DiskInformation(buf, env->smbconn) != SMB_SUCCESS)
1400 ret = -1;
1401 _SMB_unlock(env->pos);
1403 return ret;
1406 static void MountDevice(const char *name,SMBCONN smbconn, int env)
1408 devoptab_t *dotab_smb;
1409 char *aux;
1410 int l;
1412 aux=strdup(name);
1413 l=strlen(aux);
1414 if(aux[l-1]==':')aux[l-1]='\0';
1416 dotab_smb=(devoptab_t*)malloc(sizeof(devoptab_t));
1418 dotab_smb->name=strdup(aux);
1419 dotab_smb->structSize=sizeof(SMBFILESTRUCT); // size of file structure
1420 dotab_smb->open_r=__smb_open; // device open
1421 dotab_smb->close_r=__smb_close; // device close
1422 dotab_smb->write_r=__smb_write; // device write
1423 dotab_smb->read_r=__smb_read; // device read
1424 dotab_smb->seek_r=__smb_seek; // device seek
1425 dotab_smb->fstat_r=__smb_fstat; // device fstat
1426 dotab_smb->stat_r=__smb_stat; // device stat
1427 dotab_smb->link_r=NULL; // device link
1428 dotab_smb->unlink_r=__smb_unlink; // device unlink
1429 dotab_smb->chdir_r=__smb_chdir; // device chdir
1430 dotab_smb->rename_r=__smb_rename; // device rename
1431 dotab_smb->mkdir_r=__smb_mkdir; // device mkdir
1433 dotab_smb->dirStateSize=sizeof(SMBDIRSTATESTRUCT); // dirStateSize
1434 dotab_smb->diropen_r=__smb_diropen; // device diropen_r
1435 dotab_smb->dirreset_r=__smb_dirreset; // device dirreset_r
1436 dotab_smb->dirnext_r=__smb_dirnext; // device dirnext_r
1437 dotab_smb->dirclose_r=__smb_dirclose; // device dirclose_r
1438 dotab_smb->statvfs_r=__smb_statvfs_r; // device statvfs_r
1439 dotab_smb->ftruncate_r=NULL; // device ftruncate_r
1440 dotab_smb->fsync_r=NULL; // device fsync_r
1441 dotab_smb->deviceData=NULL; /* Device data */
1443 AddDevice(dotab_smb);
1445 SMBEnv[env].pos=env;
1446 SMBEnv[env].smbconn=smbconn;
1447 SMBEnv[env].first_item_dir=false;
1448 SMBEnv[env].diropen_root=false;
1449 SMBEnv[env].devoptab=dotab_smb;
1450 SMBEnv[env].SMBCONNECTED=true;
1451 SMBEnv[env].name=strdup(aux);
1453 SMBEnableReadAhead(aux,8);
1455 free(aux);
1458 bool smbInitDevice(const char* name, const char *user, const char *password, const char *share, const char *ip)
1460 int i;
1462 if(!name || strlen(name) > 9)
1463 return false;
1465 char devname[10];
1466 sprintf(devname, "%s:", name);
1467 if(FindDevice(devname) >= 0)
1468 return false;
1470 if(!smbInited)
1472 for(i=0;i<MAX_SMB_MOUNTED;i++)
1474 SMBEnv[i].SMBCONNECTED=false;
1475 SMBEnv[i].currentpath[0]='\\';
1476 SMBEnv[i].currentpath[1]='\0';
1477 SMBEnv[i].first_item_dir=false;
1478 SMBEnv[i].pos=i;
1479 SMBEnv[i].SMBReadAheadCache=NULL;
1480 LWP_MutexInit(&SMBEnv[i]._SMB_mutex, false);
1483 if(cache_thread == LWP_THREAD_NULL)
1484 if(LWP_CreateThread(&cache_thread, process_cache_thread, NULL, NULL, 0, 64) != 0)
1485 return false;
1487 smbInited = true;
1490 //root connect
1491 bool ret = true;
1492 SMBCONN smbconn;
1493 if(SMB_Connect(&smbconn, user, password, share, ip) != SMB_SUCCESS)
1494 return false;
1496 for(i=0;i<MAX_SMB_MOUNTED && SMBEnv[i].SMBCONNECTED;i++);
1498 if(i==MAX_SMB_MOUNTED)
1500 SMB_Close(smbconn);
1501 return false; // all samba connections in use
1504 SMBEnv[i].SMBCONNECTED=true; // reserved
1505 MountDevice(name,smbconn,i);
1506 return ret;
1509 bool smbInit(const char *user, const char *password, const char *share, const char *ip)
1511 return smbInitDevice("smb", user, password, share, ip);
1514 void smbClose(const char* name)
1516 smb_env *env = FindSMBEnv(name);
1517 if(env==NULL) return;
1519 _SMB_lock(env->pos);
1520 if(env->SMBCONNECTED)
1521 SMB_Close(env->smbconn);
1523 char device[11];
1524 sprintf(device, "%s:", env->name);
1525 RemoveDevice(device);
1526 env->SMBCONNECTED=false;
1527 _SMB_unlock(env->pos);
1530 bool smbCheckConnection(const char* name)
1532 char device[10];
1533 int i;
1534 bool ret;
1535 smb_env *env;
1537 for(i=0; i < 10 && name[i]!='\0' && name[i]!=':'; i++) device[i]=name[i];
1538 device[i]='\0';
1540 env=FindSMBEnv(device);
1541 if(env==NULL) return false;
1542 _SMB_lock(env->pos);
1543 ret=(SMB_Reconnect(&env->smbconn,true)==SMB_SUCCESS);
1544 _SMB_unlock(env->pos);
1545 return ret;
1548 void smbSetSearchFlags(unsigned short flags)
1550 smbFlags = flags;