source: FTPfs/curlftpfs-0.9.1/ftpfs.c @ 10

Last change on this file since 10 was 10, checked in by zsjheng, 16 years ago
File size: 40.7 KB
Line 
1/*
2    FTP file system
3    Copyright (C) 2006 Robson Braga Araujo <robsonbraga@gmail.com>
4
5    This program can be distributed under the terms of the GNU GPL.
6    See the file COPYING.
7*/
8
9#include "config.h"
10
11#include <stdlib.h>
12#include <stdio.h>
13#include <ctype.h>
14#include <stdint.h>
15#include <errno.h>
16#include <string.h>
17#include <fcntl.h>
18#include <unistd.h>
19#include <netinet/in.h>
20#include <fuse.h>
21#include <fuse_opt.h>
22#include <glib.h>
23
24#include "charset_utils.h"
25#include "path_utils.h"
26#include "ftpfs-ls.h"
27#include "cache.h"
28#include "ftpfs.h"
29
30#define CURLFTPFS_BAD_NOBODY 0x070f02
31#define CURLFTPFS_BAD_SSL 0x070f03
32
33#define CURLFTPFS_BAD_READ ((size_t)-1)
34
35#define MAX_BUFFER_LEN (300*1024)
36
37struct ftpfs ftpfs;
38static char error_buf[CURL_ERROR_SIZE];
39
40struct buffer {
41  uint8_t* p;
42  size_t len;
43  size_t size;
44  off_t begin_offset;
45};
46
47static void usage(const char* progname);
48
49static inline void buf_init(struct buffer* buf, size_t size)
50{
51    if (size) {
52        buf->p = (uint8_t*) malloc(size);
53        if (!buf->p) {
54            fprintf(stderr, "ftpfs: memory allocation failed\n");
55            exit(1);
56        }
57    } else
58        buf->p = NULL;
59    buf->begin_offset = 0;
60    buf->len = 0;
61    buf->size = size;
62}
63
64static inline void buf_free(struct buffer* buf)
65{
66    free(buf->p);
67}
68
69static inline void buf_finish(struct buffer *buf)
70{
71    buf->len = buf->size;
72}
73
74
75static inline void buf_clear(struct buffer *buf)
76{
77    buf_free(buf);
78    buf_init(buf, 0);
79}
80
81static void buf_resize(struct buffer *buf, size_t len)
82{
83    buf->size = (buf->len + len + 63) & ~31;
84    buf->p = (uint8_t *) realloc(buf->p, buf->size);
85    if (!buf->p) {
86        fprintf(stderr, "ftpfs: memory allocation failed\n");
87        exit(1);
88    }
89}
90
91static inline void buf_check_add(struct buffer *buf, size_t len)
92{
93    if (buf->len + len > buf->size)
94        buf_resize(buf, len);
95}
96
97#define _buf_add_mem(b, d, l)    \
98    buf_check_add(b, l);       \
99    memcpy(b->p + b->len, d, l); \
100    b->len += l;
101
102
103static inline void buf_add_mem(struct buffer *buf, const void *data,
104                               size_t len)
105{
106    _buf_add_mem(buf, data, len);
107}
108
109static inline void buf_add_buf(struct buffer *buf, const struct buffer *bufa)
110{
111    _buf_add_mem(buf, bufa->p, bufa->len);
112}
113
114static inline void buf_add_uint8(struct buffer *buf, uint8_t val)
115{
116    _buf_add_mem(buf, &val, 1);
117}
118
119static inline void buf_add_uint32(struct buffer *buf, uint32_t val)
120{
121    uint32_t nval = htonl(val);
122    _buf_add_mem(buf, &nval, 4);
123}
124
125static inline void buf_add_uint64(struct buffer *buf, uint64_t val)
126{
127    buf_add_uint32(buf, val >> 32);
128    buf_add_uint32(buf, val & 0xffffffff);
129}
130
131static inline void buf_add_data(struct buffer *buf, const struct buffer *data)
132{
133    buf_add_uint32(buf, data->len);
134    buf_add_mem(buf, data->p, data->len);
135}
136
137static inline void buf_add_string(struct buffer *buf, const char *str)
138{
139    struct buffer data;
140    data.p = (uint8_t *) str;
141    data.len = strlen(str);
142    buf_add_data(buf, &data);
143}
144
145static int buf_check_get(struct buffer *buf, size_t len)
146{
147    if (buf->len + len > buf->size) {
148        fprintf(stderr, "buffer too short\n");
149        return -1;
150    } else
151        return 0;
152}
153
154static inline int buf_get_mem(struct buffer *buf, void *data, size_t len)
155{
156    if (buf_check_get(buf, len) == -1)
157        return -1;
158    memcpy(data, buf->p + buf->len, len);
159    buf->len += len;
160    return 0;
161}
162
163static inline int buf_get_uint8(struct buffer *buf, uint8_t *val)
164{
165    return buf_get_mem(buf, val, 1);
166}
167
168static inline int buf_get_uint32(struct buffer *buf, uint32_t *val)
169{
170    uint32_t nval;
171    if (buf_get_mem(buf, &nval, 4) == -1)
172        return -1;
173    *val = ntohl(nval);
174    return 0;
175}
176
177static inline int buf_get_uint64(struct buffer *buf, uint64_t *val)
178{
179    uint32_t val1;
180    uint32_t val2;
181    if (buf_get_uint32(buf, &val1) == -1 || buf_get_uint32(buf, &val2) == -1)
182        return -1;
183    *val = ((uint64_t) val1 << 32) + val2;
184    return 0;
185}
186
187static inline int buf_get_data(struct buffer *buf, struct buffer *data)
188{
189    uint32_t len;
190    if (buf_get_uint32(buf, &len) == -1 || len > buf->size - buf->len)
191        return -1;
192    buf_init(data, len + 1);
193    data->size = len;
194    if (buf_get_mem(buf, data->p, data->size) == -1) {
195        buf_free(data);
196        return -1;
197    }
198    return 0;
199}
200
201static inline int buf_get_string(struct buffer *buf, char **str)
202{
203    struct buffer data;
204    if (buf_get_data(buf, &data) == -1)
205        return -1;
206    data.p[data.size] = '\0';
207    *str = (char *) data.p;
208    return 0;
209}
210
211struct ftpfs_file {
212  struct buffer buf;
213  int dirty;
214  int copied;
215  off_t last_offset;
216  int can_shrink;
217};
218
219enum {
220  KEY_HELP,
221  KEY_VERBOSE,
222  KEY_VERSION,
223};
224
225#define FTPFS_OPT(t, p, v) { t, offsetof(struct ftpfs, p), v }
226
227static struct fuse_opt ftpfs_opts[] = {
228  FTPFS_OPT("ftpfs_debug=%u",     debug, 0),
229  FTPFS_OPT("transform_symlinks", transform_symlinks, 1),
230  FTPFS_OPT("disable_epsv",       disable_epsv, 1),
231  FTPFS_OPT("skip_pasv_ip",       skip_pasv_ip, 1),
232  FTPFS_OPT("ftp_port=%s",        ftp_port, 0),
233  FTPFS_OPT("disable_eprt",       disable_eprt, 1),
234  FTPFS_OPT("tcp_nodelay",        tcp_nodelay, 1),
235  FTPFS_OPT("connect_timeout=%u", connect_timeout, 0),
236  FTPFS_OPT("ssl",                use_ssl, CURLFTPSSL_ALL),
237  FTPFS_OPT("ssl_control",        use_ssl, CURLFTPSSL_CONTROL),
238  FTPFS_OPT("ssl_try",            use_ssl, CURLFTPSSL_TRY),
239  FTPFS_OPT("no_verify_hostname", no_verify_hostname, 1),
240  FTPFS_OPT("no_verify_peer",     no_verify_peer, 1),
241  FTPFS_OPT("cert=%s",            cert, 0),
242  FTPFS_OPT("cert_type=%s",       cert_type, 0),
243  FTPFS_OPT("key=%s",             key, 0),
244  FTPFS_OPT("key_type=%s",        key_type, 0),
245  FTPFS_OPT("pass=%s",            key_password, 0),
246  FTPFS_OPT("engine=%s",          engine, 0),
247  FTPFS_OPT("cacert=%s",          cacert, 0),
248  FTPFS_OPT("capath=%s",          capath, 0),
249  FTPFS_OPT("ciphers=%s",         ciphers, 0),
250  FTPFS_OPT("interface=%s",       interface, 0),
251  FTPFS_OPT("krb4=%s",            krb4, 0),
252  FTPFS_OPT("proxy=%s",           proxy, 0),
253  FTPFS_OPT("proxytunnel",        proxytunnel, 1),
254  FTPFS_OPT("proxy_anyauth",      proxyanyauth, 1),
255  FTPFS_OPT("proxy_basic",        proxybasic, 1),
256  FTPFS_OPT("proxy_digest",       proxydigest, 1),
257  FTPFS_OPT("proxy_ntlm",         proxyntlm, 1),
258  FTPFS_OPT("httpproxy",          proxytype, CURLPROXY_HTTP),
259  FTPFS_OPT("socks4",             proxytype, CURLPROXY_SOCKS4),
260  FTPFS_OPT("socks5",             proxytype, CURLPROXY_SOCKS5),
261  FTPFS_OPT("user=%s",            user, 0),
262  FTPFS_OPT("proxy_user=%s",      proxy_user, 0),
263  FTPFS_OPT("tlsv1",              ssl_version, CURL_SSLVERSION_TLSv1),
264  FTPFS_OPT("sslv3",              ssl_version, CURL_SSLVERSION_SSLv3),
265  FTPFS_OPT("ipv4",               ip_version, CURL_IPRESOLVE_V4),
266  FTPFS_OPT("ipv6",               ip_version, CURL_IPRESOLVE_V6),
267  FTPFS_OPT("utf8",               tryutf8, 1),
268  FTPFS_OPT("codepage=%s",        codepage, 0),
269  FTPFS_OPT("iocharset=%s",       iocharset, 0),
270
271  FUSE_OPT_KEY("-h",             KEY_HELP),
272  FUSE_OPT_KEY("--help",         KEY_HELP),
273  FUSE_OPT_KEY("-v",             KEY_VERBOSE),
274  FUSE_OPT_KEY("--verbose",      KEY_VERBOSE),
275  FUSE_OPT_KEY("-V",             KEY_VERSION),
276  FUSE_OPT_KEY("--version",      KEY_VERSION),
277  FUSE_OPT_END
278};
279
280static size_t write_data(void *ptr, size_t size, size_t nmemb, void *data) {
281  struct ftpfs_file* fh = (struct ftpfs_file*)data;
282  if (fh == NULL) return 0;
283  size_t to_copy = size * nmemb;
284  if (to_copy > fh->buf.len - fh->copied) {
285    to_copy = fh->buf.len - fh->copied;
286  }
287  DEBUG(2, "write_data: %zu\n", to_copy);
288  DEBUG(3, "%*s\n", (int)to_copy, (char*)ptr);
289  memcpy(ptr, fh->buf.p + fh->copied, to_copy);
290  fh->copied += to_copy;
291  return to_copy;
292}
293
294static size_t read_data(void *ptr, size_t size, size_t nmemb, void *data) {
295  struct buffer* buf = (struct buffer*)data;
296  if (buf == NULL) return size * nmemb;
297  buf_add_mem(buf, ptr, size * nmemb);
298  DEBUG(2, "read_data: %zu\n", size * nmemb);
299  DEBUG(3, "%*s\n", (int)(size * nmemb), (char*)ptr);
300  return size * nmemb;
301}
302
303#define curl_easy_setopt_or_die(handle, option, ...) \
304  do {\
305    CURLcode res = curl_easy_setopt(handle, option, __VA_ARGS__);\
306    if (res != CURLE_OK) {\
307      fprintf(stderr, "Error setting curl: %s\n", error_buf);\
308      exit(1);\
309    }\
310  }while(0);
311
312static int ftpfs_getdir(const char* path, fuse_cache_dirh_t h,
313                        fuse_cache_dirfil_t filler) {
314  int err = 0;
315  CURLcode curl_res;
316  char* dir_path = get_fulldir_path(path);
317
318  DEBUG(1, "ftpfs_getdir: %s\n", dir_path);
319  struct buffer buf;
320  buf_init(&buf, 0);
321
322  pthread_mutex_lock(&ftpfs.lock);
323  curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
324  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, dir_path);
325  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
326  curl_res = curl_easy_perform(ftpfs.connection);
327  pthread_mutex_unlock(&ftpfs.lock);
328
329  if (curl_res != 0) {
330    DEBUG(1, "%s\n", error_buf);
331    err = -EIO;
332  } else {
333    buf_add_mem(&buf, "\0", 1);
334    parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
335              NULL, NULL, NULL, 0, h, filler); 
336  }
337
338  free(dir_path);
339  buf_free(&buf);
340  return err;
341}
342
343static int ftpfs_getattr(const char* path, struct stat* sbuf) {
344  int err;
345  CURLcode curl_res;
346  char* dir_path = get_dir_path(path);
347
348  DEBUG(2, "dir_path: %s %s\n", path, dir_path);
349  struct buffer buf;
350  buf_init(&buf, 0);
351
352  pthread_mutex_lock(&ftpfs.lock);
353  curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
354  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, dir_path);
355  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
356  curl_res = curl_easy_perform(ftpfs.connection);
357  pthread_mutex_unlock(&ftpfs.lock);
358
359  if (curl_res != 0) {
360    DEBUG(1, "%s\n", error_buf);
361  }
362  buf_add_mem(&buf, "\0", 1);
363
364  char* name = strrchr(path, '/');
365  ++name;
366  err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
367                  name, sbuf, NULL, 0, NULL, NULL); 
368
369  free(dir_path);
370  buf_free(&buf);
371  if (err) return -ENOENT;
372  return 0;
373}
374
375static int check_running() {
376  int running_handles = 0;
377  curl_multi_perform(ftpfs.multi, &running_handles);
378  return running_handles;
379}
380
381static size_t ftpfs_read_chunk(const char* full_path, char* rbuf,
382                               size_t size, off_t offset,
383                               struct fuse_file_info* fi,
384                               int update_offset) {
385  int running_handles = 0;
386  int err = 0;
387  struct ftpfs_file* fh = (struct ftpfs_file*) (uintptr_t) fi->fh;
388
389  DEBUG(2, "ftpfs_read_chunk: %s %p %zu %lld %p %p\n",
390        full_path, rbuf, size, offset, fi, fh);
391
392  pthread_mutex_lock(&ftpfs.lock);
393
394  DEBUG(2, "buffer size: %zu %lld\n", fh->buf.len, fh->buf.begin_offset);
395
396  if ((fh->buf.len < size + offset - fh->buf.begin_offset) ||
397      offset < fh->buf.begin_offset ||
398      offset > fh->buf.begin_offset + fh->buf.len) {
399    // We can't answer this from cache
400    if (ftpfs.current_fh != fh ||
401        offset < fh->buf.begin_offset ||
402        offset > fh->buf.begin_offset + fh->buf.len ||
403        !check_running()) {
404      DEBUG(1, "We need to restart the connection\n");
405      DEBUG(2, "%p %p\n", ftpfs.current_fh, fh);
406      DEBUG(2, "%lld %lld\n", fh->buf.begin_offset, offset);
407
408      buf_clear(&fh->buf);
409      fh->buf.begin_offset = offset;
410      ftpfs.current_fh = fh;
411
412      curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
413      curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
414      curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &fh->buf);
415      if (offset) {
416        char range[15];
417        snprintf(range, 15, "%lld-", offset);
418        curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_RANGE, range);
419      }
420      curl_multi_add_handle(ftpfs.multi, ftpfs.connection);
421    }
422
423    while(CURLM_CALL_MULTI_PERFORM ==
424        curl_multi_perform(ftpfs.multi, &running_handles));
425
426    curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_RANGE, NULL);
427
428    while ((fh->buf.len < size + offset - fh->buf.begin_offset) &&
429        running_handles) {
430      struct timeval timeout;
431      int rc; /* select() return code */
432
433      fd_set fdread;
434      fd_set fdwrite;
435      fd_set fdexcep;
436      int maxfd;
437
438      FD_ZERO(&fdread);
439      FD_ZERO(&fdwrite);
440      FD_ZERO(&fdexcep);
441
442      /* set a suitable timeout to play around with */
443      timeout.tv_sec = 1;
444      timeout.tv_usec = 0;
445
446      /* get file descriptors from the transfers */
447      curl_multi_fdset(ftpfs.multi, &fdread, &fdwrite, &fdexcep, &maxfd);
448
449      rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
450
451      switch(rc) {
452        case -1:
453          /* select error */
454          break;
455        case 0:
456          /* timeout, do something else */
457          break;
458        default:
459          /* one or more of curl's file descriptors say there's data to read
460             or write */
461          while(CURLM_CALL_MULTI_PERFORM ==
462              curl_multi_perform(ftpfs.multi, &running_handles));
463          break;
464      }
465    }
466
467    if (running_handles == 0) {
468      int msgs_left = 1;
469      while (msgs_left) {
470        CURLMsg* msg = curl_multi_info_read(ftpfs.multi, &msgs_left);
471        if (msg == NULL ||
472            msg->msg != CURLMSG_DONE ||
473            msg->data.result != CURLE_OK) {
474          err = 1;
475        }
476      }
477    }
478  }
479
480  size_t to_copy = fh->buf.len + fh->buf.begin_offset - offset;
481  size = size > to_copy ? to_copy : size;
482  if (rbuf) {
483    memcpy(rbuf, fh->buf.p + offset - fh->buf.begin_offset, size);
484  }
485
486  if (update_offset) {
487    fh->last_offset = offset + size;
488  }
489
490  // Check if the buffer is growing and we can delete a part of it
491  if (fh->can_shrink && fh->buf.len > MAX_BUFFER_LEN) {
492    DEBUG(2, "Shrinking buffer from %zu to %zu bytes\n",
493          fh->buf.len, to_copy - size);
494    memmove(fh->buf.p,
495            fh->buf.p + offset - fh->buf.begin_offset + size,
496            to_copy - size);
497    fh->buf.len = to_copy - size;
498    fh->buf.begin_offset = offset + size;
499  }
500
501  pthread_mutex_unlock(&ftpfs.lock);
502
503  if (err) return CURLFTPFS_BAD_READ;
504  return size;
505}
506
507static int ftpfs_open(const char* path, struct fuse_file_info* fi) {
508  DEBUG(2, "%d\n", fi->flags & O_ACCMODE);
509  int err = 0;
510  char *full_path = get_full_path(path);
511
512  struct ftpfs_file* fh =
513    (struct ftpfs_file*) malloc(sizeof(struct ftpfs_file));
514  buf_init(&fh->buf, 0);
515  fh->dirty = 0;
516  fh->copied = 0;
517  fh->last_offset = 0;
518  fh->can_shrink = 0;
519  fi->fh = (unsigned long) fh;
520
521  if ((fi->flags & O_ACCMODE) == O_RDONLY) {
522    // If it's read-only, we can load the file a bit at a time, as necessary.
523    DEBUG(1, "opening %s O_RDONLY\n", path);
524    fh->can_shrink = 1;
525    size_t size = ftpfs_read_chunk(full_path, NULL, 1, 0, fi, 0);
526
527    if (size == CURLFTPFS_BAD_READ) {
528      err = -EACCES;
529      buf_free(&fh->buf);
530      free(fh);
531    }
532  } else if ((fi->flags & O_ACCMODE) == O_WRONLY ||
533             (fi->flags & O_ACCMODE) == O_RDWR) {
534    // If we want to write to the file, we have to load it all at once,
535    // modify it in memory and then upload it as a whole as most FTP servers
536    // don't support resume for uploads.
537    DEBUG(1, "opening %s O_WRONLY or O_RDWR\n", path);
538    pthread_mutex_lock(&ftpfs.lock);
539    curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
540    curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
541    curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &fh->buf);
542    CURLcode curl_res = curl_easy_perform(ftpfs.connection);
543    pthread_mutex_unlock(&ftpfs.lock);
544
545    if (curl_res != 0) {
546      err = -EACCES;
547      buf_free(&fh->buf);
548      free(fh);
549    }
550  }
551
552  free(full_path);
553  return err;
554}
555
556static int ftpfs_read(const char* path, char* rbuf, size_t size, off_t offset,
557                      struct fuse_file_info* fi) {
558  int ret;
559  char *full_path = get_full_path(path);
560  size_t size_read = ftpfs_read_chunk(full_path, rbuf, size, offset, fi, 1);
561  free(full_path);
562  if (size_read == CURLFTPFS_BAD_READ) {
563    ret = -EIO;
564  } else {
565    ret = size_read;
566  }
567  return ret;
568}
569
570static int ftpfs_mknod(const char* path, mode_t mode, dev_t rdev) {
571  (void) rdev;
572
573  int err = 0;
574
575  if ((mode & S_IFMT) != S_IFREG)
576    return -EPERM;
577
578  char *full_path = get_full_path(path);
579
580  pthread_mutex_lock(&ftpfs.lock);
581  curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
582  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
583  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_INFILESIZE, 0);
584  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_UPLOAD, 1);
585  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_READDATA, NULL);
586  CURLcode curl_res = curl_easy_perform(ftpfs.connection);
587  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_UPLOAD, 0);
588  pthread_mutex_unlock(&ftpfs.lock);
589
590  if (curl_res != 0) {
591    err = -EPERM;
592  }
593
594  free(full_path);
595  return err;
596}
597
598static int ftpfs_chmod(const char* path, mode_t mode) {
599  int err = 0;
600
601  // We can only process a subset of the mode - so strip
602  // to supported subset
603  int mode_c = mode - (mode / 0x1000 * 0x1000);
604 
605  struct curl_slist* header = NULL;
606  char* full_path = get_dir_path(path);
607  char* filename = get_file_name(path);
608  char* cmd = g_strdup_printf("SITE CHMOD %.3o %s", mode_c, filename);
609  struct buffer buf;
610  buf_init(&buf, 0);
611
612  header = curl_slist_append(header, cmd);
613
614  pthread_mutex_lock(&ftpfs.lock);
615  curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
616  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
617  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
618  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
619  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
620  CURLcode curl_res = curl_easy_perform(ftpfs.connection);
621  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
622  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
623  pthread_mutex_unlock(&ftpfs.lock);
624
625  if (curl_res != 0) {
626    err = -EPERM;
627  }
628 
629  buf_free(&buf);
630  curl_slist_free_all(header);
631  free(full_path);
632  free(filename);
633  free(cmd); 
634  return err;
635}
636
637static int ftpfs_chown(const char* path, uid_t uid, gid_t gid) {
638  int err = 0;
639 
640  struct curl_slist* header = NULL;
641  char* full_path = get_dir_path(path);
642  char* filename = get_file_name(path);
643  char* cmd = g_strdup_printf("SITE CHUID %i %s", uid, filename);
644  char* cmd2 = g_strdup_printf("SITE CHGID %i %s", gid, filename);
645  struct buffer buf;
646  buf_init(&buf, 0);
647
648  header = curl_slist_append(header, cmd);
649  header = curl_slist_append(header, cmd2);
650
651  pthread_mutex_lock(&ftpfs.lock);
652  curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
653  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
654  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
655  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
656  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
657  CURLcode curl_res = curl_easy_perform(ftpfs.connection);
658  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
659  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
660  pthread_mutex_unlock(&ftpfs.lock);
661
662  if (curl_res != 0) {
663    err = -EPERM;
664  }
665 
666  buf_free(&buf);
667  curl_slist_free_all(header);
668  free(full_path);
669  free(filename);
670  free(cmd); 
671  free(cmd2); 
672  return err;
673}
674
675static int ftpfs_truncate(const char* path, off_t offset) {
676  DEBUG(1, "ftpfs_truncate: %lld\n", offset);
677  if (offset == 0) return ftpfs_mknod(path, S_IFREG, 0);
678  return 0;
679}
680
681static int ftpfs_utime(const char* path, struct utimbuf* time) {
682  (void) path;
683  (void) time;
684  return 0;
685}
686
687static int ftpfs_rmdir(const char* path) {
688  int err = 0;
689  struct curl_slist* header = NULL;
690  char* full_path = get_dir_path(path);
691  char* filename = get_file_name(path);
692  char* cmd = g_strdup_printf("RMD %s", filename);
693  struct buffer buf;
694  buf_init(&buf, 0);
695
696  DEBUG(2, "%s\n", full_path);
697  DEBUG(2, "%s\n", cmd);
698
699  header = curl_slist_append(header, cmd);
700
701  pthread_mutex_lock(&ftpfs.lock);
702  curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
703  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
704  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
705  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
706  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
707  CURLcode curl_res = curl_easy_perform(ftpfs.connection);
708  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
709  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
710  pthread_mutex_unlock(&ftpfs.lock);
711
712  if (curl_res != 0) {
713    err = -EPERM;
714  }
715 
716  buf_free(&buf);
717  curl_slist_free_all(header);
718  free(full_path);
719  free(filename);
720  free(cmd);
721  return err;
722}
723
724static int ftpfs_mkdir(const char* path, mode_t mode) {
725  (void) mode;
726  int err = 0;
727  struct curl_slist* header = NULL;
728  char* full_path = get_dir_path(path);
729  char* filename = get_file_name(path);
730  char* cmd = g_strdup_printf("MKD %s", filename);
731  struct buffer buf;
732  buf_init(&buf, 0);
733
734  header = curl_slist_append(header, cmd);
735
736  pthread_mutex_lock(&ftpfs.lock);
737  curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
738  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
739  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
740  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
741  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
742  CURLcode curl_res = curl_easy_perform(ftpfs.connection);
743  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
744  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
745  pthread_mutex_unlock(&ftpfs.lock);
746
747  if (curl_res != 0) {
748    err = -EPERM;
749  }
750 
751  buf_free(&buf);
752  curl_slist_free_all(header);
753  free(full_path);
754  free(filename);
755  free(cmd);
756  return err;
757}
758
759static int ftpfs_unlink(const char* path) {
760  int err = 0;
761  struct curl_slist* header = NULL;
762  char* full_path = get_dir_path(path);
763  char* filename = get_file_name(path);
764  char* cmd = g_strdup_printf("DELE %s", filename);
765  struct buffer buf;
766  buf_init(&buf, 0);
767
768  header = curl_slist_append(header, cmd);
769
770  pthread_mutex_lock(&ftpfs.lock);
771  curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
772  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
773  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
774  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
775  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
776  CURLcode curl_res = curl_easy_perform(ftpfs.connection);
777  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
778  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
779  pthread_mutex_unlock(&ftpfs.lock);
780
781  if (curl_res != 0) {
782    err = -EPERM;
783  }
784 
785  buf_free(&buf);
786  curl_slist_free_all(header);
787  free(full_path);
788  free(filename);
789  free(cmd);
790  return err;
791}
792
793static int ftpfs_write(const char *path, const char *wbuf, size_t size,
794                       off_t offset, struct fuse_file_info *fi) {
795  (void) path;
796  struct ftpfs_file* fh = (struct ftpfs_file*) (uintptr_t) fi->fh;
797  DEBUG(1, "ftpfs_write: %zu %lld\n", size, offset);
798  if (offset + size > fh->buf.size) {
799    buf_resize(&fh->buf, offset + size);
800  }
801  while (fh->buf.len < offset + size) {
802    buf_add_mem(&fh->buf, "\0", 1);
803  }
804  memcpy(fh->buf.p + offset, wbuf, size);
805  fh->dirty = 1;
806
807  return size;
808}
809
810static int ftpfs_flush(const char *path, struct fuse_file_info *fi) {
811  struct ftpfs_file* fh = (struct ftpfs_file*) (uintptr_t) fi->fh;
812  if (!fh->dirty) return 0;
813
814  int err = 0;
815  DEBUG(1, "ftpfs_flush: %zu\n", fh->buf.len);
816  char* full_path = get_full_path(path);
817
818  pthread_mutex_lock(&ftpfs.lock);
819  curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
820  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
821  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_INFILESIZE, fh->buf.len);
822  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_UPLOAD, 1);
823  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_READDATA, fh);
824  CURLcode curl_res = curl_easy_perform(ftpfs.connection);
825  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_UPLOAD, 0);
826
827  fh->dirty = 0;
828  pthread_mutex_unlock(&ftpfs.lock);
829
830  if (curl_res != 0) {
831    err = -EPERM;
832  }
833
834  free(full_path);
835  return err;
836}
837
838static int ftpfs_fsync(const char *path, int isdatasync,
839                      struct fuse_file_info *fi) {
840  (void) isdatasync;
841  return ftpfs_flush(path, fi);
842}
843
844static int ftpfs_release(const char* path, struct fuse_file_info* fi) {
845  struct ftpfs_file* fh = (struct ftpfs_file*) (uintptr_t) fi->fh;
846  ftpfs_flush(path, fi);
847  pthread_mutex_lock(&ftpfs.lock);
848  if (ftpfs.current_fh == fh) {
849    ftpfs.current_fh = NULL;
850  }
851  buf_free(&fh->buf);
852  free(fh);
853  pthread_mutex_unlock(&ftpfs.lock);
854  return 0;
855}
856
857
858static int ftpfs_rename(const char* from, const char* to) {
859  DEBUG(1, "ftpfs_rename from %s to %s\n", from, to);
860  int err = 0;
861  char* rnfr = g_strdup_printf("RNFR %s", from + 1);
862  char* rnto = g_strdup_printf("RNTO %s", to + 1);
863  struct buffer buf;
864  buf_init(&buf, 0);
865  struct curl_slist* header = NULL;
866
867  if (ftpfs.codepage) {
868    convert_charsets(ftpfs.iocharset, ftpfs.codepage, &rnfr);
869    convert_charsets(ftpfs.iocharset, ftpfs.codepage, &rnto);
870  }
871
872  header = curl_slist_append(header, rnfr);
873  header = curl_slist_append(header, rnto);
874
875  pthread_mutex_lock(&ftpfs.lock);
876  curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
877  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
878  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, ftpfs.host);
879  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
880  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
881  CURLcode curl_res = curl_easy_perform(ftpfs.connection);
882  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
883  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
884  pthread_mutex_unlock(&ftpfs.lock);
885
886  if (curl_res != 0) {
887    err = -EPERM;
888  }
889 
890  buf_free(&buf);
891  curl_slist_free_all(header);
892  free(rnfr);
893  free(rnto);
894
895  return err;
896}
897
898static int ftpfs_readlink(const char *path, char *linkbuf, size_t size) {
899  int err;
900  CURLcode curl_res;
901  char* dir_path = get_dir_path(path);
902
903  DEBUG(2, "dir_path: %s %s\n", path, dir_path);
904  struct buffer buf;
905  buf_init(&buf, 0);
906
907  pthread_mutex_lock(&ftpfs.lock);
908  curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
909  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, dir_path);
910  curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
911  curl_res = curl_easy_perform(ftpfs.connection);
912  pthread_mutex_unlock(&ftpfs.lock);
913
914  if (curl_res != 0) {
915    DEBUG(1, "%s\n", error_buf);
916  }
917  buf_add_mem(&buf, "\0", 1);
918
919  char* name = strrchr(path, '/');
920  ++name;
921  err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
922                  name, NULL, linkbuf, size, NULL, NULL); 
923
924  free(dir_path);
925  buf_free(&buf);
926  if (err) return -ENOENT;
927  return 0;
928}
929
930#if FUSE_VERSION >= 25
931static int ftpfs_statfs(const char *path, struct statvfs *buf)
932{
933    (void) path;
934
935    buf->f_namemax = 255;
936    buf->f_bsize = ftpfs.blksize;
937    buf->f_frsize = 512;
938    buf->f_blocks = 999999999 * 2;
939    buf->f_bfree =  999999999 * 2;
940    buf->f_bavail = 999999999 * 2;
941    buf->f_files =  999999999;
942    buf->f_ffree =  999999999;
943    return 0;
944}
945#else
946static int ftpfs_statfs(const char *path, struct statfs *buf)
947{
948    (void) path;
949
950    buf->f_namelen = 255;
951    buf->f_bsize = ftpfs.blksize;
952    buf->f_blocks = 999999999 * 2;
953    buf->f_bfree =  999999999 * 2;
954    buf->f_bavail = 999999999 * 2;
955    buf->f_files =  999999999;
956    buf->f_ffree =  999999999;
957    return 0;
958}
959#endif
960
961static struct fuse_cache_operations ftpfs_oper = {
962  .oper = {
963//    .init       = ftpfs_init,
964    .getattr    = ftpfs_getattr,
965    .readlink   = ftpfs_readlink,
966    .mknod      = ftpfs_mknod,
967    .mkdir      = ftpfs_mkdir,
968//    .symlink    = ftpfs_symlink,
969    .unlink     = ftpfs_unlink,
970    .rmdir      = ftpfs_rmdir,
971    .rename     = ftpfs_rename,
972    .chmod      = ftpfs_chmod,
973    .chown      = ftpfs_chown,
974    .truncate   = ftpfs_truncate,
975    .utime      = ftpfs_utime,
976    .open       = ftpfs_open,
977    .flush      = ftpfs_flush,
978    .fsync      = ftpfs_fsync,
979    .release    = ftpfs_release,
980    .read       = ftpfs_read,
981    .write      = ftpfs_write,
982    .statfs     = ftpfs_statfs,
983#if FUSE_VERSION >= 25
984//    .create     = ftpfs_create,
985//    .ftruncate  = ftpfs_ftruncate,
986//    .fgetattr   = ftpfs_fgetattr,
987#endif
988  },
989  .cache_getdir = ftpfs_getdir,
990};
991
992static int ftpfs_opt_proc(void* data, const char* arg, int key,
993                          struct fuse_args* outargs) {
994  (void) data;
995  (void) outargs;
996
997  switch (key) {
998    case FUSE_OPT_KEY_OPT:
999      return 1;
1000    case FUSE_OPT_KEY_NONOPT:
1001      if (!ftpfs.host) {
1002        const char* prefix = "";
1003        if (strncmp(arg, "ftp://", 6) && strncmp(arg, "ftps://", 7)) {
1004          prefix = "ftp://";
1005        }
1006        ftpfs.host = g_strdup_printf("%s%s%s", prefix, arg, 
1007      arg[strlen(arg)-1] == '/' ? "" : "/");
1008        return 0;
1009      } else if (!ftpfs.mountpoint)
1010        ftpfs.mountpoint = strdup(arg);
1011      return 1;
1012    case KEY_HELP:
1013      usage(outargs->argv[0]);
1014      fuse_opt_add_arg(outargs, "-ho");
1015      fuse_main(outargs->argc, outargs->argv, &ftpfs_oper.oper);
1016      exit(1);
1017    case KEY_VERBOSE:
1018      ftpfs.verbose = 1;
1019      return 0;
1020    case KEY_VERSION:
1021      fprintf(stderr, "curlftpfs %s libcurl/%s fuse/%u.%u\n",
1022              VERSION,
1023              ftpfs.curl_version->version,
1024              FUSE_MAJOR_VERSION,
1025              FUSE_MINOR_VERSION);
1026      exit(1);
1027    default:
1028      exit(1);
1029  }
1030}
1031
1032static void usage(const char* progname) {
1033  fprintf(stderr,
1034"usage: %s <ftphost> <mountpoint>\n"
1035"\n"
1036"CurlFtpFS options:\n"
1037"    -o opt,[opt...]        ftp options\n"
1038"    -v   --verbose         make libcurl print verbose debug\n"
1039"    -h   --help            print help\n"
1040"    -V   --version         print version\n"
1041"\n"
1042"FTP options:\n"
1043"    ftpfs_debug         print some debugging information\n"
1044"    transform_symlinks  prepend mountpoint to absolute symlink targets\n"
1045"    disable_epsv        use PASV, without trying EPSV first\n"
1046"    skip_pasv_ip        skip the IP address for PASV\n"
1047"    ftp_port=STR        use PORT with address instead of PASV\n"
1048"    disable_eprt        use PORT, without trying EPRT first\n"
1049"    tcp_nodelay         use the TCP_NODELAY option\n"
1050"    connect_timeout=N   maximum time allowed for connection in seconds\n"
1051"    ssl                 enable SSL/TLS for both control and data connections\n"
1052"    ssl_control         enable SSL/TLS only for control connection\n"
1053"    ssl_try             try SSL/TLS first but connect anyway\n"
1054"    no_verify_hostname  does not verify the hostname (SSL)\n"
1055"    no_verify_peer      does not verify the peer (SSL)\n"
1056"    cert=STR            client certificate file (SSL)\n"
1057"    cert_type=STR       certificate file type (DER/PEM/ENG) (SSL)\n"
1058"    key=STR             private key file name (SSL)\n"
1059"    key_type=STR        private key file type (DER/PEM/ENG) (SSL)\n"
1060"    pass=STR            pass phrase for the private key (SSL)\n"
1061"    engine=STR          crypto engine to use (SSL)\n"
1062"    cacert=STR          file with CA certificates to verify the peer (SSL)\n"
1063"    capath=STR          CA directory to verify peer against (SSL)\n"
1064"    ciphers=STR         SSL ciphers to use (SSL)\n"
1065"    interface=STR       specify network interface/address to use\n"
1066"    krb4=STR            enable krb4 with specified security level\n"
1067"    proxy=STR           use host:port HTTP proxy\n"
1068"    proxytunnel         operate through a HTTP proxy tunnel (using CONNECT)\n"
1069"    proxy_anyauth       pick \"any\" proxy authentication method\n"
1070"    proxy_basic         use Basic authentication on the proxy\n"
1071"    proxy_digest        use Digest authentication on the proxy\n"
1072"    proxy_ntlm          use NTLM authentication on the proxy\n"
1073"    httpproxy           use a HTTP proxy (default)\n"
1074"    socks4              use a SOCKS4 proxy\n"
1075"    socks5              use a SOCKS5 proxy\n"
1076"    user=STR            set server user and password\n"
1077"    proxy_user=STR      set proxy user and password\n"
1078"    tlsv1               use TLSv1 (SSL)\n"
1079"    sslv3               use SSLv3 (SSL)\n"
1080"    ipv4                resolve name to IPv4 address\n"
1081"    ipv6                resolve name to IPv6 address\n"
1082"    utf8                try to transfer file list with utf-8 encoding\n"
1083"    codepage=STR        set the codepage the server uses\n"
1084"    iocharset=STR       set the charset used by the client\n"
1085"\n", progname);
1086}
1087
1088static void set_common_curl_stuff(CURL* easy) {
1089  curl_easy_setopt_or_die(easy, CURLOPT_WRITEFUNCTION, read_data);
1090  curl_easy_setopt_or_die(easy, CURLOPT_READFUNCTION, write_data);
1091  curl_easy_setopt_or_die(easy, CURLOPT_ERRORBUFFER, error_buf);
1092  curl_easy_setopt_or_die(easy, CURLOPT_URL, ftpfs.host);
1093  curl_easy_setopt_or_die(easy, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
1094  curl_easy_setopt_or_die(easy, CURLOPT_NOSIGNAL, 1);
1095  curl_easy_setopt_or_die(easy, CURLOPT_CUSTOMREQUEST, "LIST -a");
1096
1097  if (ftpfs.tryutf8) {
1098    // We'll let the slist leak, as it will still be accessible within
1099    // libcurl. If we ever want to add more commands to CURLOPT_QUOTE, we'll
1100    // have to think of a better strategy.
1101    struct curl_slist *slist = NULL;
1102
1103    // Adding the QUOTE here will make this command be sent with every request.
1104    // This is necessary to ensure that the server is still in UTF8 mode after
1105    // we get disconnected and automatically reconnect.
1106    slist = curl_slist_append(slist, "OPTS UTF8 ON");
1107    curl_easy_setopt_or_die(easy, CURLOPT_QUOTE, slist);
1108  }
1109
1110  if (ftpfs.verbose) {
1111    curl_easy_setopt_or_die(easy, CURLOPT_VERBOSE, TRUE);
1112  }
1113
1114  if (ftpfs.disable_epsv) {
1115    curl_easy_setopt_or_die(easy, CURLOPT_FTP_USE_EPSV, FALSE);
1116  }
1117
1118  if (ftpfs.skip_pasv_ip) {
1119#ifdef CURLOPT_FTP_SKIP_PASV_IP
1120    curl_easy_setopt_or_die(easy, CURLOPT_FTP_SKIP_PASV_IP, TRUE);
1121#endif
1122  }
1123
1124  if (ftpfs.ftp_port) {
1125    curl_easy_setopt_or_die(easy, CURLOPT_FTPPORT, ftpfs.ftp_port);
1126  }
1127
1128  if (ftpfs.disable_eprt) {
1129    curl_easy_setopt_or_die(easy, CURLOPT_FTP_USE_EPRT, FALSE);
1130  }
1131
1132  if (ftpfs.tcp_nodelay) {
1133#ifdef CURLOPT_TCP_NODELAY
1134    /* CURLOPT_TCP_NODELAY is not defined in older versions */
1135    curl_easy_setopt_or_die(easy, CURLOPT_TCP_NODELAY, 1);
1136#endif
1137  }
1138
1139  curl_easy_setopt_or_die(easy, CURLOPT_CONNECTTIMEOUT, ftpfs.connect_timeout);
1140
1141  /* CURLFTPSSL_CONTROL and CURLFTPSSL_ALL should make the connection fail if
1142   * the server doesn't support SSL but libcurl only honors this beginning
1143   * with version 7.15.4 */
1144  if (ftpfs.use_ssl > CURLFTPSSL_TRY &&
1145      ftpfs.curl_version->version_num <= CURLFTPFS_BAD_SSL) {
1146    fprintf(stderr,
1147"WARNING: you are using libcurl %s.\n"
1148"This version of libcurl does not respect the mandatory SSL flag.\n" 
1149"It will try to send the user and password even if the server doesn't support\n"
1150"SSL. Please upgrade to libcurl version 7.15.4 or higher.\n"
1151"You can abort the connection now by pressing ctrl+c.\n",
1152            ftpfs.curl_version->version);
1153    int i;
1154    const int time_to_wait = 10;
1155    for (i = 0; i < time_to_wait; i++) {
1156      fprintf(stderr, "%d.. ", time_to_wait - i);
1157      sleep(1);
1158    }
1159    fprintf(stderr, "\n");
1160  }
1161  curl_easy_setopt_or_die(easy, CURLOPT_FTP_SSL, ftpfs.use_ssl);
1162
1163  curl_easy_setopt_or_die(easy, CURLOPT_SSLCERT, ftpfs.cert);
1164  curl_easy_setopt_or_die(easy, CURLOPT_SSLCERTTYPE, ftpfs.cert_type);
1165  curl_easy_setopt_or_die(easy, CURLOPT_SSLKEY, ftpfs.key);
1166  curl_easy_setopt_or_die(easy, CURLOPT_SSLKEYTYPE, ftpfs.key_type);
1167  curl_easy_setopt_or_die(easy, CURLOPT_SSLKEYPASSWD, ftpfs.key_password);
1168
1169  if (ftpfs.engine) {
1170    curl_easy_setopt_or_die(easy, CURLOPT_SSLENGINE, ftpfs.engine);
1171    curl_easy_setopt_or_die(easy, CURLOPT_SSLENGINE_DEFAULT, 1);
1172  }
1173
1174  curl_easy_setopt_or_die(easy, CURLOPT_SSL_VERIFYPEER, TRUE);
1175  if (ftpfs.no_verify_peer) {
1176    curl_easy_setopt_or_die(easy, CURLOPT_SSL_VERIFYPEER, FALSE);
1177  }
1178
1179  if (ftpfs.cacert || ftpfs.capath) {
1180    if (ftpfs.cacert) {
1181      curl_easy_setopt_or_die(easy, CURLOPT_CAINFO, ftpfs.cacert);
1182    }
1183    if (ftpfs.capath) {
1184      curl_easy_setopt_or_die(easy, CURLOPT_CAPATH, ftpfs.capath);
1185    }
1186  }
1187
1188  if (ftpfs.ciphers) {
1189    curl_easy_setopt_or_die(easy, CURLOPT_SSL_CIPHER_LIST, ftpfs.ciphers);
1190  }
1191
1192  if (ftpfs.no_verify_hostname) {
1193    /* The default is 2 which verifies even the host string. This sets to 1
1194     * which means verify the host but not the string. */
1195    curl_easy_setopt_or_die(easy, CURLOPT_SSL_VERIFYHOST, 1);
1196  }
1197
1198  curl_easy_setopt_or_die(easy, CURLOPT_INTERFACE, ftpfs.interface);
1199  curl_easy_setopt_or_die(easy, CURLOPT_KRB4LEVEL, ftpfs.krb4);
1200 
1201  if (ftpfs.proxy) {
1202    curl_easy_setopt_or_die(easy, CURLOPT_PROXY, ftpfs.proxy);
1203  }
1204
1205  /* The default proxy type is HTTP */
1206  if (!ftpfs.proxytype) {
1207    ftpfs.proxytype = CURLPROXY_HTTP;
1208  }
1209  curl_easy_setopt_or_die(easy, CURLOPT_PROXYTYPE, ftpfs.proxytype);
1210 
1211  /* Connection to FTP servers only make sense with a HTTP tunnel proxy */
1212  if (ftpfs.proxytype == CURLPROXY_HTTP || ftpfs.proxytunnel) {
1213    curl_easy_setopt_or_die(easy, CURLOPT_HTTPPROXYTUNNEL, TRUE);
1214  }
1215
1216  if (ftpfs.proxyanyauth) {
1217    curl_easy_setopt_or_die(easy, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
1218  } else if (ftpfs.proxyntlm) {
1219    curl_easy_setopt_or_die(easy, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
1220  } else if (ftpfs.proxydigest) {
1221    curl_easy_setopt_or_die(easy, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
1222  } else if (ftpfs.proxybasic) {
1223    curl_easy_setopt_or_die(easy, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
1224  }
1225
1226  curl_easy_setopt_or_die(easy, CURLOPT_USERPWD, ftpfs.user);
1227  curl_easy_setopt_or_die(easy, CURLOPT_PROXYUSERPWD, ftpfs.proxy_user);
1228  curl_easy_setopt_or_die(easy, CURLOPT_SSLVERSION, ftpfs.ssl_version);
1229  curl_easy_setopt_or_die(easy, CURLOPT_IPRESOLVE, ftpfs.ip_version);
1230}
1231
1232static void checkpasswd(const char *kind, /* for what purpose */
1233                        char **userpwd) /* pointer to allocated string */
1234{
1235  char *ptr;
1236  if(!*userpwd)
1237    return;
1238
1239  ptr = strchr(*userpwd, ':');
1240  if(!ptr) {
1241    /* no password present, prompt for one */
1242    char *passwd;
1243    char prompt[256];
1244    size_t passwdlen;
1245    size_t userlen = strlen(*userpwd);
1246    char *passptr;
1247
1248    /* build a nice-looking prompt */
1249    snprintf(prompt, sizeof(prompt),
1250        "Enter %s password for user '%s':",
1251        kind, *userpwd);
1252
1253    /* get password */
1254    passwd = getpass(prompt);
1255    passwdlen = strlen(passwd);
1256
1257    /* extend the allocated memory area to fit the password too */
1258    passptr = realloc(*userpwd,
1259        passwdlen + 1 + /* an extra for the colon */
1260        userlen + 1);   /* an extra for the zero */
1261
1262    if(passptr) {
1263      /* append the password separated with a colon */
1264      passptr[userlen]=':';
1265      memcpy(&passptr[userlen+1], passwd, passwdlen+1);
1266      *userpwd = passptr;
1267    }
1268  }
1269}
1270
1271int main(int argc, char** argv) {
1272  int res;
1273  struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1274  CURLcode curl_res;
1275  CURL* easy;
1276
1277  // Initialize curl library before we are a multithreaded program
1278  curl_global_init(CURL_GLOBAL_ALL);
1279 
1280  memset(&ftpfs, 0, sizeof(ftpfs));
1281
1282  ftpfs.curl_version = curl_version_info(CURLVERSION_NOW);
1283  ftpfs.safe_nobody = ftpfs.curl_version->version_num > CURLFTPFS_BAD_NOBODY;
1284 
1285  ftpfs.blksize = 4096;
1286 
1287  if (fuse_opt_parse(&args, &ftpfs, ftpfs_opts, ftpfs_opt_proc) == -1)
1288    exit(1);
1289
1290  if (!ftpfs.host) {
1291    fprintf(stderr, "missing host\n");
1292    fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
1293    exit(1);
1294  }
1295
1296  if (!ftpfs.iocharset) {
1297    ftpfs.iocharset = "UTF8";
1298  }
1299
1300  if (ftpfs.codepage) {
1301    convert_charsets(ftpfs.iocharset, ftpfs.codepage, &ftpfs.host);
1302  }
1303
1304  easy = curl_easy_init();
1305  if (easy == NULL) {
1306    fprintf(stderr, "Error initializing libcurl\n");
1307    exit(1);
1308  }
1309
1310  res = cache_parse_options(&args);
1311  if (res == -1)
1312    exit(1);
1313
1314  checkpasswd("host", &ftpfs.user);
1315  checkpasswd("proxy", &ftpfs.proxy_user);
1316
1317  if (ftpfs.transform_symlinks && !ftpfs.mountpoint) {
1318    fprintf(stderr, "cannot transform symlinks: no mountpoint given\n");
1319    exit(1);
1320  }
1321  if (!ftpfs.transform_symlinks)
1322    ftpfs.symlink_prefix_len = 0;
1323  else if (realpath(ftpfs.mountpoint, ftpfs.symlink_prefix) != NULL)
1324    ftpfs.symlink_prefix_len = strlen(ftpfs.symlink_prefix);
1325  else {
1326    perror("unable to normalize mount path");
1327    exit(1);
1328  }
1329
1330  set_common_curl_stuff(easy);
1331  curl_easy_setopt_or_die(easy, CURLOPT_WRITEDATA, NULL);
1332  curl_easy_setopt_or_die(easy, CURLOPT_NOBODY, ftpfs.safe_nobody);
1333  curl_res = curl_easy_perform(easy);
1334  if (curl_res != 0) {
1335    fprintf(stderr, "Error connecting to ftp: %s\n", error_buf);
1336    exit(1);
1337  }
1338  curl_easy_setopt_or_die(easy, CURLOPT_NOBODY, 0);
1339
1340  ftpfs.multi = curl_multi_init();
1341  if (ftpfs.multi == NULL) {
1342    fprintf(stderr, "Error initializing libcurl multi\n");
1343    exit(1);
1344  }
1345
1346  ftpfs.connection = easy;
1347  pthread_mutex_init(&ftpfs.lock, NULL);
1348
1349  res = fuse_main(args.argc, args.argv, cache_init(&ftpfs_oper));
1350
1351  curl_multi_remove_handle(ftpfs.multi, easy);
1352  curl_easy_cleanup(easy);
1353  curl_multi_cleanup(ftpfs.multi);
1354  curl_global_cleanup();
1355
1356  return res;
1357}
Note: See TracBrowser for help on using the repository browser.