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