http://prdownloads.sourceforge.net/lufs/lufs-0.9.7.tar.gz?download
[lufs.git] / filesystems / cefs / cefs.cpp
1 /*  Remote Windows CE device filesystem.
2
3     Based on lufs "Userland filesystem" ( http://lufs.sourceforge.net/ )
4     and synce ( http://synce.sourceforge.net/ )
5     projects.
6
7     Author: Fedor Bezrukov <fedor@ms2.inr.ac.ru>
8  */
9
10 #include <unistd.h>
11 #include <dirent.h>
12 #include <fcntl.h>
13 #include <stdio.h>
14 #include <utime.h>
15
16 #include <sys/types.h>
17 #include <sys/stat.h>
18
19 #include <vector>
20 #include <string>
21
22 #include <lufs/proto.h>
23 #include <lufs/fs.h>
24
25 #include "cefs.h"
26
27 #include <rapi.h>
28 #include <iconv.h>
29 #include <sys/mman.h>
30 #include <errno.h>
31
32 extern "C" {
33
34 void*
35 cefs_init(struct list_head *cfg, struct dir_cache *cache, struct credentials *cred, void **global_ctx){
36     return (void*)new CEFS(cfg, cache);
37 }
38
39 void
40 cefs_free(void *ctx){
41     CEFS *p = (CEFS*)ctx;
42
43     delete p;
44 }
45
46 int     
47 cefs_mount(void *ctx){
48     return ((CEFS*)ctx)->do_mount();
49 }
50
51 void    
52 cefs_umount(void *ctx){
53 //    return ((CEFS*)ctx)->do_umount();
54 }
55
56 int     
57 cefs_readdir(void *ctx, char *dir_name, struct directory *dir){
58     return ((CEFS*)ctx)->do_readdir(dir_name, dir);
59 }
60
61 int     
62 cefs_stat(void *ctx, char *name, struct lufs_fattr *fattr){
63     return ((CEFS*)ctx)->do_stat(name, fattr);
64 }
65
66 int     
67 cefs_mkdir(void *ctx, char *dir, int mode){
68     return ((CEFS*)ctx)->do_mkdir(dir, mode);
69 }
70
71 int     
72 cefs_rmdir(void *ctx, char *dir){
73     return ((CEFS*)ctx)->do_rmdir(dir);
74 }
75
76 int     
77 cefs_create(void *ctx, char *file, int mode){
78     return ((CEFS*)ctx)->do_create(file, mode);
79 }
80
81 int     
82 cefs_unlink(void *ctx, char *file){
83     return ((CEFS*)ctx)->do_unlink(file);
84 }
85
86 int     
87 cefs_rename(void *ctx, char *old_name, char *new_name){
88     return ((CEFS*)ctx)->do_rename(old_name, new_name);
89 }
90
91 int     
92 cefs_open(void *ctx, char *file, unsigned mode){
93     return ((CEFS*)ctx)->do_open(file, mode);
94 }
95
96 int     
97 cefs_release(void *ctx, char *file){
98     return ((CEFS*)ctx)->do_release(file);
99 }
100
101 int     
102 cefs_read(void *ctx, char *file, long long offset, unsigned long count, char *buf){
103     return ((CEFS*)ctx)->do_read(file, offset, count, buf);
104 }
105
106 int     
107 cefs_write(void *ctx, char *file, long long offset, unsigned long count, char *buf){
108     return ((CEFS*)ctx)->do_write(file, offset, count, buf);
109 }
110
111 int     
112 cefs_readlink(void *ctx, char *link, char *buf, int buflen){
113     return ((CEFS*)ctx)->do_readlink(link, buf, buflen);
114 }
115
116 int     
117 cefs_link(void *ctx, char *target, char *link){
118     return ((CEFS*)ctx)->do_link(target, link);
119 }
120
121 int     
122 cefs_symlink(void *ctx, char *target, char *link){
123     return ((CEFS*)ctx)->do_symlink(target, link);
124 }
125
126 int     
127 cefs_setattr(void *ctx, char *file, struct lufs_fattr *fattr){
128     return ((CEFS*)ctx)->do_setattr(file, fattr);
129 }
130
131 } /* extern "C" */
132
133
134 /*
135  * It controls whether a new Rapi connection is initialized for every
136  * function or only once per process.  I am not sure at the moment,
137  * what is better.
138  */
139 #define CERAPIINIT_PER_PROCESS
140
141 // WinCE Send buffer size
142 //#define MYBUF_SIZE (16*1024)
143 #define MYBUF_SIZE (64*1024)
144
145 //
146 // Return values from main()
147 //
148 #define TEST_SUCCEEDED 0
149 #define TEST_FAILED -1
150 //
151 // HRESULT tests
152 // 
153 #define VERIFY_HRESULT(call) \
154 TRACE("CeRapiInit");\
155 if (FAILED((call))) { WARN("FAIL."); return TEST_FAILED; }
156
157
158 //
159 // Convert text
160 //
161 char* from_unicode(const WCHAR* inbuf)
162 {
163         size_t length = wcslen(inbuf);
164         size_t inbytesleft = length * 2, outbytesleft = length;
165         char* outbuf = new char[outbytesleft+1];
166         char* outbuf_iterator = outbuf;
167         char* inbuf_iterator = (char*)inbuf;
168         
169         iconv_t cd = iconv_open("KOI8-R", "UNICODELITTLE");
170         size_t result = iconv(cd, &inbuf_iterator, &inbytesleft, &outbuf_iterator, &outbytesleft);
171         iconv_close(cd);
172
173         if ((size_t)-1 == result)
174                 strcpy(outbuf, "(failed)");
175         else
176                 outbuf[length] = 0;
177         
178         return outbuf;
179 }
180
181 WCHAR* to_unicode(const char* inbuf)
182 {
183         size_t length = strlen(inbuf);
184         size_t inbytesleft = length, outbytesleft = (length+1)* 2;
185         char * inbuf_iterator = const_cast<char*>(inbuf);
186         WCHAR* outbuf = new WCHAR[inbytesleft+1];
187         WCHAR* outbuf_iterator = outbuf;
188         
189         iconv_t cd = iconv_open("UNICODELITTLE", "KOI8-R");
190         size_t result = iconv(cd, &inbuf_iterator, &inbytesleft, (char**)&outbuf_iterator, &outbytesleft);
191         iconv_close(cd);
192
193         if ((size_t)-1 == result)
194                 outbuf[0] = 0;
195         else
196                 outbuf[length] = 0;
197
198         return outbuf;
199 }
200
201 char* ce_to_unix_name(const WCHAR* cefile)
202 {
203         char* file = from_unicode(cefile);
204         for (char* pp=file; *pp!=0; pp++)
205                 if (*pp=='/') *pp = '%';
206         return file;
207 }
208
209 WCHAR* unix_to_ce_name(const char* file)
210 {
211         WCHAR* path = to_unicode(file);
212         for (WCHAR* pp=path; *pp!=0; pp++)
213                 if (*pp=='/') *pp='\\';
214                 else if (*pp=='\\') *pp='%';
215         return path;
216 }
217
218 //static void noalrmHandler(int sig){
219 //    TRACE("False timeout...");
220 //    
221 //    signal(SIGALRM, noalrmHandler);
222 //    siginterrupt(SIGALRM, 1);
223 //}
224
225 CEFS::CEFS(struct list_head* cf, struct dir_cache *cache, struct credentials *cred){
226     cfg = cf;
227     this->cache = cache;
228     this->cred = cred;
229
230
231         stored_data = NULL;
232         stored_data_len = 0;
233         stored_file = NULL;
234         writing = FALSE;
235
236         rhnd = new RAPIHandle;
237         rhnd->sock=0;
238         rhnd->_lasterror=0;
239         rhnd->buffer=NULL;
240         rhnd->hostname=NULL;
241         rhnd->lockbuffer=NULL;
242         rhnd->lockbuffersize=0;
243
244         TRACE("in CEFS constructor");
245 }
246
247 CEFS::~CEFS(){
248         delete rhnd;
249         TRACE("in CEFS destructor");
250 }
251
252 int
253 CEFS::do_mount(){
254         TRACE("mounting a cefs.");
255 #ifdef CERAPIINIT_PER_PROCESS
256         VERIFY_HRESULT(CeRapiInit(rhnd));
257 #endif
258         return 1;
259 }
260
261 void
262 CEFS::do_umount(){
263         TRACE("umounting a cefs.");
264 #ifdef CERAPIINIT_PER_PROCESS
265         CeRapiUninit(rhnd);
266 #endif
267 }
268
269 int
270 CEFS::do_readdir(char* d, struct directory *dir){
271         int result = -1;
272         
273         TRACE("reading '"<<d<<"'");
274         
275         WCHAR* path_tmp = unix_to_ce_name(d);
276         int dlen=wcslen(path_tmp);
277         WCHAR path[dlen+5];
278         wcscpy(path,path_tmp);
279         delete[] path_tmp;
280         WCHAR* pd = path+dlen;
281         if ( *(pd-1) != '\\' ) *(pd++) = '\\';
282         *(pd++) = '*'; *(pd++) = '.'; *(pd++) = '*';
283         *(pd++) = 0;
284
285 #ifndef CERAPIINIT_PER_PROCESS
286         VERIFY_HRESULT(CeRapiInit(rhnd));
287 #endif
288
289         CE_FIND_DATA* find_data = NULL;
290         DWORD file_count = 0;
291
292         /* lufsd is now using pthreads, sorry no more alarms... */
293 //      signal(SIGALRM, noalrmHandler);
294 //      siginterrupt(SIGALRM, 1);
295
296         if( !CeFindAllFiles(rhnd,path,
297                             ( FAF_NAME|FAF_SIZE_LOW
298                               |FAF_LASTWRITE_TIME|FAF_LASTACCESS_TIME
299                               |FAF_CREATION_TIME
300                               |FAF_ATTRIBUTES ),
301                             &file_count, &find_data) ) {
302                 TRACE("could not open directory!");
303                 goto out;
304         }
305
306         struct lufs_fattr fattr;
307         fattr.f_mode = S_IFDIR;
308 //      fattr.f_mode &= ~options.o_umask;
309         fattr.f_nlink = 1;
310         fattr.f_uid = 1;
311 //      fattr.f_uid = options.o_uid;
312         fattr.f_gid = 1;
313 //      fattr.f_gid = options.o_gid;
314         /* Are these lines correct? */
315         /* I mean, they are surely wrong, but does this change anything? */
316
317         /* F.M.: yup, they're ok, not really used. they're just placeholders */
318         /*       in cache, so that each dir contains al least 2 entries... */
319 //      lu_cache_add2dir(dir, ".", NULL, &fattr);
320 //      lu_cache_add2dir(dir, "..", NULL, &fattr);
321         
322         for (unsigned i = 0; i < file_count; i++) {
323                 char* d_name = ce_to_unix_name(find_data[i].cFileName);
324                 TRACE("adding direntry " << d_name);
325
326
327                 fattr.f_mode =  S_IFREG;
328                 if ( FILE_ATTRIBUTE_DIRECTORY & find_data[i].dwFileAttributes ) {
329                         fattr.f_mode = S_IFDIR;
330 //                      fattr.f_mode |= 0111;
331                 }
332 //              fattr.f_mode |= 0666;
333 //              fattr.f_mode &= ~options.o_umask;
334                 fattr.f_size = find_data[i].nFileSizeLow;
335                 fattr.f_atime = DOSFS_FileTimeToUnixTime(
336                         &(find_data[i].ftLastAccessTime),NULL );
337                 fattr.f_mtime = DOSFS_FileTimeToUnixTime(
338                         &(find_data[i].ftLastWriteTime),NULL );
339                 fattr.f_ctime =  DOSFS_FileTimeToUnixTime(
340                         &(find_data[i].ftCreationTime),NULL );
341                 lu_cache_add2dir(dir, d_name, NULL, &fattr);
342                 delete[] d_name;
343         }
344
345         CeRapiFreeBuffer(rhnd,find_data);
346         result = 0;
347 out:
348 #ifndef CERAPIINIT_PER_PROCESS
349         CeRapiUninit(rhnd);
350 #endif
351         return result;
352 }
353
354 int
355 CEFS::do_stat(char *nm, struct lufs_fattr *fattr){
356         int result = -1;
357
358         TRACE("stat: " << nm);
359
360         string s(nm);
361         unsigned i;
362         if((i = s.find_last_of('/')) == string::npos){
363                 WARN("couldn't isolate dir in "<<nm<<"!");
364                 return -1;
365         }
366         string dir = (i == 0) ? string("/") : s.substr(0, i+1);
367         if (dir=="//") dir="/";
368         string f = dir+s.substr(i + 1, s.length() - i - 1);
369         TRACE("stat: using " << f.c_str());
370         WCHAR* path = unix_to_ce_name(f.c_str());
371 //      WCHAR* path = unix_to_ce_name(nm);
372
373         fattr->f_mode =  S_IFREG;
374 //      fattr->f_mode |= 0664;
375 //      fattr->f_mode &= ~options.o_umask;
376         fattr->f_uid = 1;
377 //      fattr->f_uid = options.o_uid;
378         fattr->f_gid = 1;
379 //      fattr->f_gid = options.o_gid;
380
381         if ( f=="/." ) { // Artificial root directory
382                 fattr->f_mode =  S_IFDIR;
383                 return 0;
384         }
385
386 #ifndef CERAPIINIT_PER_PROCESS
387         VERIFY_HRESULT(CeRapiInit(rhnd));
388 #endif
389
390         CE_FIND_DATA find_data;
391         HANDLE hnd=CeFindFirstFile(rhnd,path, &find_data);
392         delete[] path;
393         if (INVALID_HANDLE_VALUE==hnd) {
394                 TRACE("CeFindFirstFile of '"<<nm<<"'failed in do_stat!");
395                 goto out;
396         }
397         if ( FAILED(CeFindClose( rhnd,hnd )) )
398                 WARN("CeFindClose failed in do_stat!");
399
400         if ( FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes ) {
401                 fattr->f_mode = S_IFDIR;
402 //              fattr->f_mode |= 0111;
403         }
404 //      fattr->f_mode |= 0666;
405 //      fattr->f_mode &= ~options.o_umask;
406         fattr->f_nlink = 1;
407         fattr->f_size = find_data.nFileSizeLow;
408         fattr->f_atime = DOSFS_FileTimeToUnixTime(
409                 &(find_data.ftLastAccessTime),NULL );
410         fattr->f_mtime = DOSFS_FileTimeToUnixTime(
411                 &(find_data.ftLastWriteTime),NULL );
412         fattr->f_ctime =  DOSFS_FileTimeToUnixTime(
413                 &(find_data.ftCreationTime),NULL );
414
415         result = 0;
416 out:
417 #ifndef CERAPIINIT_PER_PROCESS
418         CeRapiUninit(rhnd);
419 #endif
420         return result;
421 }
422
423 int
424 CEFS::do_mkdir(char *dir, int mode){
425 #ifndef CERAPIINIT_PER_PROCESS
426         VERIFY_HRESULT(CeRapiInit(rhnd));
427 #endif
428         WCHAR* path = unix_to_ce_name(dir);
429         int res = CeCreateDirectory(rhnd,path, NULL);
430         delete[] path;
431 #ifndef CERAPIINIT_PER_PROCESS
432         CeRapiUninit(rhnd);
433 #endif
434         return res ? 0:-1;
435 }
436
437 int
438 CEFS::do_rmdir(char *dir){
439 #ifndef CERAPIINIT_PER_PROCESS
440         VERIFY_HRESULT(CeRapiInit(rhnd));
441 #endif
442         WCHAR* path = unix_to_ce_name(dir);
443         int res =  CeRemoveDirectory(rhnd,path);
444         delete[] path;
445 #ifndef CERAPIINIT_PER_PROCESS
446         CeRapiUninit(rhnd);
447 #endif
448         return res ? 0:-1;
449 }
450
451 int
452 CEFS::do_create(char *file, int mode){
453         int result = -1;
454 #ifndef CERAPIINIT_PER_PROCESS
455         VERIFY_HRESULT(CeRapiInit(rhnd));
456 #endif
457         TRACE(file<< " mode " << mode);
458         WCHAR* path = unix_to_ce_name(file);
459         HANDLE handle = CeCreateFile(rhnd,path,
460                                      GENERIC_WRITE,
461                                      0,
462                                      NULL,
463                                      CREATE_ALWAYS,
464                                      FILE_ATTRIBUTE_NORMAL,
465                                      NULL);
466         delete[] path;
467         if (INVALID_HANDLE_VALUE==handle) goto out;
468         CeCloseHandle(rhnd,handle);
469
470         result = 0;
471 out:
472 #ifndef CERAPIINIT_PER_PROCESS
473         CeRapiUninit(rhnd);
474 #endif
475         return result;
476 }
477
478 int
479 CEFS::do_unlink(char *file){
480 #ifndef CERAPIINIT_PER_PROCESS
481         VERIFY_HRESULT(CeRapiInit(rhnd));
482 #endif
483         WCHAR* path =unix_to_ce_name(file);
484         int res =  CeDeleteFile(rhnd,path);
485         delete[] path;
486 #ifndef CERAPIINIT_PER_PROCESS
487         CeRapiUninit(rhnd);
488 #endif
489         return res ? 0:-1;
490 }
491
492 int
493 CEFS::do_rename(char *old, char *nnew){
494 #ifndef CERAPIINIT_PER_PROCESS
495         VERIFY_HRESULT(CeRapiInit(rhnd));
496 #endif
497         TRACE(old << " to " << nnew);
498
499         WCHAR* oldpath = unix_to_ce_name(old);
500         WCHAR* newpath = unix_to_ce_name(nnew);
501
502         int res=CeMoveFile(rhnd, oldpath,newpath );
503
504         delete[] oldpath;
505         delete[] newpath;
506
507 #ifndef CERAPIINIT_PER_PROCESS
508         CeRapiUninit(rhnd);
509 #endif
510         return res ? 0:-1;
511 }
512
513 int
514 CEFS::do_open(char *file, unsigned mode){
515
516         TRACE(file <<"mode" << mode);
517         if (stored_data_len) {
518                 do_release(stored_file);
519         }
520         if ( mode&O_WRONLY ) {
521                 writing = TRUE;
522                 stored_file = strdup(file);
523         }
524         return 0;
525 }
526
527 int
528 CEFS::do_release(char *file){
529         TRACE(file);
530         int result = 0;
531         if (writing && stored_data_len) {
532                 result = -1;
533 #ifndef CERAPIINIT_PER_PROCESS
534                 VERIFY_HRESULT(CeRapiInit(rhnd));
535 #endif
536                 WCHAR* path = unix_to_ce_name(file);
537                 HANDLE handle = CeCreateFile(rhnd,path,
538                                              GENERIC_WRITE,
539                                              0,
540                                              NULL,
541                                              CREATE_ALWAYS,
542                                              FILE_ATTRIBUTE_NORMAL,
543                                              NULL);
544                 delete[] path;
545                 if (handle == INVALID_HANDLE_VALUE) goto out;
546                 for ( char* buffer=stored_data;
547                       buffer<stored_data+stored_data_len;
548                       buffer+=MYBUF_SIZE ) {
549                         DWORD did_write = (DWORD)-1;
550                         size_t bytes_read = MYBUF_SIZE;
551                         if ( buffer+MYBUF_SIZE >= stored_data+stored_data_len )
552                                 bytes_read = stored_data+stored_data_len-buffer;
553
554                         if ( 0==CeWriteFile(rhnd,handle, buffer, bytes_read, &did_write, NULL) )
555                                 goto out;
556                         if (bytes_read != did_write)
557                                 WARN("Only wrote %lu bytes to file of %u possible.\n"<< did_write<< bytes_read);
558                         usleep(100);
559                 }
560                 CeCloseHandle(rhnd,handle);
561
562                 result = 0;
563 out:
564 #ifndef CERAPIINIT_PER_PROCESS
565                 CeRapiUninit(rhnd);
566 #endif
567                 ;
568         }
569         if (stored_data_len) {
570                 free(stored_data);
571                 free(stored_file);
572                 stored_data_len = 0;
573                 stored_data=NULL;
574                 stored_file=NULL;
575         }
576         writing = FALSE;
577         return result;
578 }
579
580 int
581 CEFS::do_read(char *file, long long offset, unsigned long count, char *buf){
582         TRACE("read "<<file<<", "<<offset<<", "<<count);
583
584         int result = -1;
585         if ( stored_data_len==0 || strcmp(file,stored_file)!=0 ) {
586 //              signal(SIGALRM, noalrmHandler);
587 //              siginterrupt(SIGALRM, 1);
588
589                 struct lufs_fattr fattr;
590                 if ( do_stat(file, &fattr)!=0 )
591                         return -1;
592                 if (stored_data_len) {
593                         free(stored_data);
594                         free(stored_file);
595                 }
596                 stored_file=strdup(file);
597                 stored_data_len = fattr.f_size;
598                 stored_data = (char*)malloc(stored_data_len*sizeof(char));
599 #ifndef CERAPIINIT_PER_PROCESS
600                 VERIFY_HRESULT(CeRapiInit(rhnd));
601 #endif
602                 WCHAR* path = unix_to_ce_name(file);
603
604                 HANDLE handle = CeCreateFile(rhnd,path,
605                                              GENERIC_READ,
606                                              0,
607                                              NULL,
608                                              OPEN_EXISTING,
609                                              FILE_ATTRIBUTE_NORMAL,
610                                              NULL);
611                 delete[] path;
612                 if (INVALID_HANDLE_VALUE == handle ) goto out;
613                 DWORD bytes_read;
614                 for ( char* point=stored_data;
615                       CeReadFile(rhnd,handle,point,MYBUF_SIZE,&bytes_read,NULL);
616                       point += bytes_read )
617                         if (bytes_read==0) break;
618                 CeCloseHandle(rhnd,handle);
619
620                 result = 0;
621         out:
622 #ifndef CERAPIINIT_PER_PROCESS
623                 CeRapiUninit(rhnd);
624 #endif
625                 if (result < 0) return result;
626         }
627         if (offset+count>stored_data_len)
628                 count = stored_data_len-offset;
629         memcpy(buf,stored_data+offset,count);
630         result = count;
631         return result;
632 }
633
634 int
635 CEFS::do_write(char *file, long long offset, unsigned long count, char *buf){
636
637         TRACE("write "<<file<<", "<<offset<<", "<<count);
638         if ( stored_data_len!=0 && strcmp(file,stored_file)!=0 ) {
639                 do_release(file);
640                 stored_file=strdup(file);
641         }
642         if (!writing)
643                 return -1;
644         if (offset+count>stored_data_len) {
645                 stored_data_len = offset+count;
646                 stored_data = (char*)realloc(stored_data,stored_data_len);
647         }
648         memcpy(stored_data+offset,buf,count);
649         return count;
650 }
651
652
653 int
654 CEFS::do_setattr(char* file, struct lufs_fattr*)
655 {
656         TRACE(file);
657         return 0;
658 }