http://prdownloads.sourceforge.net/lufs/lufs-0.9.6.tar.gz?download
[lufs.git] / filesystems / ftpfs / ftpfs.cpp
1 /*
2  * ftpfs.cpp
3  * Copyright (C) 2002 Florin Malita <mali@go.ro>
4  *
5  * This file is part of LUFS, a free userspace filesystem implementation.
6  * See http://lufs.sourceforge.net/ for updates.
7  *
8  * LUFS is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * LUFS is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include <unistd.h>
24 #include <stdio.h>
25
26 #include <sys/types.h>
27 #include <sys/stat.h>
28
29 #include <lufs/proto.h>
30 #include <lufs/fs.h>
31
32 #include <string>
33
34 #include "ftpfs.h"
35 #include "ftplib.h"
36 #include "ftpsys.h"
37 #include "ftpsys_unix.h"
38 #include "ftpsys_netware.h"
39 #include "ftpsys_windows.h"
40
41 extern "C"{
42
43 void*
44 ftpfs_init(struct list_head *cfg, struct dir_cache *cache, struct credentials *cred, void **global_ctx){
45
46     if(!lu_opt_getchar(cfg, "MOUNT", "host")){
47         ERROR("you must specify a host!");
48         return NULL;
49     }
50
51     return (void*)new FTPFS(cfg, cache, cred);
52 }
53
54 void
55 ftpfs_free(void *ctx){
56     FTPFS *p = (FTPFS*)ctx;
57
58     delete p;
59 }
60
61 int     
62 ftpfs_mount(void *ctx){
63     return ((FTPFS*)ctx)->do_mount();
64 }
65
66 void    
67 ftpfs_umount(void *ctx){
68 //    return ((FTPFS*)ctx)->do_umount();
69 }
70
71 int     
72 ftpfs_readdir(void *ctx, char *dir_name, struct directory *dir){
73     return ((FTPFS*)ctx)->do_readdir(dir_name, dir);
74 }
75
76 int     
77 ftpfs_stat(void *ctx, char *name, struct lufs_fattr *fattr){
78     return ((FTPFS*)ctx)->do_stat(name, fattr);
79 }
80
81 int     
82 ftpfs_mkdir(void *ctx, char *dir, int mode){
83     return ((FTPFS*)ctx)->do_mkdir(dir, mode);
84 }
85
86 int     
87 ftpfs_rmdir(void *ctx, char *dir){
88     return ((FTPFS*)ctx)->do_rmdir(dir);
89 }
90
91 int     
92 ftpfs_create(void *ctx, char *file, int mode){
93     return ((FTPFS*)ctx)->do_create(file, mode);
94 }
95
96 int     
97 ftpfs_unlink(void *ctx, char *file){
98     return ((FTPFS*)ctx)->do_unlink(file);
99 }
100
101 int     
102 ftpfs_rename(void *ctx, char *old_name, char *new_name){
103     return ((FTPFS*)ctx)->do_rename(old_name, new_name);
104 }
105
106 int     
107 ftpfs_open(void *ctx, char *file, unsigned mode){
108     return ((FTPFS*)ctx)->do_open(file, mode);
109 }
110
111 int     
112 ftpfs_release(void *ctx, char *file){
113     return ((FTPFS*)ctx)->do_release(file);
114 }
115
116 int     
117 ftpfs_read(void *ctx, char *file, long long offset, unsigned long count, char *buf){
118     return ((FTPFS*)ctx)->do_read(file, offset, count, buf);
119 }
120
121 int     
122 ftpfs_write(void *ctx, char *file, long long offset, unsigned long count, char *buf){
123     return ((FTPFS*)ctx)->do_write(file, offset, count, buf);
124 }
125
126 int     
127 ftpfs_readlink(void *ctx, char *link, char *buf, int buflen){
128     return ((FTPFS*)ctx)->do_readlink(link, buf, buflen);
129 }
130
131 int     
132 ftpfs_link(void *ctx, char *target, char *link){
133     return -1;
134 }
135
136 int     
137 ftpfs_symlink(void *ctx, char *target, char *link){
138     return -1;
139 }
140
141 int     
142 ftpfs_setattr(void *ctx, char *file, struct lufs_fattr *fattr){
143     return ((FTPFS*)ctx)->do_setattr(file, fattr);
144 }
145
146 } /* extern "C" */
147
148
149
150
151 FTPFS::FTPFS(struct list_head *cf, struct dir_cache *cache, struct credentials *cred){
152     const char *c, *user, *pass;
153     long int port;
154     int active = 0;
155
156     TRACE("in constructor");
157
158     cfg = cf;
159     this->cache = cache;
160     this->cred = cred;
161
162     rw_timeout = 0;
163     if((c = lu_opt_getchar(cfg, "FTPFS", "RWTimeout"))){
164         rw_timeout = atoi(c);
165         TRACE("RWTimeout set to "<<rw_timeout);
166     }
167
168     if(!rw_timeout)
169         rw_timeout = 20;
170
171      if((c = lu_opt_getchar(cfg, "FTPFS", "DataConnectionMode"))){
172         TRACE("DataConnectionMode set to "<<c);
173         if(!strcmp(c, "Active")){
174             active = 1;
175         }
176      }
177
178      if(lu_opt_getchar(cfg, "MOUNT", "ftpactive")){
179          TRACE("DataConnectionMode set to active");
180          active = 1;
181      }
182
183      if(lu_opt_getchar(cfg, "MOUNT", "ftppassive")){
184          TRACE("DataConnectionMode set to passive");
185          active = 0;
186      }
187
188      if(lu_opt_getint(cfg, "MOUNT", "port", &port, 10) < 0)
189          port = 21;
190
191      if(!(user = lu_opt_getchar(cfg, "MOUNT", "username")))
192          user = "anonymous";
193
194      if(!(pass = lu_opt_getchar(cfg, "MOUNT", "password")))
195          pass = "user@sourceforge.net";
196
197     conn = new FTPConnection(active, (char*)lu_opt_getchar(cfg, "MOUNT", "host"), port, (char*)user, (char*)pass);
198     ftpsys = NULL;
199
200 }
201
202 FTPFS::~FTPFS(){
203     TRACE("in destructor");
204     delete conn;
205     if(ftpsys)
206         delete ftpsys;
207 }
208
209 int
210 FTPFS::do_mount(){
211     int res;
212
213     TRACE("");
214
215     res = conn->connect();
216     if(res >= 0){
217         /* ftpsys initialization */
218         if(!strcmp(conn->system, "NETWARE"))
219             ftpsys = new ftpsys_netware();
220         else if(!strcmp(conn->system, "Windows_NT"))
221             ftpsys = new ftpsys_windows();
222         else
223             ftpsys = new ftpsys_unix();
224     
225         TRACE("list command: "<<ftpsys->CMD_LIST);
226     }
227
228     
229     return (res < 0) ? 0 : 1;
230 }
231
232 void
233 FTPFS::do_umount(){
234     TRACE("");
235
236     conn->disconnect();
237 }
238
239 int
240 FTPFS::do_readdir(char *dir, struct directory *d){
241     int res;
242     struct lufs_fattr fattr;
243
244     TRACE("dir: "<<dir);
245     char *file = new char[FTP_MAXFILE];
246     char *link = new char[FTP_MAXFILE];
247     char *buf = new char[FTP_MAXLINE];
248
249     if((res = conn->execute_retry(string("CWD ") + dir, 250, 1)) < 0){
250         WARN("CWD failed!");
251         goto out;
252     }
253
254     if((res = conn->execute_open(string(ftpsys->CMD_LIST), string("A"), 0)) < 0){
255         WARN("execute_open failed!");
256         goto out;
257     }
258
259     if((res = lu_check_to(conn->dsock, 0, rw_timeout))){
260         conn->disconnect();
261         goto out;
262     }
263
264     while(fgets(buf, FTP_MAXLINE, conn->dfd)){
265         if(ftpsys->parse_line(buf, file, &fattr, link, cred) >= 0){
266             lu_cache_add2dir(d, file, link, &fattr);
267         }
268     }
269     if(ferror(conn->dfd)){
270         conn->disconnect();
271         res = -1;
272         goto out;
273     }
274
275     conn->close_data();
276     res = 0;
277   out:
278     delete buf;
279     delete link;
280     delete file;
281     return res;
282 }
283
284 int
285 FTPFS::do_stat(char *file, struct lufs_fattr *fattr){
286     string link, s(file);
287     unsigned i;
288     void *ddir;
289
290     TRACE("file: "<<file);
291     
292     if((i = s.find_last_of('/')) == string::npos){
293         WARN("couldn't isolate dir in "<<file<<"!");
294         return -1;
295     }
296     
297     string dir = (i == 0) ? string("/") : s.substr(0, i);
298     string f = s.substr(i + 1, s.length() - i - 1);
299     
300     if(!(ddir = lu_cache_mkdir((char*)dir.c_str())))
301         return -1;
302
303     if(do_readdir((char*)dir.c_str(), (struct directory*)ddir) < 0){
304         WARN("do_readdir failed!");
305         lu_cache_killdir((struct directory*)ddir);
306         return -1;
307     }
308
309     lu_cache_add_dir(cache, (struct directory*)ddir);
310
311     if(lu_cache_lookup(cache, (char*)dir.c_str(), (char*)f.c_str(), fattr, NULL, 0) < 0)
312         return -1;
313
314     return 0;
315 }
316
317 int
318 FTPFS::do_readlink(char *lnk, char *buf, int buflen){
319     unsigned i;
320     string link, s(lnk);
321     struct lufs_fattr fattr;
322     void *ddir;
323
324     TRACE("");
325
326     if((i = s.find_last_of('/')) == string::npos){
327         WARN("couldn't isolate dir in "<<lnk<<"!");
328         return -1;
329     }
330
331     string dir = (i == 0) ? string("/") : s.substr(0, i);
332     string f = s.substr(i + 1, s.length() - i - 1);
333
334     if(!(ddir = lu_cache_mkdir((char*)dir.c_str())))
335         return -1;
336
337     if(do_readdir((char*)dir.c_str(), (struct directory*)ddir) < 0){
338         WARN("do_readdir failed!");
339         lu_cache_killdir((struct directory*)ddir);
340         return -1;
341     }
342
343     lu_cache_add_dir(cache, (struct directory*)ddir);
344
345     if(lu_cache_lookup(cache, (char*)dir.c_str(), (char*)f.c_str(), &fattr, buf, buflen) < 0){
346         WARN("lookup still failing... why!?!?!?");
347         return -1;
348     }
349
350     return strlen(buf);
351 }
352
353 int
354 FTPFS::do_open(char *file, unsigned mode){
355     TRACE("");
356
357     return 0;
358 }
359
360 int
361 FTPFS::do_release(char *file){
362     TRACE("");
363     conn->close_data();
364     return 0;
365 }
366
367 int
368 FTPFS::do_read(char *file, long long offset, unsigned long count, char *b){
369     int res = 0, tries = 0;
370     
371   again:
372     TRACE("read "<<file<<", "<<offset<<", "<<count);
373
374     if(tries++ > FTP_MAXTRIES){
375         WARN("too many failures!");
376         if(res < 0)
377             return res;
378         else
379             return -1;
380     }
381
382     if((res = conn->execute_open(string("RETR ") + file, string("I"), offset)) < 0){
383         WARN("couldn't open data connection!");
384         return res;
385     }
386
387     if((res = lu_check_to(conn->dsock, 0, rw_timeout))){
388         conn->close_data();
389         goto again;
390     }
391
392     if((res = fread(b, 1, count, conn->dfd)) < (int)count){
393         TRACE("short read: "<<res);
394         if(ferror(conn->dfd)){
395             conn->close_data();
396             goto again;
397         }
398     }
399
400     conn->last_off += res;
401
402     return res;
403 }
404
405 int
406 FTPFS::do_mkdir(char *dir, int mode){
407     int res;
408
409     TRACE("");
410
411     if((res = conn->execute_retry(string("MKD ") + dir, 257, 1)) < 0){
412         WARN("MKDIR failed!");
413         return res;
414     }
415
416     return 0;
417 }
418
419
420 int
421 FTPFS::do_rmdir(char *dir){
422     int res;
423
424     TRACE("");
425
426     if((res = conn->execute_retry(string("RMD ") + dir, 0, 1)) < 0){
427         WARN("execute failed!");
428         return res;
429     }
430
431     /* Ugly WarFTP hack */
432     if((conn->get_response() / 100) != 2){
433         WARN("RMDIR failed!");
434         return -1;
435     }
436
437     return 0;
438 }
439
440 int
441 FTPFS::do_unlink(char *file){
442     int res;
443
444     TRACE("");
445
446     if((res = conn->execute_retry(string("DELE ") + file, 250, 1)) < 0){
447         WARN("DELE failed!");
448         return res;
449     }
450
451     return 0;
452 }
453
454 int
455 FTPFS::do_create(char *file, int mode){
456     int res;
457
458     TRACE("");
459
460     if((res = conn->execute_open(string("STOR ") + file, string("I"), 0)) < 0){
461         WARN("couldn't create file!");
462         return res;
463     }
464
465     conn->close_data();
466
467     return 0;
468 }
469
470 int
471 FTPFS::do_rename(char *old, char *nnew){
472     int res;
473
474     TRACE("");
475
476     if((res = conn->execute_retry(string("RNFR ") + old, 350, 1)) < 0){
477         WARN("RNFR failed!");
478         return res;
479     }
480
481
482     if((res = conn->execute_retry(string("RNTO ") + nnew, 250, 1)) < 0){
483         WARN("RNTO failed!");
484         return res;
485     }
486
487     return 0;
488 }
489
490 int
491 FTPFS::do_write(char *file, long long offset, unsigned long count, char *b){
492     int res = 0, tries = 0;
493
494     TRACE("");
495     
496   again:
497     if(tries++ > FTP_MAXTRIES){
498         WARN("too many failures!");
499         if(res < 0)
500             return res;
501         else
502             return -1;
503     }
504
505     if((res = conn->execute_open(string("STOR ") + file, string("I"), offset)) < 0){
506         WARN("couldn't open data connection!");
507         return res;
508     }
509
510     if((res = lu_check_to(0, conn->dsock, rw_timeout))){
511         conn->close_data();
512         goto again;
513     }
514
515     if((res = fwrite(b, 1, count, conn->dfd)) < (int)count){
516         TRACE("short write: "<<res);
517         if(ferror(conn->dfd)){
518             conn->close_data();
519             goto again;
520         }
521     }
522
523     conn->last_off += res;
524
525     return res;
526 }
527
528 int
529 FTPFS::do_setattr(char *file, struct lufs_fattr *fattr){
530     int res;
531     char buf[10];
532
533     TRACE("setattr "<<file);
534
535     if(snprintf(buf, 10, "%lo", fattr->f_mode & 0777) >= 10)
536         buf[9] = 0;
537
538     string cmd = string ("SITE CHMOD ") + buf + string(" ") + file;
539
540     TRACE("cmd: "<<cmd);
541
542     if((res = conn->execute_retry(cmd, 200, 1)) < 0){
543         WARN("SITE CHMOD failed!");
544         return res;
545     }
546
547     return 0;
548 }
549
550