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 #include <semaphore.h>
24 #include <assert.h>
25 
26 #include "charset_utils.h"
27 #include "path_utils.h"
28 #include "ftpfs-ls.h"
29 #include "cache.h"
30 #include "ftpfs.h"
31 
32 #define CURLFTPFS_BAD_NOBODY 0x070f02
33 #define CURLFTPFS_BAD_SSL 0x070f03
34 
35 #define CURLFTPFS_BAD_READ ((size_t)-1)
36 
37 #define MAX_BUFFER_LEN (300*1024)
38 
39 struct ftpfs ftpfs;
40 static char error_buf[CURL_ERROR_SIZE];
41 
42 struct buffer {
43   uint8_t* p;
44   size_t len;
45   size_t size;
46   off_t begin_offset;
47 };
48 
49 static void usage(const char* progname);
50 
51 static void buf_init(struct buffer* buf)
52 {
53     buf->p = NULL;
54     buf->begin_offset = 0;
55     buf->len = 0;
56     buf->size = 0;
57 }
58 
59 static inline void buf_free(struct buffer* buf)
60 {
61     free(buf->p);
62 }
63 
64 static inline void buf_clear(struct buffer *buf)
65 {
66     buf_free(buf);
67     buf_init(buf);
68 }
69 
70 static int buf_resize(struct buffer *buf, size_t len)
71 {
72     buf->size = (buf->len + len + 63) & ~31;
73     buf->p = (uint8_t *) realloc(buf->p, buf->size);
74     if (!buf->p) {
75         fprintf(stderr, "ftpfs: memory allocation failed\n");
76         return -1;
77     }
78     return 0;
79 }
80 
81 static int buf_add_mem(struct buffer *buf, const void *data, size_t len)
82 {
83     if (buf->len + len > buf->size && buf_resize(buf, len) == -1)
84         return -1;
85 
86     memcpy(buf->p + buf->len, data, len);
87     buf->len += len;
88     return 0;
89 }
90 
91 static void buf_null_terminate(struct buffer *buf)
92 {
93     if (buf_add_mem(buf, "\0", 1) == -1)
94         exit(1);
95 }
96 
97 struct ftpfs_file {
98   struct buffer buf;
99   int dirty;
100   int copied;
101   off_t last_offset;
102   int can_shrink;
103   pthread_t thread_id;
104   mode_t mode;
105   char * open_path;
106   char * full_path;
107   struct buffer stream_buf;
108   CURL *write_conn;
109   sem_t data_avail;
110   sem_t data_need;
111   sem_t data_written;
112   sem_t ready;
113   int isready;
114   int eof;
115   int written_flag;
116   int write_fail_cause;
117   int write_may_start;
118   char curl_error_buffer[CURL_ERROR_SIZE];
119   off_t pos;
120 };
121 
122 enum {
123   KEY_HELP,
124   KEY_VERBOSE,
125   KEY_VERSION,
126 };
127 
128 #define FTPFS_OPT(t, p, v) { t, offsetof(struct ftpfs, p), v }
129 
130 static struct fuse_opt ftpfs_opts[] = {
131   FTPFS_OPT("ftpfs_debug=%u",     debug, 0),
132   FTPFS_OPT("transform_symlinks", transform_symlinks, 1),
133   FTPFS_OPT("disable_epsv",       disable_epsv, 1),
134   FTPFS_OPT("enable_epsv",        disable_epsv, 0),
135   FTPFS_OPT("skip_pasv_ip",       skip_pasv_ip, 1),
136   FTPFS_OPT("ftp_port=%s",        ftp_port, 0),
137   FTPFS_OPT("disable_eprt",       disable_eprt, 1),
138   FTPFS_OPT("ftp_method=%s",      ftp_method, 0),
139   FTPFS_OPT("custom_list=%s",     custom_list, 0),
140   FTPFS_OPT("tcp_nodelay",        tcp_nodelay, 1),
141   FTPFS_OPT("connect_timeout=%u", connect_timeout, 0),
142   FTPFS_OPT("ssl",                use_ssl, CURLFTPSSL_ALL),
143   FTPFS_OPT("ssl_control",        use_ssl, CURLFTPSSL_CONTROL),
144   FTPFS_OPT("ssl_try",            use_ssl, CURLFTPSSL_TRY),
145   FTPFS_OPT("no_verify_hostname", no_verify_hostname, 1),
146   FTPFS_OPT("no_verify_peer",     no_verify_peer, 1),
147   FTPFS_OPT("cert=%s",            cert, 0),
148   FTPFS_OPT("cert_type=%s",       cert_type, 0),
149   FTPFS_OPT("key=%s",             key, 0),
150   FTPFS_OPT("key_type=%s",        key_type, 0),
151   FTPFS_OPT("pass=%s",            key_password, 0),
152   FTPFS_OPT("engine=%s",          engine, 0),
153   FTPFS_OPT("cacert=%s",          cacert, 0),
154   FTPFS_OPT("capath=%s",          capath, 0),
155   FTPFS_OPT("ciphers=%s",         ciphers, 0),
156   FTPFS_OPT("interface=%s",       interface, 0),
157   FTPFS_OPT("krb4=%s",            krb4, 0),
158   FTPFS_OPT("proxy=%s",           proxy, 0),
159   FTPFS_OPT("proxytunnel",        proxytunnel, 1),
160   FTPFS_OPT("proxy_anyauth",      proxyanyauth, 1),
161   FTPFS_OPT("proxy_basic",        proxybasic, 1),
162   FTPFS_OPT("proxy_digest",       proxydigest, 1),
163   FTPFS_OPT("proxy_ntlm",         proxyntlm, 1),
164   FTPFS_OPT("httpproxy",          proxytype, CURLPROXY_HTTP),
165   FTPFS_OPT("socks4",             proxytype, CURLPROXY_SOCKS4),
166   FTPFS_OPT("socks5",             proxytype, CURLPROXY_SOCKS5),
167   FTPFS_OPT("user=%s",            user, 0),
168   FTPFS_OPT("proxy_user=%s",      proxy_user, 0),
169   FTPFS_OPT("tlsv1",              ssl_version, CURL_SSLVERSION_TLSv1),
170   FTPFS_OPT("sslv3",              ssl_version, CURL_SSLVERSION_SSLv3),
171   FTPFS_OPT("ipv4",               ip_version, CURL_IPRESOLVE_V4),
172   FTPFS_OPT("ipv6",               ip_version, CURL_IPRESOLVE_V6),
173   FTPFS_OPT("utf8",               tryutf8, 1),
174   FTPFS_OPT("codepage=%s",        codepage, 0),
175   FTPFS_OPT("iocharset=%s",       iocharset, 0),
176   FTPFS_OPT("nomulticonn",        multiconn, 0),
177 
178   FUSE_OPT_KEY("-h",             KEY_HELP),
179   FUSE_OPT_KEY("--help",         KEY_HELP),
180   FUSE_OPT_KEY("-v",             KEY_VERBOSE),
181   FUSE_OPT_KEY("--verbose",      KEY_VERBOSE),
182   FUSE_OPT_KEY("-V",             KEY_VERSION),
183   FUSE_OPT_KEY("--version",      KEY_VERSION),
184   FUSE_OPT_END
185 };
186 
187 static struct ftpfs_file *get_ftpfs_file(struct fuse_file_info *fi)
188 {
189   return (struct ftpfs_file *) (uintptr_t) fi->fh;
190 }
191 
192 static void cancel_previous_multi()
193 {
194   //curl_multi_cleanup(ftpfs.multi);
195 
196   if (!ftpfs.attached_to_multi) return;
197 
198   DEBUG(1, "cancel previous multi\n");
199 
200   CURLMcode curlMCode = curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
201   if (curlMCode != CURLE_OK)
202   {
203       fprintf(stderr, "curl_multi_remove_handle problem: %d\n", curlMCode);
204       exit(1);
205   }
206   ftpfs.attached_to_multi = 0;
207 }
208 
209 static int op_return(int err, char * operation)
210 {
211 	if(!err)
212 	{
213 		DEBUG(2, "%s successful\n", operation);
214 		return 0;
215 	}
216 	fprintf(stderr, "ftpfs: operation %s failed because %s\n", operation, strerror(-err));
217 	return err;
218 }
219 
220 
221 static size_t write_data(void *ptr, size_t size, size_t nmemb, void *data) {
222   struct ftpfs_file* fh = (struct ftpfs_file*)data;
223   if (fh == NULL) return 0;
224   size_t to_copy = size * nmemb;
225   if (to_copy > fh->buf.len - fh->copied) {
226     to_copy = fh->buf.len - fh->copied;
227   }
228   DEBUG(2, "write_data: %zu\n", to_copy);
229   DEBUG(3, "%*s\n", (int)to_copy, (char*)ptr);
230   memcpy(ptr, fh->buf.p + fh->copied, to_copy);
231   fh->copied += to_copy;
232   return to_copy;
233 }
234 
235 static size_t read_data(void *ptr, size_t size, size_t nmemb, void *data) {
236   struct buffer* buf = (struct buffer*)data;
237   if (buf == NULL) return size * nmemb;
238   if (buf_add_mem(buf, ptr, size * nmemb) == -1)
239     return 0;
240 
241   DEBUG(2, "read_data: %zu\n", size * nmemb);
242   DEBUG(3, "%*s\n", (int)(size * nmemb), (char*)ptr);
243   return size * nmemb;
244 }
245 
246 #define curl_easy_setopt_or_die(handle, option, ...) \
247   do {\
248     CURLcode res = curl_easy_setopt(handle, option, __VA_ARGS__);\
249     if (res != CURLE_OK) {\
250       fprintf(stderr, "Error setting curl: %s\n", error_buf);\
251       exit(1);\
252     }\
253   }while(0)
254 
255 static int ftpfs_getdir(const char* path, fuse_cache_dirh_t h,
256                         fuse_cache_dirfil_t filler) {
257   int err = 0;
258   CURLcode curl_res;
259   char* dir_path = get_fulldir_path(path);
260   char* dir_path_uri = path_to_uri(dir_path);
261 
262   DEBUG(1, "ftpfs_getdir: %s\n", dir_path);
263   struct buffer buf;
264   buf_init(&buf);
265 
266   pthread_mutex_lock(&ftpfs.lock);
267   cancel_previous_multi();
268   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, dir_path_uri);
269   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
270   curl_res = curl_easy_perform(ftpfs.connection);
271   pthread_mutex_unlock(&ftpfs.lock);
272 
273   if (curl_res != 0) {
274     DEBUG(1, "%s\n", error_buf);
275     err = -EIO;
276   } else {
277     buf_null_terminate(&buf);
278     parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
279               NULL, NULL, NULL, 0, h, filler);
280   }
281 
282   free_uri(dir_path_uri);
283   free(dir_path);
284   buf_free(&buf);
285   return op_return(err, "ftpfs_getdir");
286 }
287 
288 static int ftpfs_getattr(const char* path, struct stat* sbuf) {
289   int err;
290   CURLcode curl_res;
291   char* dir_path = get_dir_path(path);
292   char* dir_path_uri = path_to_uri(dir_path);
293 
294   DEBUG(2, "ftpfs_getattr: %s dir_path=%s\n", path, dir_path);
295   struct buffer buf;
296   buf_init(&buf);
297 
298   pthread_mutex_lock(&ftpfs.lock);
299   cancel_previous_multi();
300   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, dir_path_uri);
301   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
302   curl_res = curl_easy_perform(ftpfs.connection);
303   pthread_mutex_unlock(&ftpfs.lock);
304 
305   if (curl_res != 0) {
306     DEBUG(1, "%s\n", error_buf);
307   }
308   buf_null_terminate(&buf);
309 
310   char* name = strrchr(path, '/');
311   ++name;
312   err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
313                   name, sbuf, NULL, 0, NULL, NULL);
314 
315   free_uri(dir_path_uri);
316   free(dir_path);
317   buf_free(&buf);
318   if (err) return op_return(-ENOENT, "ftpfs_getattr");
319   return 0;
320 }
321 
322 
323 static int check_running() {
324   int running_handles = 0;
325   curl_multi_perform(ftpfs.multi, &running_handles);
326   return running_handles;
327 }
328 
329 static size_t ftpfs_read_chunk(const char* full_path, char* rbuf,
330                                size_t size, off_t offset,
331                                struct fuse_file_info* fi,
332                                int update_offset) {
333   int running_handles = 0;
334   int err = 0;
335   struct ftpfs_file* fh = get_ftpfs_file(fi);
336   char* full_path_uri = path_to_uri(full_path); /* TODO: optimize bu pushing up conversion to context */
337 
338   DEBUG(2, "ftpfs_read_chunk: %s %p %zu %lld %p %p\n",
339         full_path, rbuf, size, offset, fi, fh);
340 
341   pthread_mutex_lock(&ftpfs.lock);
342 
343   DEBUG(2, "buffer size: %zu %lld\n", fh->buf.len, fh->buf.begin_offset);
344 
345   if ((fh->buf.len < size + offset - fh->buf.begin_offset) ||
346       offset < fh->buf.begin_offset ||
347       offset > fh->buf.begin_offset + fh->buf.len) {
348     // We can't answer this from cache
349     if (ftpfs.current_fh != fh ||
350         offset < fh->buf.begin_offset ||
351         offset > fh->buf.begin_offset + fh->buf.len ||
352         !check_running()) {
353       DEBUG(1, "We need to restart the connection %p\n", ftpfs.connection);
354       DEBUG(2, "current_fh=%p fh=%p\n", ftpfs.current_fh, fh);
355       DEBUG(2, "buf.begin_offset=%lld offset=%lld\n", fh->buf.begin_offset, offset);
356 
357       buf_clear(&fh->buf);
358       fh->buf.begin_offset = offset;
359       ftpfs.current_fh = fh;
360 
361       cancel_previous_multi();
362 
363       curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path_uri);
364       curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &fh->buf);
365       if (offset) {
366         char range[15];
367         snprintf(range, 15, "%lld-", (long long) offset);
368         curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_RANGE, range);
369       }
370 
371       CURLMcode curlMCode =  curl_multi_add_handle(ftpfs.multi, ftpfs.connection);
372       if (curlMCode != CURLE_OK)
373       {
374           fprintf(stderr, "curl_multi_add_handle problem: %d\n", curlMCode);
375           exit(1);
376       }
377       ftpfs.attached_to_multi = 1;
378     }
379 
380     while(CURLM_CALL_MULTI_PERFORM ==
381         curl_multi_perform(ftpfs.multi, &running_handles));
382 
383     curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_RANGE, NULL);
384 
385     while ((fh->buf.len < size + offset - fh->buf.begin_offset) &&
386         running_handles) {
387       struct timeval timeout;
388       int rc; /* select() return code */
389 
390       fd_set fdread;
391       fd_set fdwrite;
392       fd_set fdexcep;
393       int maxfd;
394 
395       FD_ZERO(&fdread);
396       FD_ZERO(&fdwrite);
397       FD_ZERO(&fdexcep);
398 
399       /* set a suitable timeout to play around with */
400       timeout.tv_sec = 1;
401       timeout.tv_usec = 0;
402 
403       /* get file descriptors from the transfers */
404       curl_multi_fdset(ftpfs.multi, &fdread, &fdwrite, &fdexcep, &maxfd);
405 
406       rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
407       if (rc == -1) {
408           err = 1;
409           break;
410       }
411       while(CURLM_CALL_MULTI_PERFORM ==
412             curl_multi_perform(ftpfs.multi, &running_handles));
413     }
414 
415     if (running_handles == 0) {
416       int msgs_left = 1;
417       while (msgs_left) {
418         CURLMsg* msg = curl_multi_info_read(ftpfs.multi, &msgs_left);
419         if (msg == NULL ||
420             msg->msg != CURLMSG_DONE ||
421             msg->data.result != CURLE_OK) {
422           DEBUG(1, "error: curl_multi_info %d\n", msg->msg);
423           err = 1;
424         }
425       }
426     }
427   }
428 
429   size_t to_copy = fh->buf.len + fh->buf.begin_offset - offset;
430   size = size > to_copy ? to_copy : size;
431   if (rbuf) {
432     memcpy(rbuf, fh->buf.p + offset - fh->buf.begin_offset, size);
433   }
434 
435   if (update_offset) {
436     fh->last_offset = offset + size;
437   }
438 
439   // Check if the buffer is growing and we can delete a part of it
440   if (fh->can_shrink && fh->buf.len > MAX_BUFFER_LEN) {
441     DEBUG(2, "Shrinking buffer from %zu to %zu bytes\n",
442           fh->buf.len, to_copy - size);
443     memmove(fh->buf.p,
444             fh->buf.p + offset - fh->buf.begin_offset + size,
445             to_copy - size);
446     fh->buf.len = to_copy - size;
447     fh->buf.begin_offset = offset + size;
448   }
449 
450   pthread_mutex_unlock(&ftpfs.lock);
451 
452   free_uri(full_path_uri);
453   if (err) return CURLFTPFS_BAD_READ;
454   return size;
455 }
456 
457 static void set_common_curl_stuff(CURL* easy);
458 
459 static size_t write_data_bg(void *ptr, size_t size, size_t nmemb, void *data) {
460   struct ftpfs_file *fh = data;
461   unsigned to_copy = size * nmemb;
462 
463   if (!fh->isready) {
464     sem_post(&fh->ready);
465     fh->isready = 1;
466   }
467 
468   if (fh->stream_buf.len == 0 && fh->written_flag) {
469     sem_post(&fh->data_written); /* ftpfs_write can return */
470   }
471 
472   sem_wait(&fh->data_avail);
473 
474   DEBUG(2, "write_data_bg: data_avail eof=%d\n", fh->eof);
475 
476   if (fh->eof)
477     return 0;
478 
479   DEBUG(2, "write_data_bg: %d %zd\n", to_copy, fh->stream_buf.len);
480   if (to_copy > fh->stream_buf.len)
481     to_copy = fh->stream_buf.len;
482 
483   memcpy(ptr, fh->stream_buf.p, to_copy);
484   if (fh->stream_buf.len > to_copy) {
485     size_t newlen = fh->stream_buf.len - to_copy;
486     memmove(fh->stream_buf.p, fh->stream_buf.p + to_copy, newlen);
487     fh->stream_buf.len = newlen;
488     sem_post(&fh->data_avail);
489     DEBUG(2, "write_data_bg: data_avail\n");
490 
491   } else {
492     fh->stream_buf.len = 0;
493     fh->written_flag = 1;
494     sem_post(&fh->data_need);
495     DEBUG(2, "write_data_bg: data_need\n");
496   }
497 
498   return to_copy;
499 }
500 
501 int write_thread_ctr = 0;
502 
503 static void *ftpfs_write_thread(void *data) {
504   struct ftpfs_file *fh = data;
505   char range[15];
506   char* full_path_uri = path_to_uri(fh->full_path); /* TODO: optimize bu pushing up conversion to context */
507 
508   DEBUG(2, "enter streaming write thread #%d path=%s pos=%lld\n", ++write_thread_ctr, fh->full_path, fh->pos);
509 
510 
511   curl_easy_setopt_or_die(fh->write_conn, CURLOPT_URL, full_path_uri);
512   curl_easy_setopt_or_die(fh->write_conn, CURLOPT_UPLOAD, 1);
513   curl_easy_setopt_or_die(fh->write_conn, CURLOPT_INFILESIZE, -1);
514   curl_easy_setopt_or_die(fh->write_conn, CURLOPT_READFUNCTION, write_data_bg);
515   curl_easy_setopt_or_die(fh->write_conn, CURLOPT_READDATA, fh);
516   curl_easy_setopt_or_die(fh->write_conn, CURLOPT_LOW_SPEED_LIMIT, 1);
517   curl_easy_setopt_or_die(fh->write_conn, CURLOPT_LOW_SPEED_TIME, 60);
518 
519   fh->curl_error_buffer[0] = '\0';
520   curl_easy_setopt_or_die(fh->write_conn, CURLOPT_ERRORBUFFER, fh->curl_error_buffer);
521 
522   if (fh->pos > 0) {
523     /* resuming a streaming write */
524     //snprintf(range, 15, "%lld-", (long long) fh->pos);
525     //curl_easy_setopt_or_die(fh->write_conn, CURLOPT_RANGE, range);
526 
527 	curl_easy_setopt_or_die(fh->write_conn, CURLOPT_APPEND, 1);
528 
529 	//curl_easy_setopt_or_die(fh->write_conn, CURLOPT_RESUME_FROM_LARGE, (curl_off_t)fh->pos);
530   }
531 
532   CURLcode curl_res = curl_easy_perform(fh->write_conn);
533 
534   curl_easy_setopt_or_die(fh->write_conn, CURLOPT_UPLOAD, 0);
535 
536   if (!fh->isready)
537     sem_post(&fh->ready);
538 
539   if (curl_res != CURLE_OK)
540   {
541 	  DEBUG(1, "write problem: %d(%s) text=%s\n", curl_res, curl_easy_strerror(curl_res), fh->curl_error_buffer);
542 	  fh->write_fail_cause = curl_res;
543 	  /* problem - let ftpfs_write continue to avoid hang */
544 	  sem_post(&fh->data_need);
545   }
546 
547   DEBUG(2, "leaving streaming write thread #%d curl_res=%d\n", write_thread_ctr--, curl_res);
548 
549   sem_post(&fh->data_written); /* ftpfs_write may return */
550 
551   free_uri(full_path_uri);
552 
553   return NULL;
554 }
555 
556 /* returns 1 on success, 0 on failure */
557 static int start_write_thread(struct ftpfs_file *fh)
558 {
559 	if (fh->write_conn != NULL)
560 	{
561 		fprintf(stderr, "assert fh->write_conn == NULL failed!\n");
562 		exit(1);
563 	}
564 
565 	fh->written_flag=0;
566 	fh->isready=0;
567 	fh->eof=0;
568 	sem_init(&fh->data_avail, 0, 0);
569 	sem_init(&fh->data_need, 0, 0);
570 	sem_init(&fh->data_written, 0, 0);
571 	sem_init(&fh->ready, 0, 0);
572 
573     fh->write_conn = curl_easy_init();
574     if (fh->write_conn == NULL) {
575       fprintf(stderr, "Error initializing libcurl\n");
576       return 0;
577     } else {
578       int err;
579       set_common_curl_stuff(fh->write_conn);
580       err = pthread_create(&fh->thread_id, NULL, ftpfs_write_thread, fh);
581       if (err) {
582         fprintf(stderr, "failed to create thread: %s\n", strerror(err));
583         /* FIXME: destroy curl_easy */
584         return 0;
585       }
586     }
587 	return 1;
588 }
589 
590 static int finish_write_thread(struct ftpfs_file *fh)
591 {
592     if (fh->write_fail_cause == CURLE_OK)
593     {
594       sem_wait(&fh->data_need);  /* only wait when there has been no error */
595     }
596     sem_post(&fh->data_avail);
597     fh->eof = 1;
598 
599     pthread_join(fh->thread_id, NULL);
600     DEBUG(2, "finish_write_thread after pthread_join. write_fail_cause=%d\n", fh->write_fail_cause);
601 
602     curl_easy_cleanup(fh->write_conn);
603     fh->write_conn = NULL;
604 
605     sem_destroy(&fh->data_avail);
606     sem_destroy(&fh->data_need);
607     sem_destroy(&fh->data_written);
608     sem_destroy(&fh->ready);
609 
610     if (fh->write_fail_cause != CURLE_OK)
611     {
612       return -EIO;
613     }
614     return 0;
615 }
616 
617 
618 static void free_ftpfs_file(struct ftpfs_file *fh) {
619   if (fh->write_conn)
620     curl_easy_cleanup(fh->write_conn);
621   g_free(fh->full_path);
622   g_free(fh->open_path);
623   sem_destroy(&fh->data_avail);
624   sem_destroy(&fh->data_need);
625   sem_destroy(&fh->data_written);
626   sem_destroy(&fh->ready);
627   free(fh);
628 }
629 
630 static int buffer_file(struct ftpfs_file *fh) {
631   char* full_path_uri = path_to_uri(fh->full_path); /* TODO: optimize bu pushing up conversion to context */
632   // If we want to write to the file, we have to load it all at once,
633   // modify it in memory and then upload it as a whole as most FTP servers
634   // don't support resume for uploads.
635   pthread_mutex_lock(&ftpfs.lock);
636   cancel_previous_multi();
637   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path_uri);
638   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &fh->buf);
639   CURLcode curl_res = curl_easy_perform(ftpfs.connection);
640   pthread_mutex_unlock(&ftpfs.lock);
641 
642   free_uri(full_path_uri);
643 
644   if (curl_res != 0) {
645     return -EACCES;
646   }
647 
648   return 0;
649 }
650 
651 static int create_empty_file(const char * path)
652 {
653   int err = 0;
654 
655   char *full_path = get_full_path(path);
656   char* full_path_uri = path_to_uri(full_path);
657 
658   pthread_mutex_lock(&ftpfs.lock);
659   cancel_previous_multi();
660   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path_uri);
661   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_INFILESIZE, 0);
662   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_UPLOAD, 1);
663   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_READDATA, NULL);
664   CURLcode curl_res = curl_easy_perform(ftpfs.connection);
665   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_UPLOAD, 0);
666   pthread_mutex_unlock(&ftpfs.lock);
667 
668   if (curl_res != 0) {
669     err = -EPERM;
670   }
671 
672   free_uri(full_path_uri);
673   free(full_path);
674   return err;
675 }
676 
677 static int ftpfs_mknod(const char* path, mode_t mode, dev_t rdev);
678 static int ftpfs_chmod(const char* path, mode_t mode);
679 
680 static char * flags_to_string(int flags)
681 {
682 	char * access_mode_str = NULL;
683 	if ((flags & O_ACCMODE) == O_WRONLY)
684 		access_mode_str = "O_WRONLY";
685 	else if ((flags & O_ACCMODE) == O_RDWR)
686 		access_mode_str = "O_RDWR";
687 	else if ((flags & O_ACCMODE) == O_RDONLY)
688 		access_mode_str = "O_RDONLY";
689 
690 	return g_strdup_printf("access_mode=%s, flags=%s%s%s%s",
691 			access_mode_str,
692 			(flags & O_CREAT) ? "O_CREAT " : "",
693 			(flags & O_TRUNC) ? "O_TRUNC " : "",
694 			(flags & O_EXCL) ? "O_EXCL " : "",
695 			(flags & O_APPEND) ? "O_APPEND " : "");
696 
697 }
698 
699 static int test_exists(const char* path)
700 {
701 	struct stat sbuf;
702 	return ftpfs_getattr(path, &sbuf);
703 }
704 
705 static __off_t test_size(const char* path)
706 {
707 	struct stat sbuf;
708 	int err = ftpfs_getattr(path, &sbuf);
709 	if (err)
710 		return err;
711 	return sbuf.st_size;
712 }
713 
714 static int ftpfs_open_common(const char* path, mode_t mode,
715                              struct fuse_file_info* fi) {
716 
717   char * flagsAsStr = flags_to_string(fi->flags);
718   DEBUG(2, "ftpfs_open_common: %s\n", flagsAsStr);
719   int err = 0;
720 
721   struct ftpfs_file* fh =
722     (struct ftpfs_file*) malloc(sizeof(struct ftpfs_file));
723 
724   memset(fh, 0, sizeof(*fh));
725   buf_init(&fh->buf);
726   fh->mode = mode;
727   fh->dirty = 0;
728   fh->copied = 0;
729   fh->last_offset = 0;
730   fh->can_shrink = 0;
731   buf_init(&fh->stream_buf);
732   /* sem_init(&fh->data_avail, 0, 0);
733   sem_init(&fh->data_need, 0, 0);
734   sem_init(&fh->data_written, 0, 0);
735   sem_init(&fh->ready, 0, 0); */
736   fh->open_path = strdup(path);
737   fh->full_path = get_full_path(path);
738   fh->written_flag = 0;
739   fh->write_fail_cause = CURLE_OK;
740   fh->curl_error_buffer[0] = '\0';
741   fh->write_may_start = 0;
742   fi->fh = (unsigned long) fh;
743 
744   if ((fi->flags & O_ACCMODE) == O_RDONLY) {
745     if (fi->flags & O_CREAT) {
746       err = ftpfs_mknod(path, (mode & 07777) | S_IFREG, 0);
747     } else {
748       // If it's read-only, we can load the file a bit at a time, as necessary.
749       DEBUG(1, "opening %s O_RDONLY\n", path);
750       fh->can_shrink = 1;
751       size_t size = ftpfs_read_chunk(fh->full_path, NULL, 1, 0, fi, 0);
752 
753       if (size == CURLFTPFS_BAD_READ) {
754         DEBUG(1, "initial read failed size=%d\n", size);
755         err = -EACCES;
756       }
757     }
758   }
759 
760   else if ((fi->flags & O_ACCMODE) == O_RDWR || (fi->flags & O_ACCMODE) == O_WRONLY)
761   {
762 #ifndef CURLFTPFS_O_RW_WORKAROUND
763 	  if ((fi->flags & O_ACCMODE) == O_RDWR)
764 	  {
765 		  err = -ENOTSUP;
766 		  goto fin;
767 	  }
768 #endif
769 
770 
771 	  if ((fi->flags & O_APPEND))
772 	  {
773 		DEBUG(1, "opening %s with O_APPEND - not supported!\n", path);
774 		err = -ENOTSUP;
775 	  }
776 
777 	  if ((fi->flags & O_EXCL))
778 	  {
779 		DEBUG(1, "opening %s with O_EXCL - testing existence\n", path);
780 		int exists_r = test_exists(path);
781 		if (exists_r != -ENOENT)
782 			err = -EACCES;
783 	  }
784 
785 	  if (!err)
786 	  {
787 		  if ((fi->flags & O_CREAT) || (fi->flags & O_TRUNC))
788 	      {
789 	        DEBUG(1, "opening %s for writing with O_CREAT or O_TRUNC. write thread will start now\n", path);
790 
791 
792 	    	fh->write_may_start=1;
793 
794 	        if (start_write_thread(fh))
795 	        {
796 	          sem_wait(&fh->ready);
797 	          /* chmod makes only sense on O_CREAT */
798 	          if (fi->flags & O_CREAT) ftpfs_chmod(path, mode);
799 	          sem_post(&fh->data_need);
800 	        }
801 	        else
802 	        {
803 	          err = -EIO;
804 	        }
805 	      }
806 	      else
807 	      {
808 	    	/* in this case we have to start writing later */
809 	        DEBUG(1, "opening %s for writing without O_CREAT or O_TRUNC. write thread will start after ftruncate\n", path);
810 	        /* expecting ftruncate */
811 	        fh->write_may_start=0;
812 	      }
813 	  }
814 
815   } else {
816       err = -EIO;
817   }
818 
819   fin:
820   if (err)
821     free_ftpfs_file(fh);
822 
823   g_free(flagsAsStr);
824   return op_return(err, "ftpfs_open");
825 }
826 
827 static int ftpfs_open(const char* path, struct fuse_file_info* fi) {
828   return ftpfs_open_common(path, 0, fi);
829 }
830 
831 #if FUSE_VERSION >= 25
832 static int ftpfs_create(const char* path, mode_t mode,
833                         struct fuse_file_info* fi) {
834   return ftpfs_open_common(path, mode, fi);
835 }
836 #endif
837 
838 static int ftpfs_read(const char* path, char* rbuf, size_t size, off_t offset,
839                       struct fuse_file_info* fi) {
840   int ret;
841   struct ftpfs_file *fh = get_ftpfs_file(fi);
842 
843   DEBUG(1, "ftpfs_read: %s size=%zu offset=%lld has_write_conn=%d pos=%lld\n", path, size, (long long) offset, fh->write_conn!=0, fh->pos);
844 
845   if (fh->pos>0 || fh->write_conn!=NULL)
846   {
847 	  fprintf(stderr, "in read/write mode we cannot read from a file that has already been written to\n");
848 	  return op_return(-EIO, "ftpfs_read");
849   }
850 
851   char *full_path = get_full_path(path);
852   size_t size_read = ftpfs_read_chunk(full_path, rbuf, size, offset, fi, 1);
853   free(full_path);
854   if (size_read == CURLFTPFS_BAD_READ) {
855     ret = -EIO;
856   } else {
857     ret = size_read;
858   }
859 
860   if (ret<0) op_return(ret, "ftpfs_read");
861   return ret;
862 }
863 
864 static int ftpfs_mknod(const char* path, mode_t mode, dev_t rdev) {
865   (void) rdev;
866 
867   int err = 0;
868 
869   DEBUG(1, "ftpfs_mknode: mode=%d\n", (int)mode);
870 
871   if ((mode & S_IFMT) != S_IFREG)
872     return -EPERM;
873 
874   err = create_empty_file(path);
875 
876   if (!err)
877       ftpfs_chmod(path, mode);
878 
879   return op_return(err, "ftpfs_mknod");
880 }
881 
882 static int ftpfs_chmod(const char* path, mode_t mode) {
883   int err = 0;
884 
885   // We can only process a subset of the mode - so strip
886   // to supported subset
887   int mode_c = mode - (mode / 0x1000 * 0x1000);
888 
889   struct curl_slist* header = NULL;
890   char* full_path = get_dir_path(path);
891   char* full_path_uri = path_to_uri(full_path);
892   char* filename = get_file_name(path);
893   char* cmd = g_strdup_printf("SITE CHMOD %.3o %s", mode_c, filename);
894   struct buffer buf;
895   buf_init(&buf);
896 
897   header = curl_slist_append(header, cmd);
898 
899   pthread_mutex_lock(&ftpfs.lock);
900   cancel_previous_multi();
901   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
902   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path_uri);
903   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
904   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
905   CURLcode curl_res = curl_easy_perform(ftpfs.connection);
906   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
907   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
908   pthread_mutex_unlock(&ftpfs.lock);
909 
910   if (curl_res != 0) {
911     err = -EPERM;
912   }
913 
914   buf_free(&buf);
915   curl_slist_free_all(header);
916   free_uri(full_path_uri);
917   free(full_path);
918   free(filename);
919   free(cmd);
920   return op_return(err, "ftpfs_chmod");
921 }
922 
923 static int ftpfs_chown(const char* path, uid_t uid, gid_t gid) {
924   int err = 0;
925 
926   DEBUG(1, "ftpfs_chown: %d %d\n", (int)uid, (int)gid);
927 
928   struct curl_slist* header = NULL;
929   char* full_path = get_dir_path(path);
930   char* full_path_uri = path_to_uri(full_path);
931   char* filename = get_file_name(path);
932   char* cmd = g_strdup_printf("SITE CHUID %i %s", uid, filename);
933   char* cmd2 = g_strdup_printf("SITE CHGID %i %s", gid, filename);
934   struct buffer buf;
935   buf_init(&buf);
936 
937   header = curl_slist_append(header, cmd);
938   header = curl_slist_append(header, cmd2);
939 
940   pthread_mutex_lock(&ftpfs.lock);
941   cancel_previous_multi();
942   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
943   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path_uri);
944   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
945   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
946   CURLcode curl_res = curl_easy_perform(ftpfs.connection);
947   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
948   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
949   pthread_mutex_unlock(&ftpfs.lock);
950 
951   if (curl_res != 0) {
952     err = -EPERM;
953   }
954 
955   buf_free(&buf);
956   curl_slist_free_all(header);
957   free_uri(full_path_uri);
958   free(full_path);
959   free(filename);
960   free(cmd);
961   free(cmd2);
962   return op_return(err, "ftpfs_chown");
963 }
964 
965 static int ftpfs_truncate(const char* path, off_t offset) {
966   DEBUG(1, "ftpfs_truncate: %s len=%lld\n", path, offset);
967   /* we can't use ftpfs_mknod here, because we don't know the right permissions */
968   if (offset == 0) return op_return(create_empty_file(path), "ftpfs_truncate");
969 
970   /* fix openoffice problem, truncating exactly to file length */
971 
972   __off_t size = (long long int)test_size(path);
973   DEBUG(1, "ftpfs_truncate: %s check filesize=%lld\n", path, (long long int)size);
974 
975   if (offset == size)
976 	  return op_return(0, "ftpfs_ftruncate");
977 
978   DEBUG(1, "ftpfs_truncate problem: %s offset != 0 or filesize=%lld != offset\n", path, (long long int)size);
979 
980 
981   return op_return(-EPERM, "ftpfs_truncate");
982 }
983 
984 static int ftpfs_ftruncate(const char * path , off_t offset, struct fuse_file_info * fi)
985 {
986   DEBUG(1, "ftpfs_ftruncate: %s len=%lld\n", path, offset);
987   struct ftpfs_file *fh = get_ftpfs_file(fi);
988 
989   if (offset == 0)
990   {
991 	 if (fh->pos == 0)
992 	 {
993 		 fh->write_may_start=1;
994 		 return op_return(create_empty_file(fh->open_path), "ftpfs_ftruncate");
995 	 }
996 	 return op_return(-EPERM, "ftpfs_ftruncate");
997   }
998   /* fix openoffice problem, truncating exactly to file length */
999 
1000   __off_t size = test_size(path);
1001   DEBUG(1, "ftpfs_ftruncate: %s check filesize=%lld\n", path, (long long int)size);
1002 
1003   if (offset == size)
1004 	  return op_return(0, "ftpfs_ftruncate");
1005 
1006   DEBUG(1, "ftpfs_ftruncate problem: %s offset != 0 or filesize(=%lld) != offset(=%lld)\n", path, (long long int)size, (long long int) offset);
1007 
1008   return op_return(-EPERM, "ftpfs_ftruncate");
1009 }
1010 
1011 static int ftpfs_utime(const char* path, struct utimbuf* time) {
1012   (void) path;
1013   (void) time;
1014   return op_return(0, "ftpfs_utime");
1015 }
1016 
1017 static int ftpfs_rmdir(const char* path) {
1018   int err = 0;
1019   struct curl_slist* header = NULL;
1020   char* full_path = get_dir_path(path);
1021   char* full_path_uri = path_to_uri(full_path);
1022   char* filename = get_file_name(path);
1023   char* cmd = g_strdup_printf("RMD %s", filename);
1024   struct buffer buf;
1025   buf_init(&buf);
1026 
1027   DEBUG(2, "%s\n", full_path);
1028   DEBUG(2, "%s\n", cmd);
1029 
1030   header = curl_slist_append(header, cmd);
1031 
1032   pthread_mutex_lock(&ftpfs.lock);
1033   cancel_previous_multi();
1034   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
1035   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path_uri);
1036   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
1037   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
1038   CURLcode curl_res = curl_easy_perform(ftpfs.connection);
1039   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
1040   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
1041   pthread_mutex_unlock(&ftpfs.lock);
1042 
1043   if (curl_res != 0) {
1044     err = -EPERM;
1045   }
1046 
1047   buf_free(&buf);
1048   curl_slist_free_all(header);
1049   free_uri(full_path_uri);
1050   free(full_path);
1051   free(filename);
1052   free(cmd);
1053   return op_return(err, "ftpfs_rmdir");
1054 }
1055 
1056 static int ftpfs_mkdir(const char* path, mode_t mode) {
1057   int err = 0;
1058   struct curl_slist* header = NULL;
1059   char* full_path = get_dir_path(path);
1060   char* full_path_uri = path_to_uri(full_path);
1061   char* filename = get_file_name(path);
1062   char* cmd = g_strdup_printf("MKD %s", filename);
1063   struct buffer buf;
1064   buf_init(&buf);
1065 
1066   header = curl_slist_append(header, cmd);
1067 
1068   pthread_mutex_lock(&ftpfs.lock);
1069   cancel_previous_multi();
1070   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
1071   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path_uri);
1072   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
1073   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
1074   CURLcode curl_res = curl_easy_perform(ftpfs.connection);
1075   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
1076   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
1077   pthread_mutex_unlock(&ftpfs.lock);
1078 
1079   if (curl_res != 0) {
1080     err = -EPERM;
1081   }
1082 
1083   buf_free(&buf);
1084   curl_slist_free_all(header);
1085   free_uri(full_path_uri);
1086   free(full_path);
1087   free(filename);
1088   free(cmd);
1089 
1090   if (!err)
1091     ftpfs_chmod(path, mode);
1092 
1093   return op_return(err, "ftpfs_mkdir");
1094 }
1095 
1096 static int ftpfs_unlink(const char* path) {
1097   int err = 0;
1098   struct curl_slist* header = NULL;
1099   char* full_path = get_dir_path(path);
1100   char* full_path_uri = path_to_uri(full_path);
1101   char* filename = get_file_name(path);
1102   char* cmd = g_strdup_printf("DELE %s", filename);
1103   struct buffer buf;
1104   buf_init(&buf);
1105 
1106   header = curl_slist_append(header, cmd);
1107 
1108   pthread_mutex_lock(&ftpfs.lock);
1109   cancel_previous_multi();
1110   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
1111   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path_uri);
1112   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
1113   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
1114   CURLcode curl_res = curl_easy_perform(ftpfs.connection);
1115   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
1116   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
1117   pthread_mutex_unlock(&ftpfs.lock);
1118 
1119   if (curl_res != 0) {
1120     err = -EPERM;
1121   }
1122 
1123   buf_free(&buf);
1124   curl_slist_free_all(header);
1125   free_uri(full_path_uri);
1126   free(full_path);
1127   free(filename);
1128   free(cmd);
1129   return op_return(err, "ftpfs_unlink");
1130 }
1131 
1132 static int ftpfs_write(const char *path, const char *wbuf, size_t size,
1133                        off_t offset, struct fuse_file_info *fi) {
1134   (void) path;
1135   struct ftpfs_file *fh = get_ftpfs_file(fi);
1136 
1137   DEBUG(1, "ftpfs_write: %s size=%zu offset=%lld has_write_conn=%d pos=%lld\n", path, size, (long long) offset, fh->write_conn!=0, fh->pos);
1138 
1139   if (fh->write_fail_cause != CURLE_OK)
1140   {
1141     DEBUG(1, "previous write failed. cause=%d\n", fh->write_fail_cause);
1142     return -EIO;
1143   }
1144 
1145   if (!fh->write_conn && fh->pos == 0 && offset == 0)
1146   {
1147     DEBUG(1, "ftpfs_write: starting a streaming write at pos=%lld\n", fh->pos);
1148 
1149     /* check if the file has been truncated to zero or has been newly created */
1150     if (!fh->write_may_start)
1151     {
1152     	long long size = (long long int)test_size(path);
1153     	if (size != 0)
1154     	{
1155     		fprintf(stderr, "ftpfs_write: start writing with no previous truncate not allowed! size check rval=%lld\n", size);
1156     		return op_return(-EIO, "ftpfs_write");
1157     	}
1158     }
1159 
1160 	int success = start_write_thread(fh);
1161     if (!success)
1162     {
1163       return op_return(-EIO, "ftpfs_write");
1164     }
1165     sem_wait(&fh->ready);
1166 	sem_post(&fh->data_need);
1167   }
1168 
1169   if (!fh->write_conn && fh->pos >0 && offset == fh->pos)
1170   {
1171     /* resume a streaming write */
1172     DEBUG(1, "ftpfs_write: resuming a streaming write at pos=%lld\n", fh->pos);
1173 
1174     int success = start_write_thread(fh);
1175     if (!success)
1176     {
1177       return op_return(-EIO, "ftpfs_write");
1178     }
1179     sem_wait(&fh->ready);
1180     sem_post(&fh->data_need);
1181   }
1182 
1183   if (fh->write_conn) {
1184     sem_wait(&fh->data_need);
1185 
1186     if (offset != fh->pos) {
1187       DEBUG(1, "non-sequential write detected -> fail\n");
1188 
1189       sem_post(&fh->data_avail);
1190       finish_write_thread(fh);
1191       return op_return(-EIO, "ftpfs_write");
1192 
1193 
1194     } else {
1195       if (buf_add_mem(&fh->stream_buf, wbuf, size) == -1) {
1196         sem_post(&fh->data_need);
1197         return op_return(-ENOMEM, "ftpfs_write");
1198       }
1199       fh->pos += size;
1200       /* wake up write_data_bg */
1201       sem_post(&fh->data_avail);
1202       /* wait until libcurl has completely written the current chunk or finished/failed */
1203       sem_wait(&fh->data_written);
1204       fh->written_flag = 0;
1205 
1206       if (fh->write_fail_cause != CURLE_OK)
1207       {
1208     	/* TODO: on error we should problably unlink the target file  */
1209         DEBUG(1, "writing failed. cause=%d\n", fh->write_fail_cause);
1210         return op_return(-EIO, "ftpfs_write");
1211       }
1212     }
1213 
1214   }
1215 
1216   return size;
1217 
1218 }
1219 
1220 static int ftpfs_flush(const char *path, struct fuse_file_info *fi) {
1221   int err = 0;
1222   struct ftpfs_file* fh = get_ftpfs_file(fi);
1223 
1224   DEBUG(1, "ftpfs_flush: buf.len=%zu buf.pos=%lld write_conn=%d\n", fh->buf.len, fh->pos, fh->write_conn!=0);
1225 
1226   if (fh->write_conn) {
1227     err = finish_write_thread(fh);
1228     if (err) return op_return(err, "ftpfs_flush");
1229 
1230     struct stat sbuf;
1231 
1232     /* check if the resulting file has the correct size
1233      this is important, because we use APPE for continuing
1234      writing after a premature flush */
1235     err = ftpfs_getattr(path, &sbuf);
1236     if (err) return op_return(err, "ftpfs_flush");
1237 
1238     if (sbuf.st_size != fh->pos)
1239     {
1240     	fh->write_fail_cause = -999;
1241     	fprintf(stderr, "ftpfs_flush: check filesize problem: size=%lld expected=%lld\n", sbuf.st_size, fh->pos);
1242     	return op_return(-EIO, "ftpfs_flush");
1243     }
1244 
1245     return 0;
1246   }
1247 
1248 
1249   if (!fh->dirty) return 0;
1250 
1251   return op_return(-EIO, "ftpfs_flush");
1252 
1253 }
1254 
1255 static int ftpfs_fsync(const char *path, int isdatasync,
1256                       struct fuse_file_info *fi) {
1257 	DEBUG(1, "ftpfs_fsync %s\n", path);
1258   (void) isdatasync;
1259   return ftpfs_flush(path, fi);
1260 }
1261 
1262 static int ftpfs_release(const char* path, struct fuse_file_info* fi) {
1263 
1264   DEBUG(1, "ftpfs_release %s\n", path);
1265   struct ftpfs_file* fh = get_ftpfs_file(fi);
1266   ftpfs_flush(path, fi);
1267   pthread_mutex_lock(&ftpfs.lock);
1268   if (ftpfs.current_fh == fh) {
1269     ftpfs.current_fh = NULL;
1270   }
1271   pthread_mutex_unlock(&ftpfs.lock);
1272 
1273   /*
1274   if (fh->write_conn) {
1275 	  finish_write_thread(fh);
1276   }
1277   */
1278   free_ftpfs_file(fh);
1279   return op_return(0, "ftpfs_release");
1280 }
1281 
1282 
1283 static int ftpfs_rename(const char* from, const char* to) {
1284   DEBUG(1, "ftpfs_rename from %s to %s\n", from, to);
1285   int err = 0;
1286   char* rnfr = g_strdup_printf("RNFR %s", from + 1);
1287   char* rnto = g_strdup_printf("RNTO %s", to + 1);
1288   struct buffer buf;
1289   buf_init(&buf);
1290   struct curl_slist* header = NULL;
1291 
1292   if (ftpfs.codepage) {
1293     convert_charsets(ftpfs.iocharset, ftpfs.codepage, &rnfr);
1294     convert_charsets(ftpfs.iocharset, ftpfs.codepage, &rnto);
1295   }
1296 
1297   header = curl_slist_append(header, rnfr);
1298   header = curl_slist_append(header, rnto);
1299 
1300   pthread_mutex_lock(&ftpfs.lock);
1301   cancel_previous_multi();
1302   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
1303   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, ftpfs.host);
1304   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
1305   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
1306   CURLcode curl_res = curl_easy_perform(ftpfs.connection);
1307   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
1308   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
1309   pthread_mutex_unlock(&ftpfs.lock);
1310 
1311   if (curl_res != 0) {
1312     err = -EPERM;
1313   }
1314 
1315   buf_free(&buf);
1316   curl_slist_free_all(header);
1317   free(rnfr);
1318   free(rnto);
1319 
1320   return op_return(err, "ftpfs_rename");
1321 }
1322 
1323 static int ftpfs_readlink(const char *path, char *linkbuf, size_t size) {
1324   int err;
1325   CURLcode curl_res;
1326   char* dir_path = get_dir_path(path);
1327   char* dir_path_uri = path_to_uri(dir_path);
1328 
1329   DEBUG(2, "dir_path: %s %s\n", path, dir_path);
1330   struct buffer buf;
1331   buf_init(&buf);
1332 
1333   pthread_mutex_lock(&ftpfs.lock);
1334   cancel_previous_multi();
1335   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, dir_path_uri);
1336   curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
1337   curl_res = curl_easy_perform(ftpfs.connection);
1338   pthread_mutex_unlock(&ftpfs.lock);
1339 
1340   if (curl_res != 0) {
1341     DEBUG(1, "%s\n", error_buf);
1342   }
1343   buf_null_terminate(&buf);
1344 
1345   char* name = strrchr(path, '/');
1346   ++name;
1347   err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
1348                   name, NULL, linkbuf, size, NULL, NULL);
1349 
1350   free_uri(dir_path_uri);
1351   free(dir_path);
1352   buf_free(&buf);
1353   if (err) return op_return(-ENOENT, "ftpfs_readlink");
1354   return op_return(0, "ftpfs_readlink");
1355 }
1356 
1357 #if FUSE_VERSION >= 25
1358 static int ftpfs_statfs(const char *path, struct statvfs *buf)
1359 {
1360     (void) path;
1361 
1362     buf->f_namemax = 255;
1363     buf->f_bsize = ftpfs.blksize;
1364     buf->f_frsize = 512;
1365     buf->f_blocks = 999999999 * 2;
1366     buf->f_bfree =  999999999 * 2;
1367     buf->f_bavail = 999999999 * 2;
1368     buf->f_files =  999999999;
1369     buf->f_ffree =  999999999;
1370     return op_return(0, "ftpfs_statfs");
1371 }
1372 #else
1373 static int ftpfs_statfs(const char *path, struct statfs *buf)
1374 {
1375     (void) path;
1376 
1377     buf->f_namelen = 255;
1378     buf->f_bsize = ftpfs.blksize;
1379     buf->f_blocks = 999999999 * 2;
1380     buf->f_bfree =  999999999 * 2;
1381     buf->f_bavail = 999999999 * 2;
1382     buf->f_files =  999999999;
1383     buf->f_ffree =  999999999;
1384     return op_return(0, "ftpfs_statfs");
1385 }
1386 #endif
1387 
1388 static struct fuse_cache_operations ftpfs_oper = {
1389   .oper = {
1390 //    .init       = ftpfs_init,
1391     .getattr    = ftpfs_getattr,
1392     .readlink   = ftpfs_readlink,
1393     .mknod      = ftpfs_mknod,
1394     .mkdir      = ftpfs_mkdir,
1395 //    .symlink    = ftpfs_symlink,
1396     .unlink     = ftpfs_unlink,
1397     .rmdir      = ftpfs_rmdir,
1398     .rename     = ftpfs_rename,
1399     .chmod      = ftpfs_chmod,
1400     .chown      = ftpfs_chown,
1401     .truncate   = ftpfs_truncate,
1402     .utime      = ftpfs_utime,
1403     .open       = ftpfs_open,
1404     .flush      = ftpfs_flush,
1405     .fsync      = ftpfs_fsync,
1406     .release    = ftpfs_release,
1407     .read       = ftpfs_read,
1408     .write      = ftpfs_write,
1409     .statfs     = ftpfs_statfs,
1410 #if FUSE_VERSION >= 25
1411     .create     = ftpfs_create,
1412     .ftruncate  = ftpfs_ftruncate,
1413 //    .fgetattr   = ftpfs_fgetattr,
1414 #endif
1415   },
1416   .cache_getdir = ftpfs_getdir,
1417 };
1418 
1419 static int curlftpfs_fuse_main(struct fuse_args *args)
1420 {
1421 #if FUSE_VERSION >= 26
1422     return fuse_main(args->argc, args->argv, cache_init(&ftpfs_oper), NULL);
1423 #else
1424     return fuse_main(args->argc, args->argv, cache_init(&ftpfs_oper));
1425 #endif
1426 }
1427 
1428 static int ftpfs_opt_proc(void* data, const char* arg, int key,
1429                           struct fuse_args* outargs) {
1430   (void) data;
1431   (void) outargs;
1432 
1433   switch (key) {
1434     case FUSE_OPT_KEY_OPT:
1435       return 1;
1436     case FUSE_OPT_KEY_NONOPT:
1437       if (!ftpfs.host) {
1438         const char* prefix = "";
1439         if (strncmp(arg, "ftp://", 6) && strncmp(arg, "ftps://", 7)) {
1440           prefix = "ftp://";
1441         }
1442         ftpfs.host = g_strdup_printf("%s%s%s", prefix, arg,
1443 			arg[strlen(arg)-1] == '/' ? "" : "/");
1444         return 0;
1445       } else if (!ftpfs.mountpoint)
1446         ftpfs.mountpoint = strdup(arg);
1447       return 1;
1448     case KEY_HELP:
1449       usage(outargs->argv[0]);
1450       fuse_opt_add_arg(outargs, "-ho");
1451       curlftpfs_fuse_main(outargs);
1452       exit(1);
1453     case KEY_VERBOSE:
1454       ftpfs.verbose = 1;
1455       return 0;
1456     case KEY_VERSION:
1457       fprintf(stderr, "curlftpfs %s libcurl/%s fuse/%u.%u\n",
1458               VERSION,
1459               ftpfs.curl_version->version,
1460               FUSE_MAJOR_VERSION,
1461               FUSE_MINOR_VERSION);
1462       exit(1);
1463     default:
1464       exit(1);
1465   }
1466 }
1467 
1468 static void usage(const char* progname) {
1469   fprintf(stderr,
1470 "usage: %s <ftphost> <mountpoint>\n"
1471 "\n"
1472 "CurlFtpFS options:\n"
1473 "    -o opt,[opt...]        ftp options\n"
1474 "    -v   --verbose         make libcurl print verbose debug\n"
1475 "    -h   --help            print help\n"
1476 "    -V   --version         print version\n"
1477 "\n"
1478 "FTP options:\n"
1479 "    ftpfs_debug         print some debugging information\n"
1480 "    transform_symlinks  prepend mountpoint to absolute symlink targets\n"
1481 "    disable_epsv        use PASV, without trying EPSV first (default)\n"
1482 "    enable_epsv         try EPSV before reverting to PASV\n"
1483 "    skip_pasv_ip        skip the IP address for PASV\n"
1484 "    ftp_port=STR        use PORT with address instead of PASV\n"
1485 "    disable_eprt        use PORT, without trying EPRT first\n"
1486 "    ftp_method          [multicwd/singlecwd] Control CWD usage\n"
1487 "    custom_list=STR     Command used to list files. Defaults to \"LIST -a\"\n"
1488 "    tcp_nodelay         use the TCP_NODELAY option\n"
1489 "    connect_timeout=N   maximum time allowed for connection in seconds\n"
1490 "    ssl                 enable SSL/TLS for both control and data connections\n"
1491 "    ssl_control         enable SSL/TLS only for control connection\n"
1492 "    ssl_try             try SSL/TLS first but connect anyway\n"
1493 "    no_verify_hostname  does not verify the hostname (SSL)\n"
1494 "    no_verify_peer      does not verify the peer (SSL)\n"
1495 "    cert=STR            client certificate file (SSL)\n"
1496 "    cert_type=STR       certificate file type (DER/PEM/ENG) (SSL)\n"
1497 "    key=STR             private key file name (SSL)\n"
1498 "    key_type=STR        private key file type (DER/PEM/ENG) (SSL)\n"
1499 "    pass=STR            pass phrase for the private key (SSL)\n"
1500 "    engine=STR          crypto engine to use (SSL)\n"
1501 "    cacert=STR          file with CA certificates to verify the peer (SSL)\n"
1502 "    capath=STR          CA directory to verify peer against (SSL)\n"
1503 "    ciphers=STR         SSL ciphers to use (SSL)\n"
1504 "    interface=STR       specify network interface/address to use\n"
1505 "    krb4=STR            enable krb4 with specified security level\n"
1506 "    proxy=STR           use host:port HTTP proxy\n"
1507 "    proxytunnel         operate through a HTTP proxy tunnel (using CONNECT)\n"
1508 "    proxy_anyauth       pick \"any\" proxy authentication method\n"
1509 "    proxy_basic         use Basic authentication on the proxy\n"
1510 "    proxy_digest        use Digest authentication on the proxy\n"
1511 "    proxy_ntlm          use NTLM authentication on the proxy\n"
1512 "    httpproxy           use a HTTP proxy (default)\n"
1513 "    socks4              use a SOCKS4 proxy\n"
1514 "    socks5              use a SOCKS5 proxy\n"
1515 "    user=STR            set server user and password\n"
1516 "    proxy_user=STR      set proxy user and password\n"
1517 "    tlsv1               use TLSv1 (SSL)\n"
1518 "    sslv3               use SSLv3 (SSL)\n"
1519 "    ipv4                resolve name to IPv4 address\n"
1520 "    ipv6                resolve name to IPv6 address\n"
1521 "    utf8                try to transfer file list with utf-8 encoding\n"
1522 "    codepage=STR        set the codepage the server uses\n"
1523 "    iocharset=STR       set the charset used by the client\n"
1524 "\n"
1525 "CurlFtpFS cache options:  \n"
1526 "    cache=yes|no              enable/disable cache (default: yes)\n"
1527 "    cache_timeout=SECS        set timeout for stat, dir, link at once\n"
1528 "                              default is %d seconds\n"
1529 "    cache_stat_timeout=SECS   set stat timeout\n"
1530 "    cache_dir_timeout=SECS    set dir timeout\n"
1531 "    cache_link_timeout=SECS   set link timeout\n"
1532 "\n", progname, DEFAULT_CACHE_TIMEOUT);
1533 }
1534 
1535 static int ftpfilemethod(const char *str)
1536 {
1537   if(!strcmp("singlecwd", str))
1538     return CURLFTPMETHOD_SINGLECWD;
1539   if(!strcmp("multicwd", str))
1540     return CURLFTPMETHOD_MULTICWD;
1541   DEBUG(1, "unrecognized ftp file method '%s', using default\n", str);
1542   return CURLFTPMETHOD_MULTICWD;
1543 }
1544 
1545 static void set_common_curl_stuff(CURL* easy) {
1546   curl_easy_setopt_or_die(easy, CURLOPT_WRITEFUNCTION, read_data);
1547   curl_easy_setopt_or_die(easy, CURLOPT_READFUNCTION, write_data);
1548   curl_easy_setopt_or_die(easy, CURLOPT_ERRORBUFFER, error_buf);
1549   curl_easy_setopt_or_die(easy, CURLOPT_URL, ftpfs.host);
1550   curl_easy_setopt_or_die(easy, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
1551   curl_easy_setopt_or_die(easy, CURLOPT_NOSIGNAL, 1);
1552   curl_easy_setopt_or_die(easy, CURLOPT_CUSTOMREQUEST, "LIST -a");
1553 
1554   if (ftpfs.custom_list) {
1555     curl_easy_setopt_or_die(easy, CURLOPT_CUSTOMREQUEST, ftpfs.custom_list);
1556   }
1557 
1558   if (ftpfs.tryutf8) {
1559     // We'll let the slist leak, as it will still be accessible within
1560     // libcurl. If we ever want to add more commands to CURLOPT_QUOTE, we'll
1561     // have to think of a better strategy.
1562     struct curl_slist *slist = NULL;
1563 
1564     // Adding the QUOTE here will make this command be sent with every request.
1565     // This is necessary to ensure that the server is still in UTF8 mode after
1566     // we get disconnected and automatically reconnect.
1567     slist = curl_slist_append(slist, "OPTS UTF8 ON");
1568     curl_easy_setopt_or_die(easy, CURLOPT_QUOTE, slist);
1569   }
1570 
1571   if (ftpfs.verbose) {
1572     curl_easy_setopt_or_die(easy, CURLOPT_VERBOSE, TRUE);
1573   }
1574 
1575   if (ftpfs.disable_epsv) {
1576     curl_easy_setopt_or_die(easy, CURLOPT_FTP_USE_EPSV, FALSE);
1577   }
1578 
1579   if (ftpfs.skip_pasv_ip) {
1580     curl_easy_setopt_or_die(easy, CURLOPT_FTP_SKIP_PASV_IP, TRUE);
1581   }
1582 
1583   if (ftpfs.ftp_port) {
1584     curl_easy_setopt_or_die(easy, CURLOPT_FTPPORT, ftpfs.ftp_port);
1585   }
1586 
1587   if (ftpfs.disable_eprt) {
1588     curl_easy_setopt_or_die(easy, CURLOPT_FTP_USE_EPRT, FALSE);
1589   }
1590 
1591   if (ftpfs.ftp_method) {
1592     curl_easy_setopt_or_die(easy, CURLOPT_FTP_FILEMETHOD,
1593                             ftpfilemethod(ftpfs.ftp_method));
1594   }
1595 
1596   if (ftpfs.tcp_nodelay) {
1597     /* CURLOPT_TCP_NODELAY is not defined in older versions */
1598     curl_easy_setopt_or_die(easy, CURLOPT_TCP_NODELAY, 1);
1599   }
1600 
1601   curl_easy_setopt_or_die(easy, CURLOPT_CONNECTTIMEOUT, ftpfs.connect_timeout);
1602 
1603   /* CURLFTPSSL_CONTROL and CURLFTPSSL_ALL should make the connection fail if
1604    * the server doesn't support SSL but libcurl only honors this beginning
1605    * with version 7.15.4 */
1606   if (ftpfs.use_ssl > CURLFTPSSL_TRY &&
1607       ftpfs.curl_version->version_num <= CURLFTPFS_BAD_SSL) {
1608     fprintf(stderr,
1609 "WARNING: you are using libcurl %s.\n"
1610 "This version of libcurl does not respect the mandatory SSL flag.\n"
1611 "It will try to send the user and password even if the server doesn't support\n"
1612 "SSL. Please upgrade to libcurl version 7.15.4 or higher.\n"
1613 "You can abort the connection now by pressing ctrl+c.\n",
1614             ftpfs.curl_version->version);
1615     int i;
1616     const int time_to_wait = 10;
1617     for (i = 0; i < time_to_wait; i++) {
1618       fprintf(stderr, "%d.. ", time_to_wait - i);
1619       sleep(1);
1620     }
1621     fprintf(stderr, "\n");
1622   }
1623   curl_easy_setopt_or_die(easy, CURLOPT_FTP_SSL, ftpfs.use_ssl);
1624 
1625   curl_easy_setopt_or_die(easy, CURLOPT_SSLCERT, ftpfs.cert);
1626   curl_easy_setopt_or_die(easy, CURLOPT_SSLCERTTYPE, ftpfs.cert_type);
1627   curl_easy_setopt_or_die(easy, CURLOPT_SSLKEY, ftpfs.key);
1628   curl_easy_setopt_or_die(easy, CURLOPT_SSLKEYTYPE, ftpfs.key_type);
1629   curl_easy_setopt_or_die(easy, CURLOPT_SSLKEYPASSWD, ftpfs.key_password);
1630 
1631   if (ftpfs.engine) {
1632     curl_easy_setopt_or_die(easy, CURLOPT_SSLENGINE, ftpfs.engine);
1633     curl_easy_setopt_or_die(easy, CURLOPT_SSLENGINE_DEFAULT, 1);
1634   }
1635 
1636   curl_easy_setopt_or_die(easy, CURLOPT_SSL_VERIFYPEER, TRUE);
1637   if (ftpfs.no_verify_peer) {
1638     curl_easy_setopt_or_die(easy, CURLOPT_SSL_VERIFYPEER, FALSE);
1639   }
1640 
1641   if (ftpfs.cacert || ftpfs.capath) {
1642     if (ftpfs.cacert) {
1643       curl_easy_setopt_or_die(easy, CURLOPT_CAINFO, ftpfs.cacert);
1644     }
1645     if (ftpfs.capath) {
1646       curl_easy_setopt_or_die(easy, CURLOPT_CAPATH, ftpfs.capath);
1647     }
1648   }
1649 
1650   if (ftpfs.ciphers) {
1651     curl_easy_setopt_or_die(easy, CURLOPT_SSL_CIPHER_LIST, ftpfs.ciphers);
1652   }
1653 
1654   if (ftpfs.no_verify_hostname) {
1655     /* The default is 2 which verifies even the host string. This sets to 1
1656      * which means verify the host but not the string. */
1657     curl_easy_setopt_or_die(easy, CURLOPT_SSL_VERIFYHOST, 1);
1658   }
1659 
1660   curl_easy_setopt_or_die(easy, CURLOPT_INTERFACE, ftpfs.interface);
1661   curl_easy_setopt_or_die(easy, CURLOPT_KRB4LEVEL, ftpfs.krb4);
1662 
1663   if (ftpfs.proxy) {
1664     curl_easy_setopt_or_die(easy, CURLOPT_PROXY, ftpfs.proxy);
1665   }
1666 
1667   /* The default proxy type is HTTP */
1668   if (!ftpfs.proxytype) {
1669     ftpfs.proxytype = CURLPROXY_HTTP;
1670   }
1671   curl_easy_setopt_or_die(easy, CURLOPT_PROXYTYPE, ftpfs.proxytype);
1672 
1673   /* Connection to FTP servers only make sense with a HTTP tunnel proxy */
1674   if (ftpfs.proxytype == CURLPROXY_HTTP || ftpfs.proxytunnel) {
1675     curl_easy_setopt_or_die(easy, CURLOPT_HTTPPROXYTUNNEL, TRUE);
1676   }
1677 
1678   if (ftpfs.proxyanyauth) {
1679     curl_easy_setopt_or_die(easy, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
1680   } else if (ftpfs.proxyntlm) {
1681     curl_easy_setopt_or_die(easy, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
1682   } else if (ftpfs.proxydigest) {
1683     curl_easy_setopt_or_die(easy, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
1684   } else if (ftpfs.proxybasic) {
1685     curl_easy_setopt_or_die(easy, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
1686   }
1687 
1688   curl_easy_setopt_or_die(easy, CURLOPT_USERPWD, ftpfs.user);
1689   curl_easy_setopt_or_die(easy, CURLOPT_PROXYUSERPWD, ftpfs.proxy_user);
1690   curl_easy_setopt_or_die(easy, CURLOPT_SSLVERSION, ftpfs.ssl_version);
1691   curl_easy_setopt_or_die(easy, CURLOPT_IPRESOLVE, ftpfs.ip_version);
1692 }
1693 
1694 static void checkpasswd(const char *kind, /* for what purpose */
1695                         char **userpwd) /* pointer to allocated string */
1696 {
1697   char *ptr;
1698   if(!*userpwd)
1699     return;
1700 
1701   ptr = strchr(*userpwd, ':');
1702   if(!ptr) {
1703     /* no password present, prompt for one */
1704     char *passwd;
1705     char prompt[256];
1706     size_t passwdlen;
1707     size_t userlen = strlen(*userpwd);
1708     char *passptr;
1709 
1710     /* build a nice-looking prompt */
1711     snprintf(prompt, sizeof(prompt),
1712         "Enter %s password for user '%s':",
1713         kind, *userpwd);
1714 
1715     /* get password */
1716     passwd = getpass(prompt);
1717     passwdlen = strlen(passwd);
1718 
1719     /* extend the allocated memory area to fit the password too */
1720     passptr = realloc(*userpwd,
1721         passwdlen + 1 + /* an extra for the colon */
1722         userlen + 1);   /* an extra for the zero */
1723 
1724     if(passptr) {
1725       /* append the password separated with a colon */
1726       passptr[userlen]=':';
1727       memcpy(&passptr[userlen+1], passwd, passwdlen+1);
1728       *userpwd = passptr;
1729     }
1730   }
1731 }
1732 
1733 #if FUSE_VERSION == 25
1734 static int fuse_opt_insert_arg(struct fuse_args *args, int pos,
1735                                const char *arg)
1736 {
1737     assert(pos <= args->argc);
1738     if (fuse_opt_add_arg(args, arg) == -1)
1739         return -1;
1740 
1741     if (pos != args->argc - 1) {
1742         char *newarg = args->argv[args->argc - 1];
1743         memmove(&args->argv[pos + 1], &args->argv[pos],
1744                 sizeof(char *) * (args->argc - pos - 1));
1745         args->argv[pos] = newarg;
1746     }
1747     return 0;
1748 }
1749 #endif
1750 
1751 int main(int argc, char** argv) {
1752   int res;
1753   struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
1754   CURLcode curl_res;
1755   CURL* easy;
1756   char *tmp;
1757 
1758   // Initialize curl library before we are a multithreaded program
1759   curl_global_init(CURL_GLOBAL_ALL);
1760 
1761   memset(&ftpfs, 0, sizeof(ftpfs));
1762 
1763   // Set some default values
1764   ftpfs.curl_version = curl_version_info(CURLVERSION_NOW);
1765   ftpfs.safe_nobody = ftpfs.curl_version->version_num > CURLFTPFS_BAD_NOBODY;
1766   ftpfs.blksize = 4096;
1767   ftpfs.disable_epsv = 1;
1768   ftpfs.multiconn = 1;
1769   ftpfs.attached_to_multi = 0;
1770 
1771   if (fuse_opt_parse(&args, &ftpfs, ftpfs_opts, ftpfs_opt_proc) == -1)
1772     exit(1);
1773 
1774   if (!ftpfs.host) {
1775     fprintf(stderr, "missing host\n");
1776     fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
1777     exit(1);
1778   }
1779 
1780   if (!ftpfs.iocharset) {
1781     ftpfs.iocharset = "UTF8";
1782   }
1783 
1784   if (ftpfs.codepage) {
1785     convert_charsets(ftpfs.iocharset, ftpfs.codepage, &ftpfs.host);
1786   }
1787 
1788   easy = curl_easy_init();
1789   if (easy == NULL) {
1790     fprintf(stderr, "Error initializing libcurl\n");
1791     exit(1);
1792   }
1793 
1794   res = cache_parse_options(&args);
1795   if (res == -1)
1796     exit(1);
1797 
1798   checkpasswd("host", &ftpfs.user);
1799   checkpasswd("proxy", &ftpfs.proxy_user);
1800 
1801   if (ftpfs.transform_symlinks && !ftpfs.mountpoint) {
1802     fprintf(stderr, "cannot transform symlinks: no mountpoint given\n");
1803     exit(1);
1804   }
1805   if (!ftpfs.transform_symlinks)
1806     ftpfs.symlink_prefix_len = 0;
1807   else if (realpath(ftpfs.mountpoint, ftpfs.symlink_prefix) != NULL)
1808     ftpfs.symlink_prefix_len = strlen(ftpfs.symlink_prefix);
1809   else {
1810     perror("unable to normalize mount path");
1811     exit(1);
1812   }
1813 
1814   set_common_curl_stuff(easy);
1815   curl_easy_setopt_or_die(easy, CURLOPT_WRITEDATA, NULL);
1816   curl_easy_setopt_or_die(easy, CURLOPT_NOBODY, ftpfs.safe_nobody);
1817   curl_res = curl_easy_perform(easy);
1818   if (curl_res != 0) {
1819     fprintf(stderr, "Error connecting to ftp: %s\n", error_buf);
1820     exit(1);
1821   }
1822   curl_easy_setopt_or_die(easy, CURLOPT_NOBODY, 0);
1823 
1824   ftpfs.multi = curl_multi_init();
1825   if (ftpfs.multi == NULL) {
1826     fprintf(stderr, "Error initializing libcurl multi\n");
1827     exit(1);
1828   }
1829 
1830   ftpfs.connection = easy;
1831   pthread_mutex_init(&ftpfs.lock, NULL);
1832 
1833   // Set the filesystem name to show the current server
1834   tmp = g_strdup_printf("-ofsname=curlftpfs#%s", ftpfs.host);
1835   fuse_opt_insert_arg(&args, 1, tmp);
1836   g_free(tmp);
1837 
1838   res = curlftpfs_fuse_main(&args);
1839 
1840   cancel_previous_multi();
1841   curl_multi_cleanup(ftpfs.multi);
1842   curl_easy_cleanup(easy);
1843   curl_global_cleanup();
1844   fuse_opt_free_args(&args);
1845 
1846   return res;
1847 }
1848