1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765 |
- /*
- FTP file system
- Copyright (C) 2006 Robson Braga Araujo <robsonbraga@gmail.com>
- This program can be distributed under the terms of the GNU GPL.
- See the file COPYING.
- */
- #define FUSE_USE_VERSION 31
- #include "config.h"
- #include <stdlib.h>
- #include <stdio.h>
- #include <ctype.h>
- #include <stdint.h>
- #include <errno.h>
- #include <string.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <netinet/in.h>
- #include <fuse.h>
- #include <fuse_opt.h>
- #include <glib.h>
- #include <semaphore.h>
- #include <assert.h>
- #include "charset_utils.h"
- #include "path_utils.h"
- #include "ftpfs-ls.h"
- #include "cache.h"
- #include "ftpfs.h"
- #define CURLFTPFS_BAD_NOBODY 0x070f02
- #define CURLFTPFS_BAD_SSL 0x070f03
- #define CURLFTPFS_BAD_READ ((size_t)-1)
- #define MAX_BUFFER_LEN (300*1024)
- struct ftpfs ftpfs;
- static char error_buf[CURL_ERROR_SIZE];
- struct buffer {
- uint8_t* p;
- size_t len;
- size_t size;
- off_t begin_offset;
- };
- static void usage(const char* progname);
- static void buf_init(struct buffer* buf) {
- buf->p = NULL;
- buf->begin_offset = 0;
- buf->len = 0;
- buf->size = 0;
- }
- static inline void buf_free(struct buffer* buf) {
- free(buf->p);
- }
- static inline void buf_clear(struct buffer *buf) {
- buf_free(buf);
- buf_init(buf);
- }
- static int buf_resize(struct buffer *buf, size_t len) {
- buf->size = (buf->len + len + 63) & ~31;
- buf->p = (uint8_t *) realloc(buf->p, buf->size);
- if (!buf->p) {
- fprintf(stderr, "ftpfs: memory allocation failed\n");
- return -1;
- }
- return 0;
- }
- static int buf_add_mem(struct buffer *buf, const void *data, size_t len)
- {
- if (buf->len + len > buf->size && buf_resize(buf, len) == -1)
- return -1;
- memcpy(buf->p + buf->len, data, len);
- buf->len += len;
- return 0;
- }
- static void buf_null_terminate(struct buffer *buf)
- {
- if (buf_add_mem(buf, "\0", 1) == -1)
- exit(1);
- }
- struct ftpfs_file {
- struct buffer buf;
- int dirty;
- int copied;
- off_t last_offset;
- int can_shrink;
- pthread_t thread_id;
- mode_t mode;
- char * open_path;
- char * full_path;
- struct buffer stream_buf;
- CURL *write_conn;
- sem_t data_avail;
- sem_t data_need;
- sem_t data_written;
- sem_t ready;
- int isready;
- int eof;
- int written_flag;
- int write_fail_cause;
- int write_may_start;
- char curl_error_buffer[CURL_ERROR_SIZE];
- off_t pos;
- };
- enum {
- KEY_HELP,
- KEY_VERBOSE,
- KEY_VERSION,
- };
- #define FTPFS_OPT(t, p, v) { t, offsetof(struct ftpfs, p), v }
- static struct fuse_opt ftpfs_opts[] = {
- FTPFS_OPT("ftpfs_debug=%u", debug, 0),
- FTPFS_OPT("transform_symlinks", transform_symlinks, 1),
- FTPFS_OPT("disable_epsv", disable_epsv, 1),
- FTPFS_OPT("enable_epsv", disable_epsv, 0),
- FTPFS_OPT("skip_pasv_ip", skip_pasv_ip, 1),
- FTPFS_OPT("ftp_port=%s", ftp_port, 0),
- FTPFS_OPT("disable_eprt", disable_eprt, 1),
- FTPFS_OPT("ftp_method=%s", ftp_method, 0),
- FTPFS_OPT("custom_list=%s", custom_list, 0),
- FTPFS_OPT("tcp_nodelay", tcp_nodelay, 1),
- FTPFS_OPT("connect_timeout=%u", connect_timeout, 0),
- FTPFS_OPT("ssl", use_ssl, CURLFTPSSL_ALL),
- FTPFS_OPT("ssl_control", use_ssl, CURLFTPSSL_CONTROL),
- FTPFS_OPT("ssl_try", use_ssl, CURLFTPSSL_TRY),
- FTPFS_OPT("no_verify_hostname", no_verify_hostname, 1),
- FTPFS_OPT("no_verify_peer", no_verify_peer, 1),
- FTPFS_OPT("cert=%s", cert, 0),
- FTPFS_OPT("cert_type=%s", cert_type, 0),
- FTPFS_OPT("key=%s", key, 0),
- FTPFS_OPT("key_type=%s", key_type, 0),
- FTPFS_OPT("pass=%s", key_password, 0),
- FTPFS_OPT("engine=%s", engine, 0),
- FTPFS_OPT("cacert=%s", cacert, 0),
- FTPFS_OPT("capath=%s", capath, 0),
- FTPFS_OPT("ciphers=%s", ciphers, 0),
- FTPFS_OPT("interface=%s", interface, 0),
- FTPFS_OPT("krb4=%s", krb4, 0),
- FTPFS_OPT("proxy=%s", proxy, 0),
- FTPFS_OPT("proxytunnel", proxytunnel, 1),
- FTPFS_OPT("proxy_anyauth", proxyanyauth, 1),
- FTPFS_OPT("proxy_basic", proxybasic, 1),
- FTPFS_OPT("proxy_digest", proxydigest, 1),
- FTPFS_OPT("proxy_ntlm", proxyntlm, 1),
- FTPFS_OPT("httpproxy", proxytype, CURLPROXY_HTTP),
- FTPFS_OPT("socks4", proxytype, CURLPROXY_SOCKS4),
- FTPFS_OPT("socks5", proxytype, CURLPROXY_SOCKS5),
- FTPFS_OPT("user=%s", user, 0),
- FTPFS_OPT("proxy_user=%s", proxy_user, 0),
- FTPFS_OPT("tlsv1", ssl_version, CURL_SSLVERSION_TLSv1),
- FTPFS_OPT("sslv3", ssl_version, CURL_SSLVERSION_SSLv3),
- FTPFS_OPT("ipv4", ip_version, CURL_IPRESOLVE_V4),
- FTPFS_OPT("ipv6", ip_version, CURL_IPRESOLVE_V6),
- FTPFS_OPT("utf8", tryutf8, 1),
- FTPFS_OPT("codepage=%s", codepage, 0),
- FTPFS_OPT("iocharset=%s", iocharset, 0),
- FTPFS_OPT("nomulticonn", multiconn, 0),
- FUSE_OPT_KEY("-h", KEY_HELP),
- FUSE_OPT_KEY("--help", KEY_HELP),
- FUSE_OPT_KEY("-v", KEY_VERBOSE),
- FUSE_OPT_KEY("--verbose", KEY_VERBOSE),
- FUSE_OPT_KEY("-V", KEY_VERSION),
- FUSE_OPT_KEY("--version", KEY_VERSION),
- FUSE_OPT_END
- };
- static struct ftpfs_file *get_ftpfs_file(struct fuse_file_info *fi) {
- return (struct ftpfs_file *) (uintptr_t) fi->fh;
- }
- static void cancel_previous_multi() {
- if (!ftpfs.attached_to_multi) return;
- DEBUG(1, "cancel previous multi\n");
- CURLMcode curlMCode = curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
- if (curlMCode) {
- fprintf(stderr, "curl_multi_remove_handle problem: %d %s\n",
- curlMCode, curl_multi_strerror(curlMCode));
- exit(1);
- }
- ftpfs.attached_to_multi = 0;
- }
- static int op_return(int err, char * operation) {
- if(!err) {
- DEBUG(2, "%s successful\n", operation);
- return 0;
- }
- fprintf(stderr, "ftpfs: operation %s failed because %s\n", operation, strerror(-err));
- return err;
- }
- static int ftpfs_getdir(const char* path, fuse_fill_dir_t filler, void *fuse_buf) {
- int err = 0;
- CURLcode curl_res;
- char* dir_path = get_fulldir_path(path);
- struct buffer buf;
- buf_init(&buf);
- pthread_mutex_lock(&ftpfs.lock);
- cancel_previous_multi();
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, dir_path);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
- curl_res = curl_easy_perform(ftpfs.connection);
- pthread_mutex_unlock(&ftpfs.lock);
- printf("curl_easy_perform() = %d\n", curl_res);
- if (curl_res != 0) {
- DEBUG(1, "%s\n", error_buf);
- err = -EIO;
- } else {
- buf_null_terminate(&buf);
- parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
- NULL, NULL, NULL, 0, 0, filler, fuse_buf);
- }
- free(dir_path);
- buf_free(&buf);
- return op_return(err, "ftpfs_getdir");
- }
- static int ftpfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
- off_t offset, struct fuse_file_info *fi, enum fuse_readdir_flags flags) {
- (void) offset;
- (void) fi;
- (void) flags;
- return ftpfs_getdir(path, filler, buf);
- }
- static size_t write_data(void *ptr, size_t size, size_t nmemb, void *data) {
- struct ftpfs_file* fh = (struct ftpfs_file*)data;
- if (fh == NULL) return 0;
- size_t to_copy = size * nmemb;
- if (to_copy > fh->buf.len - fh->copied) {
- to_copy = fh->buf.len - fh->copied;
- }
- DEBUG(2, "write_data: %zu\n", to_copy);
- DEBUG(3, "%*s\n", (int)to_copy, (char*)ptr);
- memcpy(ptr, fh->buf.p + fh->copied, to_copy);
- fh->copied += to_copy;
- return to_copy;
- }
- static size_t read_data(void *ptr, size_t size, size_t nmemb, void *data) {
- struct buffer* buf = (struct buffer*)data;
- if (buf == NULL) return size * nmemb;
- if (buf_add_mem(buf, ptr, size * nmemb) == -1)
- return 0;
- DEBUG(2, "read_data: %zu\n", size * nmemb);
- DEBUG(3, "%*s\n", (int)(size * nmemb), (char*)ptr);
- return size * nmemb;
- }
- static int ftpfs_getattr(const char* path, struct stat* sbuf) {
- int err = 0;
- CURLcode curl_res;
- if (strcmp(path, "/") == 0) {
- sbuf->st_mode = S_IFDIR | 0755;
- sbuf->st_nlink = 2;
- return err;
- }
- char* dir_path = get_dir_path(path);
- DEBUG(1, "ftpfs_getattr: %s dir_path=%s\n", path, dir_path);
- struct buffer buf;
- buf_init(&buf);
- pthread_mutex_lock(&ftpfs.lock);
- cancel_previous_multi();
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, dir_path);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
- curl_res = curl_easy_perform(ftpfs.connection);
- pthread_mutex_unlock(&ftpfs.lock);
- if (curl_res != 0) {
- DEBUG(1, "%s\n", error_buf);
- }
- buf_null_terminate(&buf);
- char* name = strrchr(path, '/');
- ++name;
- err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
- name, sbuf, NULL, 0, 0, 0, NULL);
- free(dir_path);
- buf_free(&buf);
- if (err) return op_return(-ENOENT, "ftpfs_getattr");
- return 0;
- }
- static int check_running() {
- int running_handles = 0;
- curl_multi_perform(ftpfs.multi, &running_handles);
- return running_handles;
- }
- static size_t ftpfs_read_chunk(const char* full_path, char* rbuf,
- size_t size, off_t offset,
- struct fuse_file_info* fi,
- int update_offset) {
- int running_handles = 0;
- int err = 0;
- struct ftpfs_file* fh = get_ftpfs_file(fi);
- DEBUG(2, "ftpfs_read_chunk: %s %p %zu %lld %p %p\n",
- full_path, rbuf, size, offset, fi, fh);
- pthread_mutex_lock(&ftpfs.lock);
- DEBUG(2, "buffer size: %zu %lld\n", fh->buf.len, fh->buf.begin_offset);
- if ((fh->buf.len < size + offset - fh->buf.begin_offset) ||
- offset < fh->buf.begin_offset ||
- offset > fh->buf.begin_offset + fh->buf.len) {
- // We can't answer this from cache
- if (ftpfs.current_fh != fh ||
- offset < fh->buf.begin_offset ||
- offset > fh->buf.begin_offset + fh->buf.len ||
- !check_running()) {
- DEBUG(1, "We need to restart the connection %p\n", ftpfs.connection);
- DEBUG(2, "current_fh=%p fh=%p\n", ftpfs.current_fh, fh);
- DEBUG(2, "buf.begin_offset=%lld offset=%lld\n", fh->buf.begin_offset, offset);
- buf_clear(&fh->buf);
- fh->buf.begin_offset = offset;
- ftpfs.current_fh = fh;
- cancel_previous_multi();
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &fh->buf);
- if (offset) {
- char range[15];
- snprintf(range, 15, "%lld-", (long long) offset);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_RANGE, range);
- }
- CURLMcode curlMCode = curl_multi_add_handle(ftpfs.multi, ftpfs.connection);
- if (curlMCode) {
- fprintf(stderr, "curl_multi_remove_handle problem: %d %s\n",
- curlMCode, curl_multi_strerror(curlMCode));
- exit(1);
- }
- ftpfs.attached_to_multi = 1;
- }
- while(CURLM_CALL_MULTI_PERFORM ==
- curl_multi_perform(ftpfs.multi, &running_handles));
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_RANGE, NULL);
- while ((fh->buf.len < size + offset - fh->buf.begin_offset) &&
- running_handles) {
- struct timeval timeout;
- int rc; /* select() return code */
- fd_set fdread;
- fd_set fdwrite;
- fd_set fdexcep;
- int maxfd;
- FD_ZERO(&fdread);
- FD_ZERO(&fdwrite);
- FD_ZERO(&fdexcep);
- /* set a suitable timeout to play around with */
- timeout.tv_sec = 1;
- timeout.tv_usec = 0;
- /* get file descriptors from the transfers */
- curl_multi_fdset(ftpfs.multi, &fdread, &fdwrite, &fdexcep, &maxfd);
- rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
- if (rc == -1) {
- err = 1;
- break;
- }
- while(CURLM_CALL_MULTI_PERFORM ==
- curl_multi_perform(ftpfs.multi, &running_handles));
- }
- if (running_handles == 0) {
- int msgs_left = 1;
- while (msgs_left) {
- CURLMsg* msg = curl_multi_info_read(ftpfs.multi, &msgs_left);
- if (msg == NULL ||
- msg->msg != CURLMSG_DONE ||
- msg->data.result != CURLE_OK) {
- DEBUG(1, "error: curl_multi_info %d\n", msg->msg);
- err = 1;
- }
- }
- }
- }
- size_t to_copy = fh->buf.len + fh->buf.begin_offset - offset;
- size = size > to_copy ? to_copy : size;
- if (rbuf) {
- memcpy(rbuf, fh->buf.p + offset - fh->buf.begin_offset, size);
- }
- if (update_offset) {
- fh->last_offset = offset + size;
- }
- // Check if the buffer is growing and we can delete a part of it
- if (fh->can_shrink && fh->buf.len > MAX_BUFFER_LEN) {
- DEBUG(2, "Shrinking buffer from %zu to %zu bytes\n",
- fh->buf.len, to_copy - size);
- memmove(fh->buf.p,
- fh->buf.p + offset - fh->buf.begin_offset + size,
- to_copy - size);
- fh->buf.len = to_copy - size;
- fh->buf.begin_offset = offset + size;
- }
- pthread_mutex_unlock(&ftpfs.lock);
- if (err) return CURLFTPFS_BAD_READ;
- return size;
- }
- static void set_common_curl_stuff(CURL* easy);
- static size_t write_data_bg(void *ptr, size_t size, size_t nmemb, void *data) {
- struct ftpfs_file *fh = data;
- unsigned to_copy = size * nmemb;
- if (!fh->isready) {
- sem_post(&fh->ready);
- fh->isready = 1;
- }
- if (fh->stream_buf.len == 0 && fh->written_flag) {
- sem_post(&fh->data_written); /* ftpfs_write can return */
- }
- sem_wait(&fh->data_avail);
- DEBUG(2, "write_data_bg: data_avail eof=%d\n", fh->eof);
- if (fh->eof)
- return 0;
- DEBUG(2, "write_data_bg: %d %zd\n", to_copy, fh->stream_buf.len);
- if (to_copy > fh->stream_buf.len)
- to_copy = fh->stream_buf.len;
- memcpy(ptr, fh->stream_buf.p, to_copy);
- if (fh->stream_buf.len > to_copy) {
- size_t newlen = fh->stream_buf.len - to_copy;
- memmove(fh->stream_buf.p, fh->stream_buf.p + to_copy, newlen);
- fh->stream_buf.len = newlen;
- sem_post(&fh->data_avail);
- DEBUG(2, "write_data_bg: data_avail\n");
- } else {
- fh->stream_buf.len = 0;
- fh->written_flag = 1;
- sem_post(&fh->data_need);
- DEBUG(2, "write_data_bg: data_need\n");
- }
- return to_copy;
- }
- int write_thread_ctr = 0;
- static void *ftpfs_write_thread(void *data) {
- struct ftpfs_file *fh = data;
- DEBUG(2, "enter streaming write thread #%d path=%s pos=%lld\n", ++write_thread_ctr, fh->full_path, fh->pos);
- curl_easy_setopt_or_die(fh->write_conn, CURLOPT_URL, fh->full_path);
- curl_easy_setopt_or_die(fh->write_conn, CURLOPT_UPLOAD, 1);
- curl_easy_setopt_or_die(fh->write_conn, CURLOPT_INFILESIZE, -1L);
- curl_easy_setopt_or_die(fh->write_conn, CURLOPT_READFUNCTION, write_data_bg);
- curl_easy_setopt_or_die(fh->write_conn, CURLOPT_READDATA, fh);
- curl_easy_setopt_or_die(fh->write_conn, CURLOPT_LOW_SPEED_LIMIT, 1);
- curl_easy_setopt_or_die(fh->write_conn, CURLOPT_LOW_SPEED_TIME, 60);
- fh->curl_error_buffer[0] = '\0';
- curl_easy_setopt_or_die(fh->write_conn, CURLOPT_ERRORBUFFER, fh->curl_error_buffer);
- if (fh->pos > 0) {
- /* resuming a streaming write */
- //snprintf(range, 15, "%lld-", (long long) fh->pos);
- //curl_easy_setopt_or_die(fh->write_conn, CURLOPT_RANGE, range);
- curl_easy_setopt_or_die(fh->write_conn, CURLOPT_APPEND, 1);
- //curl_easy_setopt_or_die(fh->write_conn, CURLOPT_RESUME_FROM_LARGE, (curl_off_t)fh->pos);
- }
- CURLcode curl_res = curl_easy_perform(fh->write_conn);
- curl_easy_setopt_or_die(fh->write_conn, CURLOPT_UPLOAD, 0);
- if (!fh->isready)
- sem_post(&fh->ready);
- if (curl_res != CURLE_OK)
- {
- DEBUG(1, "write problem: %d(%s) text=%s\n", curl_res, curl_easy_strerror(curl_res), fh->curl_error_buffer);
- fh->write_fail_cause = curl_res;
- /* problem - let ftpfs_write continue to avoid hang */
- sem_post(&fh->data_need);
- }
- DEBUG(2, "leaving streaming write thread #%d curl_res=%d\n", write_thread_ctr--, curl_res);
- sem_post(&fh->data_written); /* ftpfs_write may return */
- return NULL;
- }
- /* returns 1 on success, 0 on failure */
- static int start_write_thread(struct ftpfs_file *fh) {
- if (fh->write_conn != NULL)
- {
- fprintf(stderr, "assert fh->write_conn == NULL failed!\n");
- exit(1);
- }
- fh->written_flag=0;
- fh->isready=0;
- fh->eof=0;
- sem_init(&fh->data_avail, 0, 0);
- sem_init(&fh->data_need, 0, 0);
- sem_init(&fh->data_written, 0, 0);
- sem_init(&fh->ready, 0, 0);
- fh->write_conn = curl_easy_init();
- if (fh->write_conn == NULL) {
- fprintf(stderr, "Error initializing libcurl\n");
- return 0;
- } else {
- int err;
- set_common_curl_stuff(fh->write_conn);
- err = pthread_create(&fh->thread_id, NULL, ftpfs_write_thread, fh);
- if (err) {
- fprintf(stderr, "failed to create thread: %s\n", strerror(err));
- /* FIXME: destroy curl_easy */
- return 0;
- }
- }
- return 1;
- }
- static int finish_write_thread(struct ftpfs_file *fh) {
- if (fh->write_fail_cause == CURLE_OK) {
- sem_wait(&fh->data_need); /* only wait when there has been no error */
- }
- sem_post(&fh->data_avail);
- fh->eof = 1;
- pthread_join(fh->thread_id, NULL);
- DEBUG(2, "finish_write_thread after pthread_join. write_fail_cause=%d\n", fh->write_fail_cause);
- curl_easy_cleanup(fh->write_conn);
- fh->write_conn = NULL;
- sem_destroy(&fh->data_avail);
- sem_destroy(&fh->data_need);
- sem_destroy(&fh->data_written);
- sem_destroy(&fh->ready);
- if (fh->write_fail_cause != CURLE_OK)
- {
- return -EIO;
- }
- return 0;
- }
- static void free_ftpfs_file(struct ftpfs_file *fh) {
- buf_free(&fh->buf);
- buf_free(&fh->stream_buf);
- if (fh->write_conn)
- curl_easy_cleanup(fh->write_conn);
- g_free(fh->full_path);
- g_free(fh->open_path);
- sem_destroy(&fh->data_avail);
- sem_destroy(&fh->data_need);
- sem_destroy(&fh->data_written);
- sem_destroy(&fh->ready);
- free(fh);
- }
- static int create_empty_file(const char * path) {
- int err = 0;
- char *full_path = get_full_path(path);
- pthread_mutex_lock(&ftpfs.lock);
- cancel_previous_multi();
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_INFILESIZE, 0);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_UPLOAD, 1);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_READDATA, NULL);
- CURLcode curl_res = curl_easy_perform(ftpfs.connection);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_UPLOAD, 0);
- pthread_mutex_unlock(&ftpfs.lock);
- if (curl_res != 0) {
- err = -EPERM;
- }
- free(full_path);
- return err;
- }
- static int ftpfs_mknod(const char* path, mode_t mode, dev_t rdev);
- static int ftpfs_chmod(const char* path, mode_t mode);
- static char * flags_to_string(int flags) {
- char * access_mode_str = NULL;
- if ((flags & O_ACCMODE) == O_WRONLY)
- access_mode_str = "O_WRONLY";
- else if ((flags & O_ACCMODE) == O_RDWR)
- access_mode_str = "O_RDWR";
- else if ((flags & O_ACCMODE) == O_RDONLY)
- access_mode_str = "O_RDONLY";
- return g_strdup_printf("access_mode=%s, flags=%s%s%s%s",
- access_mode_str,
- (flags & O_CREAT) ? "O_CREAT " : "",
- (flags & O_TRUNC) ? "O_TRUNC " : "",
- (flags & O_EXCL) ? "O_EXCL " : "",
- (flags & O_APPEND) ? "O_APPEND " : "");
- }
- static int test_exists(const char* path) {
- struct stat sbuf;
- return ftpfs_getattr(path, &sbuf);
- }
- static __off_t test_size(const char* path) {
- struct stat sbuf;
- int err = ftpfs_getattr(path, &sbuf);
- if (err)
- return err;
- return sbuf.st_size;
- }
- static int ftpfs_open_common(const char* path, mode_t mode,
- struct fuse_file_info* fi) {
- char * flagsAsStr = flags_to_string(fi->flags);
- DEBUG(2, "ftpfs_open_common: %s\n", flagsAsStr);
- int err = 0;
- struct ftpfs_file* fh =
- (struct ftpfs_file*) malloc(sizeof(struct ftpfs_file));
- memset(fh, 0, sizeof(*fh));
- buf_init(&fh->buf);
- fh->mode = mode;
- fh->dirty = 0;
- fh->copied = 0;
- fh->last_offset = 0;
- fh->can_shrink = 0;
- buf_init(&fh->stream_buf);
- /* sem_init(&fh->data_avail, 0, 0);
- sem_init(&fh->data_need, 0, 0);
- sem_init(&fh->data_written, 0, 0);
- sem_init(&fh->ready, 0, 0); */
- fh->open_path = strdup(path);
- fh->full_path = get_full_path(path);
- fh->written_flag = 0;
- fh->write_fail_cause = CURLE_OK;
- fh->curl_error_buffer[0] = '\0';
- fh->write_may_start = 0;
- fi->fh = (unsigned long) fh;
- if ((fi->flags & O_ACCMODE) == O_RDONLY) {
- if (fi->flags & O_CREAT) {
- err = ftpfs_mknod(path, (mode & 07777) | S_IFREG, 0);
- } else {
- // If it's read-only, we can load the file a bit at a time, as necessary.
- DEBUG(1, "opening %s O_RDONLY\n", path);
- fh->can_shrink = 1;
- size_t size = ftpfs_read_chunk(fh->full_path, NULL, 1, 0, fi, 0);
- if (size == CURLFTPFS_BAD_READ) {
- DEBUG(1, "initial read failed size=%d\n", size);
- err = -EACCES;
- }
- }
- }
- else if ((fi->flags & O_ACCMODE) == O_RDWR || (fi->flags & O_ACCMODE) == O_WRONLY)
- {
- #ifndef CURLFTPFS_O_RW_WORKAROUND
- if ((fi->flags & O_ACCMODE) == O_RDWR)
- {
- err = -ENOTSUP;
- goto fin;
- }
- #endif
- if ((fi->flags & O_APPEND))
- {
- DEBUG(1, "opening %s with O_APPEND - not supported!\n", path);
- err = -ENOTSUP;
- }
- if ((fi->flags & O_EXCL))
- {
- DEBUG(1, "opening %s with O_EXCL - testing existence\n", path);
- int exists_r = test_exists(path);
- if (exists_r != -ENOENT)
- err = -EACCES;
- }
- if (!err)
- {
- if ((fi->flags & O_CREAT) || (fi->flags & O_TRUNC))
- {
- DEBUG(1, "opening %s for writing with O_CREAT or O_TRUNC. write thread will start now\n", path);
- fh->write_may_start=1;
- if (start_write_thread(fh))
- {
- sem_wait(&fh->ready);
- /* chmod makes only sense on O_CREAT */
- if (fi->flags & O_CREAT) ftpfs_chmod(path, mode);
- sem_post(&fh->data_need);
- }
- else
- {
- err = -EIO;
- }
- }
- else
- {
- /* in this case we have to start writing later */
- DEBUG(1, "opening %s for writing without O_CREAT or O_TRUNC. write thread will start after ftruncate\n", path);
- /* expecting ftruncate */
- fh->write_may_start=0;
- }
- }
- } else {
- err = -EIO;
- }
- fin:
- if (err)
- free_ftpfs_file(fh);
- g_free(flagsAsStr);
- return op_return(err, "ftpfs_open");
- }
- static int ftpfs_open(const char* path, struct fuse_file_info* fi) {
- return ftpfs_open_common(path, 0, fi);
- }
- #if FUSE_VERSION >= 25
- static int ftpfs_create(const char* path, mode_t mode,
- struct fuse_file_info* fi) {
- return ftpfs_open_common(path, mode, fi);
- }
- #endif
- static int ftpfs_read(const char* path, char* rbuf, size_t size, off_t offset,
- struct fuse_file_info* fi) {
- int ret;
- struct ftpfs_file *fh = get_ftpfs_file(fi);
- 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);
- if (fh->pos>0 || fh->write_conn!=NULL)
- {
- fprintf(stderr, "in read/write mode we cannot read from a file that has already been written to\n");
- return op_return(-EIO, "ftpfs_read");
- }
- char *full_path = get_full_path(path);
- size_t size_read = ftpfs_read_chunk(full_path, rbuf, size, offset, fi, 1);
- free(full_path);
- if (size_read == CURLFTPFS_BAD_READ) {
- ret = -EIO;
- } else {
- ret = size_read;
- }
- if (ret<0) op_return(ret, "ftpfs_read");
- return ret;
- }
- static int ftpfs_mknod(const char* path, mode_t mode, dev_t rdev) {
- (void) rdev;
- int err = 0;
- DEBUG(1, "ftpfs_mknode: mode=%d\n", (int)mode);
- if ((mode & S_IFMT) != S_IFREG)
- return -EPERM;
- err = create_empty_file(path);
- if (!err)
- ftpfs_chmod(path, mode);
- return op_return(err, "ftpfs_mknod");
- }
- static int ftpfs_chmod(const char* path, mode_t mode) {
- (void) mode;
- (void) path;
- int err = 0;
- // We can only process a subset of the mode - so strip
- // to supported subset
- /* int mode_c = mode - (mode / 0x1000 * 0x1000);*/
- /**/
- /* struct curl_slist* header = NULL;*/
- /* char* full_path = get_dir_path(path);*/
- /* char* filename = get_file_name(path);*/
- /* char* cmd = g_strdup_printf("SITE CHMOD %.3o %s", mode_c, filename);*/
- /* struct buffer buf;*/
- /* buf_init(&buf);*/
- /**/
- /* header = curl_slist_append(header, cmd);*/
- /**/
- /* pthread_mutex_lock(&ftpfs.lock);*/
- /* cancel_previous_multi();*/
- /* curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);*/
- /* curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);*/
- /* curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);*/
- /* curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);*/
- /* CURLcode curl_res = curl_easy_perform(ftpfs.connection);*/
- /* curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);*/
- /* curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);*/
- /* pthread_mutex_unlock(&ftpfs.lock);*/
- /**/
- /* if (curl_res != 0) {*/
- /* err = -EPERM;*/
- /* }*/
- /**/
- /* buf_free(&buf);*/
- /* curl_slist_free_all(header);*/
- /* free(full_path);*/
- /* free(filename);*/
- /* free(cmd); */
- return op_return(err, "ftpfs_chmod");
- }
- static int ftpfs_chown(const char* path, uid_t uid, gid_t gid, struct fuse_file_info *c) {
- (void) c;
- int err = 0;
- DEBUG(1, "ftpfs_chown: %d %d\n", (int)uid, (int)gid);
- struct curl_slist* header = NULL;
- char* full_path = get_dir_path(path);
- char* filename = get_file_name(path);
- char* cmd = g_strdup_printf("SITE CHUID %i %s", uid, filename);
- char* cmd2 = g_strdup_printf("SITE CHGID %i %s", gid, filename);
- struct buffer buf;
- buf_init(&buf);
- header = curl_slist_append(header, cmd);
- header = curl_slist_append(header, cmd2);
- pthread_mutex_lock(&ftpfs.lock);
- cancel_previous_multi();
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
- CURLcode curl_res = curl_easy_perform(ftpfs.connection);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
- pthread_mutex_unlock(&ftpfs.lock);
- if (curl_res != 0) {
- err = -EPERM;
- }
- buf_free(&buf);
- curl_slist_free_all(header);
- free(full_path);
- free(filename);
- free(cmd);
- free(cmd2);
- return op_return(err, "ftpfs_chown");
- }
- static int ftpfs_truncate(const char* path, off_t offset, struct fuse_file_info *c) {
- (void) c;
- DEBUG(1, "ftpfs_truncate: %s len=%lld\n", path, offset);
- /* we can't use ftpfs_mknod here, because we don't know the right permissions */
- if (offset == 0) return op_return(create_empty_file(path), "ftpfs_truncate");
- /* fix openoffice problem, truncating exactly to file length */
- __off_t size = (long long int)test_size(path);
- DEBUG(1, "ftpfs_truncate: %s check filesize=%lld\n", path, (long long int)size);
- if (offset == size)
- return op_return(0, "ftpfs_ftruncate");
- DEBUG(1, "ftpfs_truncate problem: %s offset != 0 or filesize=%lld != offset\n", path, (long long int)size);
- return op_return(-EPERM, "ftpfs_truncate");
- }
- static int ftpfs_utime(const char* path, const struct timespec *t, struct fuse_file_info *c) {
- (void) path;
- (void) t;
- (void) c;
- return op_return(0, "ftpfs_utime");
- }
- static int ftpfs_rmdir(const char* path) {
- int err = 0;
- struct curl_slist* header = NULL;
- char* full_path = get_dir_path(path);
- char* filename = get_file_name(path);
- char* cmd = g_strdup_printf("RMD %s", filename);
- struct buffer buf;
- buf_init(&buf);
- DEBUG(2, "%s\n", full_path);
- DEBUG(2, "%s\n", cmd);
- header = curl_slist_append(header, cmd);
- pthread_mutex_lock(&ftpfs.lock);
- cancel_previous_multi();
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
- CURLcode curl_res = curl_easy_perform(ftpfs.connection);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
- pthread_mutex_unlock(&ftpfs.lock);
- if (curl_res != 0) {
- err = -EPERM;
- }
- buf_free(&buf);
- curl_slist_free_all(header);
- free(full_path);
- free(filename);
- free(cmd);
- return op_return(err, "ftpfs_rmdir");
- }
- static int ftpfs_mkdir(const char* path, mode_t mode) {
- int err = 0;
- struct curl_slist* header = NULL;
- char* full_path = get_dir_path(path);
- char* filename = get_file_name(path);
- char* cmd = g_strdup_printf("MKD %s", filename);
- struct buffer buf;
- buf_init(&buf);
- header = curl_slist_append(header, cmd);
- pthread_mutex_lock(&ftpfs.lock);
- cancel_previous_multi();
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
- CURLcode curl_res = curl_easy_perform(ftpfs.connection);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
- pthread_mutex_unlock(&ftpfs.lock);
- if (curl_res != 0) {
- err = -EPERM;
- }
- buf_free(&buf);
- curl_slist_free_all(header);
- free(full_path);
- free(filename);
- free(cmd);
- if (!err)
- ftpfs_chmod(path, mode);
- return op_return(err, "ftpfs_mkdir");
- }
- static int ftpfs_unlink(const char* path) {
- int err = 0;
- struct curl_slist* header = NULL;
- char* full_path = get_dir_path(path);
- char* filename = get_file_name(path);
- char* cmd = g_strdup_printf("DELE %s", filename);
- struct buffer buf;
- buf_init(&buf);
- header = curl_slist_append(header, cmd);
- pthread_mutex_lock(&ftpfs.lock);
- cancel_previous_multi();
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, full_path);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
- CURLcode curl_res = curl_easy_perform(ftpfs.connection);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
- pthread_mutex_unlock(&ftpfs.lock);
- if (curl_res != 0) {
- err = -EPERM;
- }
- buf_free(&buf);
- curl_slist_free_all(header);
- free(full_path);
- free(filename);
- free(cmd);
- return op_return(err, "ftpfs_unlink");
- }
- static int ftpfs_write(const char *path, const char *wbuf, size_t size,
- off_t offset, struct fuse_file_info *fi) {
- (void) path;
- struct ftpfs_file *fh = get_ftpfs_file(fi);
- 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);
- if (fh->write_fail_cause != CURLE_OK)
- {
- DEBUG(1, "previous write failed. cause=%d\n", fh->write_fail_cause);
- return -EIO;
- }
- if (!fh->write_conn && fh->pos == 0 && offset == 0)
- {
- DEBUG(1, "ftpfs_write: starting a streaming write at pos=%lld\n", fh->pos);
- /* check if the file has been truncated to zero or has been newly created */
- if (!fh->write_may_start)
- {
- long long size = (long long int)test_size(path);
- if (size != 0)
- {
- fprintf(stderr, "ftpfs_write: start writing with no previous truncate not allowed! size check rval=%lld\n", size);
- return op_return(-EIO, "ftpfs_write");
- }
- }
- int success = start_write_thread(fh);
- if (!success)
- {
- return op_return(-EIO, "ftpfs_write");
- }
- sem_wait(&fh->ready);
- sem_post(&fh->data_need);
- }
- if (!fh->write_conn && fh->pos >0 && offset == fh->pos)
- {
- /* resume a streaming write */
- DEBUG(1, "ftpfs_write: resuming a streaming write at pos=%lld\n", fh->pos);
- int success = start_write_thread(fh);
- if (!success)
- {
- return op_return(-EIO, "ftpfs_write");
- }
- sem_wait(&fh->ready);
- sem_post(&fh->data_need);
- }
- if (fh->write_conn) {
- sem_wait(&fh->data_need);
- if (offset != fh->pos) {
- DEBUG(1, "non-sequential write detected -> fail\n");
- sem_post(&fh->data_avail);
- finish_write_thread(fh);
- return op_return(-EIO, "ftpfs_write");
- } else {
- if (buf_add_mem(&fh->stream_buf, wbuf, size) == -1) {
- sem_post(&fh->data_need);
- return op_return(-ENOMEM, "ftpfs_write");
- }
- fh->pos += size;
- /* wake up write_data_bg */
- sem_post(&fh->data_avail);
- /* wait until libcurl has completely written the current chunk or finished/failed */
- sem_wait(&fh->data_written);
- fh->written_flag = 0;
- if (fh->write_fail_cause != CURLE_OK)
- {
- /* TODO: on error we should problably unlink the target file */
- DEBUG(1, "writing failed. cause=%d\n", fh->write_fail_cause);
- return op_return(-EIO, "ftpfs_write");
- }
- }
- }
- return size;
- }
- static int ftpfs_flush(const char *path, struct fuse_file_info *fi) {
- int err = 0;
- struct ftpfs_file* fh = get_ftpfs_file(fi);
- DEBUG(1, "ftpfs_flush: buf.len=%zu buf.pos=%lld write_conn=%d\n", fh->buf.len, fh->pos, fh->write_conn!=0);
- if (fh->write_conn) {
- err = finish_write_thread(fh);
- if (err) return op_return(err, "ftpfs_flush");
- struct stat sbuf;
- /* check if the resulting file has the correct size
- this is important, because we use APPE for continuing
- writing after a premature flush */
- err = ftpfs_getattr(path, &sbuf);
- if (err) return op_return(err, "ftpfs_flush");
- if (sbuf.st_size != fh->pos)
- {
- fh->write_fail_cause = -999;
- fprintf(stderr, "ftpfs_flush: check filesize problem: size=%lld expected=%lld\n", sbuf.st_size, fh->pos);
- return op_return(-EIO, "ftpfs_flush");
- }
- return 0;
- }
- if (!fh->dirty) return 0;
- return op_return(-EIO, "ftpfs_flush");
- }
- static int ftpfs_fsync(const char *path, int isdatasync,
- struct fuse_file_info *fi) {
- (void) isdatasync;
- DEBUG(1, "ftpfs_fsync %s\n", path);
- return ftpfs_flush(path, fi);
- }
- static int ftpfs_release(const char* path, struct fuse_file_info* fi) {
- struct ftpfs_file* fh = get_ftpfs_file(fi);
- ftpfs_flush(path, fi);
- pthread_mutex_lock(&ftpfs.lock);
- if (ftpfs.current_fh == fh) {
- ftpfs.current_fh = NULL;
- }
- pthread_mutex_unlock(&ftpfs.lock);
- /*
- if (fh->write_conn) {
- finish_write_thread(fh);
- }
- */
- free_ftpfs_file(fh);
- return op_return(0, "ftpfs_release");
- }
- static int ftpfs_rename(const char* from, const char* to, unsigned int p) {
- (void) p;
- DEBUG(1, "ftpfs_rename from %s to %s\n", from, to);
- int err = 0;
- char* rnfr = g_strdup_printf("RNFR %s", from + 1);
- char* rnto = g_strdup_printf("RNTO %s", to + 1);
- struct buffer buf;
- buf_init(&buf);
- struct curl_slist* header = NULL;
- if (ftpfs.codepage) {
- convert_charsets(ftpfs.iocharset, ftpfs.codepage, &rnfr);
- convert_charsets(ftpfs.iocharset, ftpfs.codepage, &rnto);
- }
- header = curl_slist_append(header, rnfr);
- header = curl_slist_append(header, rnto);
- pthread_mutex_lock(&ftpfs.lock);
- cancel_previous_multi();
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, header);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, ftpfs.host);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, ftpfs.safe_nobody);
- CURLcode curl_res = curl_easy_perform(ftpfs.connection);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_POSTQUOTE, NULL);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_NOBODY, 0);
- pthread_mutex_unlock(&ftpfs.lock);
- if (curl_res != 0) {
- err = -EPERM;
- }
- buf_free(&buf);
- curl_slist_free_all(header);
- free(rnfr);
- free(rnto);
- return op_return(err, "ftpfs_rename");
- }
- static int ftpfs_readlink(const char *path, char *linkbuf, size_t size) {
- (void) linkbuf;
- (void) size;
- int err = 0;
- CURLcode curl_res;
- char* dir_path = get_dir_path(path);
- DEBUG(2, "dir_path: %s %s\n", path, dir_path);
- struct buffer buf;
- buf_init(&buf);
- pthread_mutex_lock(&ftpfs.lock);
- cancel_previous_multi();
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, dir_path);
- curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &buf);
- curl_res = curl_easy_perform(ftpfs.connection);
- pthread_mutex_unlock(&ftpfs.lock);
- if (curl_res != 0) {
- DEBUG(1, "%s\n", error_buf);
- }
- buf_null_terminate(&buf);
- char* name = strrchr(path, '/');
- ++name;
- /* err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,*/
- /* name, NULL, linkbuf, size, NULL );*/
- free(dir_path);
- buf_free(&buf);
- if (err) return op_return(-ENOENT, "ftpfs_readlink");
- return op_return(0, "ftpfs_readlink");
- }
- static int ftpfs_statfs(const char *path, struct statvfs *buf) {
- (void) path;
- buf->f_namemax = 255;
- buf->f_bsize = ftpfs.blksize;
- buf->f_frsize = 512;
- buf->f_blocks = 999999999 * 2;
- buf->f_bfree = 999999999 * 2;
- buf->f_bavail = 999999999 * 2;
- buf->f_files = 999999999;
- buf->f_ffree = 999999999;
- return op_return(0, "ftpfs_statfs");
- }
- static void *ftpfs_init(struct fuse_conn_info *conn, struct fuse_config *cfg) {
- (void) conn;
- (void) cfg;
- return 0;
- }
- static int __ftpfs_getattr(const char *a, struct stat *b, struct fuse_file_info *c) {
- (void) c;
- return ftpfs_getattr(a, b);
- }
- static int __ftpfs_chmod(const char *a, mode_t b, struct fuse_file_info *c) {
- (void) c;
- return ftpfs_chmod(a, b);
- }
- static struct fuse_operations ftpfs_oper = {
- .init = ftpfs_init,
- .getattr = __ftpfs_getattr,
- .readlink = ftpfs_readlink,
- .mknod = ftpfs_mknod,
- .mkdir = ftpfs_mkdir,
- .readdir = ftpfs_readdir,
- // .symlink = ftpfs_symlink,
- .unlink = ftpfs_unlink,
- .rmdir = ftpfs_rmdir,
- .rename = ftpfs_rename,
- .chmod = __ftpfs_chmod,
- .chown = ftpfs_chown,
- .truncate = ftpfs_truncate,
- .utimens = ftpfs_utime,
- .open = ftpfs_open,
- .flush = ftpfs_flush,
- .fsync = ftpfs_fsync,
- .release = ftpfs_release,
- .read = ftpfs_read,
- .write = ftpfs_write,
- .statfs = ftpfs_statfs,
- .create = ftpfs_create,
- };
- static int ftpfs_opt_proc(void* data, const char* arg, int key,
- struct fuse_args* outargs) {
- (void) data;
- (void) outargs;
- switch (key) {
- case FUSE_OPT_KEY_OPT:
- return 1;
- case FUSE_OPT_KEY_NONOPT:
- if (!ftpfs.host) {
- const char* prefix = "";
- if (strncmp(arg, "ftp://", 6) && strncmp(arg, "ftps://", 7)) {
- prefix = "ftp://";
- }
- ftpfs.host = g_strdup_printf("%s%s%s", prefix, arg,
- arg[strlen(arg)-1] == '/' ? "" : "/");
- return 0;
- } else if (!ftpfs.mountpoint)
- ftpfs.mountpoint = strdup(arg);
- return 1;
- case KEY_HELP:
- usage(outargs->argv[0]);
- fuse_opt_add_arg(outargs, "-ho");
- exit(1);
- case KEY_VERBOSE:
- ftpfs.verbose = 1;
- return 0;
- case KEY_VERSION:
- fprintf(stderr, "curlftpfs %s libcurl/%s fuse/%u.%u\n",
- VERSION,
- ftpfs.curl_version->version,
- FUSE_MAJOR_VERSION,
- FUSE_MINOR_VERSION);
- exit(1);
- default:
- exit(1);
- }
- }
- static void usage(const char* progname) {
- fprintf(stderr,
- "usage: %s <ftphost> <mountpoint>\n"
- "\n"
- "CurlFtpFS options:\n"
- " -o opt,[opt...] ftp options\n"
- " -v --verbose make libcurl print verbose debug\n"
- " -h --help print help\n"
- " -V --version print version\n"
- "\n"
- "FTP options:\n"
- " ftpfs_debug print some debugging information\n"
- " transform_symlinks prepend mountpoint to absolute symlink targets\n"
- " disable_epsv use PASV, without trying EPSV first (default)\n"
- " enable_epsv try EPSV before reverting to PASV\n"
- " skip_pasv_ip skip the IP address for PASV\n"
- " ftp_port=STR use PORT with address instead of PASV\n"
- " disable_eprt use PORT, without trying EPRT first\n"
- " ftp_method [multicwd/singlecwd] Control CWD usage\n"
- " custom_list=STR Command used to list files. Defaults to \"LIST -a\"\n"
- " tcp_nodelay use the TCP_NODELAY option\n"
- " connect_timeout=N maximum time allowed for connection in seconds\n"
- " ssl enable SSL/TLS for both control and data connections\n"
- " ssl_control enable SSL/TLS only for control connection\n"
- " ssl_try try SSL/TLS first but connect anyway\n"
- " no_verify_hostname does not verify the hostname (SSL)\n"
- " no_verify_peer does not verify the peer (SSL)\n"
- " cert=STR client certificate file (SSL)\n"
- " cert_type=STR certificate file type (DER/PEM/ENG) (SSL)\n"
- " key=STR private key file name (SSL)\n"
- " key_type=STR private key file type (DER/PEM/ENG) (SSL)\n"
- " pass=STR pass phrase for the private key (SSL)\n"
- " engine=STR crypto engine to use (SSL)\n"
- " cacert=STR file with CA certificates to verify the peer (SSL)\n"
- " capath=STR CA directory to verify peer against (SSL)\n"
- " ciphers=STR SSL ciphers to use (SSL)\n"
- " interface=STR specify network interface/address to use\n"
- " krb4=STR enable krb4 with specified security level\n"
- " proxy=STR use host:port HTTP proxy\n"
- " proxytunnel operate through a HTTP proxy tunnel (using CONNECT)\n"
- " proxy_anyauth pick \"any\" proxy authentication method\n"
- " proxy_basic use Basic authentication on the proxy\n"
- " proxy_digest use Digest authentication on the proxy\n"
- " proxy_ntlm use NTLM authentication on the proxy\n"
- " httpproxy use a HTTP proxy (default)\n"
- " socks4 use a SOCKS4 proxy\n"
- " socks5 use a SOCKS5 proxy\n"
- " user=STR set server user and password\n"
- " proxy_user=STR set proxy user and password\n"
- " tlsv1 use TLSv1 (SSL)\n"
- " sslv3 use SSLv3 (SSL)\n"
- " ipv4 resolve name to IPv4 address\n"
- " ipv6 resolve name to IPv6 address\n"
- " utf8 try to transfer file list with utf-8 encoding\n"
- " codepage=STR set the codepage the server uses\n"
- " iocharset=STR set the charset used by the client\n"
- "\n"
- "CurlFtpFS cache options: \n"
- " cache=yes|no enable/disable cache (default: yes)\n"
- " cache_timeout=SECS set timeout for stat, dir, link at once\n"
- " default is %d seconds\n"
- " cache_stat_timeout=SECS set stat timeout\n"
- " cache_dir_timeout=SECS set dir timeout\n"
- " cache_link_timeout=SECS set link timeout\n"
- "\n", progname, DEFAULT_CACHE_TIMEOUT_SECS);
- }
- static int ftpfilemethod(const char *str)
- {
- if(!strcmp("singlecwd", str))
- return CURLFTPMETHOD_SINGLECWD;
- if(!strcmp("multicwd", str))
- return CURLFTPMETHOD_MULTICWD;
- DEBUG(1, "unrecognized ftp file method '%s', using default\n", str);
- return CURLFTPMETHOD_MULTICWD;
- }
- static void set_common_curl_stuff(CURL* easy) {
- curl_easy_setopt_or_die(easy, CURLOPT_WRITEFUNCTION, read_data);
- curl_easy_setopt_or_die(easy, CURLOPT_READFUNCTION, write_data);
- curl_easy_setopt_or_die(easy, CURLOPT_ERRORBUFFER, error_buf);
- curl_easy_setopt_or_die(easy, CURLOPT_URL, ftpfs.host);
- curl_easy_setopt_or_die(easy, CURLOPT_NETRC, CURL_NETRC_OPTIONAL);
- curl_easy_setopt_or_die(easy, CURLOPT_NOSIGNAL, 1);
- curl_easy_setopt_or_die(easy, CURLOPT_CUSTOMREQUEST, "LIST -a");
- if (ftpfs.custom_list) {
- curl_easy_setopt_or_die(easy, CURLOPT_CUSTOMREQUEST, ftpfs.custom_list);
- }
- if (ftpfs.tryutf8) {
- // We'll let the slist leak, as it will still be accessible within
- // libcurl. If we ever want to add more commands to CURLOPT_QUOTE, we'll
- // have to think of a better strategy.
- struct curl_slist *slist = NULL;
- // Adding the QUOTE here will make this command be sent with every request.
- // This is necessary to ensure that the server is still in UTF8 mode after
- // we get disconnected and automatically reconnect.
- slist = curl_slist_append(slist, "OPTS UTF8 ON");
- curl_easy_setopt_or_die(easy, CURLOPT_QUOTE, slist);
- }
- if (ftpfs.verbose) {
- curl_easy_setopt_or_die(easy, CURLOPT_VERBOSE, TRUE);
- }
- if (ftpfs.disable_epsv) {
- curl_easy_setopt_or_die(easy, CURLOPT_FTP_USE_EPSV, FALSE);
- }
- if (ftpfs.skip_pasv_ip) {
- curl_easy_setopt_or_die(easy, CURLOPT_FTP_SKIP_PASV_IP, TRUE);
- }
- if (ftpfs.ftp_port) {
- curl_easy_setopt_or_die(easy, CURLOPT_FTPPORT, ftpfs.ftp_port);
- }
- if (ftpfs.disable_eprt) {
- curl_easy_setopt_or_die(easy, CURLOPT_FTP_USE_EPRT, FALSE);
- }
- if (ftpfs.ftp_method) {
- curl_easy_setopt_or_die(easy, CURLOPT_FTP_FILEMETHOD,
- ftpfilemethod(ftpfs.ftp_method));
- }
- if (ftpfs.tcp_nodelay) {
- /* CURLOPT_TCP_NODELAY is not defined in older versions */
- curl_easy_setopt_or_die(easy, CURLOPT_TCP_NODELAY, 1);
- }
- curl_easy_setopt_or_die(easy, CURLOPT_CONNECTTIMEOUT, ftpfs.connect_timeout);
- /* CURLFTPSSL_CONTROL and CURLFTPSSL_ALL should make the connection fail if
- * the server doesn't support SSL but libcurl only honors this beginning
- * with version 7.15.4 */
- if (ftpfs.use_ssl > CURLFTPSSL_TRY &&
- ftpfs.curl_version->version_num <= CURLFTPFS_BAD_SSL) {
- fprintf(stderr,
- "WARNING: you are using libcurl %s.\n"
- "This version of libcurl does not respect the mandatory SSL flag.\n"
- "It will try to send the user and password even if the server doesn't support\n"
- "SSL. Please upgrade to libcurl version 7.15.4 or higher.\n"
- "You can abort the connection now by pressing ctrl+c.\n",
- ftpfs.curl_version->version);
- int i;
- const int time_to_wait = 10;
- for (i = 0; i < time_to_wait; i++) {
- fprintf(stderr, "%d.. ", time_to_wait - i);
- sleep(1);
- }
- fprintf(stderr, "\n");
- }
- curl_easy_setopt_or_die(easy, CURLOPT_FTP_SSL, ftpfs.use_ssl);
- curl_easy_setopt_or_die(easy, CURLOPT_SSLCERT, ftpfs.cert);
- curl_easy_setopt_or_die(easy, CURLOPT_SSLCERTTYPE, ftpfs.cert_type);
- curl_easy_setopt_or_die(easy, CURLOPT_SSLKEY, ftpfs.key);
- curl_easy_setopt_or_die(easy, CURLOPT_SSLKEYTYPE, ftpfs.key_type);
- curl_easy_setopt_or_die(easy, CURLOPT_SSLKEYPASSWD, ftpfs.key_password);
- if (ftpfs.engine) {
- curl_easy_setopt_or_die(easy, CURLOPT_SSLENGINE, ftpfs.engine);
- curl_easy_setopt_or_die(easy, CURLOPT_SSLENGINE_DEFAULT, 1);
- }
- curl_easy_setopt_or_die(easy, CURLOPT_SSL_VERIFYPEER, TRUE);
- if (ftpfs.no_verify_peer) {
- curl_easy_setopt_or_die(easy, CURLOPT_SSL_VERIFYPEER, FALSE);
- }
- if (ftpfs.cacert || ftpfs.capath) {
- if (ftpfs.cacert) {
- curl_easy_setopt_or_die(easy, CURLOPT_CAINFO, ftpfs.cacert);
- }
- if (ftpfs.capath) {
- curl_easy_setopt_or_die(easy, CURLOPT_CAPATH, ftpfs.capath);
- }
- }
- if (ftpfs.ciphers) {
- curl_easy_setopt_or_die(easy, CURLOPT_SSL_CIPHER_LIST, ftpfs.ciphers);
- }
- if (ftpfs.no_verify_hostname) {
- /* The default is 2 which verifies even the host string. This sets to 1
- * which means verify the host but not the string. */
- curl_easy_setopt_or_die(easy, CURLOPT_SSL_VERIFYHOST, 1);
- }
- curl_easy_setopt_or_die(easy, CURLOPT_INTERFACE, ftpfs.interface);
- curl_easy_setopt_or_die(easy, CURLOPT_KRB4LEVEL, ftpfs.krb4);
- if (ftpfs.proxy) {
- curl_easy_setopt_or_die(easy, CURLOPT_PROXY, ftpfs.proxy);
- }
- /* The default proxy type is HTTP */
- if (!ftpfs.proxytype) {
- ftpfs.proxytype = CURLPROXY_HTTP;
- }
- curl_easy_setopt_or_die(easy, CURLOPT_PROXYTYPE, ftpfs.proxytype);
- /* Connection to FTP servers only make sense with a HTTP tunnel proxy */
- if (ftpfs.proxytype == CURLPROXY_HTTP || ftpfs.proxytunnel) {
- curl_easy_setopt_or_die(easy, CURLOPT_HTTPPROXYTUNNEL, TRUE);
- }
- if (ftpfs.proxyanyauth) {
- curl_easy_setopt_or_die(easy, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
- } else if (ftpfs.proxyntlm) {
- curl_easy_setopt_or_die(easy, CURLOPT_PROXYAUTH, CURLAUTH_NTLM);
- } else if (ftpfs.proxydigest) {
- curl_easy_setopt_or_die(easy, CURLOPT_PROXYAUTH, CURLAUTH_DIGEST);
- } else if (ftpfs.proxybasic) {
- curl_easy_setopt_or_die(easy, CURLOPT_PROXYAUTH, CURLAUTH_BASIC);
- }
- curl_easy_setopt_or_die(easy, CURLOPT_USERPWD, ftpfs.user);
- curl_easy_setopt_or_die(easy, CURLOPT_PROXYUSERPWD, ftpfs.proxy_user);
- curl_easy_setopt_or_die(easy, CURLOPT_SSLVERSION, ftpfs.ssl_version);
- curl_easy_setopt_or_die(easy, CURLOPT_IPRESOLVE, ftpfs.ip_version);
- }
- static void checkpasswd(const char *kind, /* for what purpose */
- char **userpwd) /* pointer to allocated string */
- {
- char *ptr;
- if(!*userpwd)
- return;
- ptr = strchr(*userpwd, ':');
- if(!ptr) {
- /* no password present, prompt for one */
- char *passwd;
- char prompt[256];
- size_t passwdlen;
- size_t userlen = strlen(*userpwd);
- char *passptr;
- /* build a nice-looking prompt */
- snprintf(prompt, sizeof(prompt),
- "Enter %s password for user '%s':",
- kind, *userpwd);
- /* get password */
- passwd = getpass(prompt);
- passwdlen = strlen(passwd);
- /* extend the allocated memory area to fit the password too */
- passptr = realloc(*userpwd,
- passwdlen + 1 + /* an extra for the colon */
- userlen + 1); /* an extra for the zero */
- if(passptr) {
- /* append the password separated with a colon */
- passptr[userlen]=':';
- memcpy(&passptr[userlen+1], passwd, passwdlen+1);
- *userpwd = passptr;
- }
- }
- }
- #if FUSE_VERSION == 25
- static int fuse_opt_insert_arg(struct fuse_args *args, int pos,
- const char *arg)
- {
- assert(pos <= args->argc);
- if (fuse_opt_add_arg(args, arg) == -1)
- return -1;
- if (pos != args->argc - 1) {
- char *newarg = args->argv[args->argc - 1];
- memmove(&args->argv[pos + 1], &args->argv[pos],
- sizeof(char *) * (args->argc - pos - 1));
- args->argv[pos] = newarg;
- }
- return 0;
- }
- #endif
- int main(int argc, char** argv) {
- int res;
- struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
- CURLcode curl_res;
- CURL* easy;
- char *tmp;
- // Initialize curl library before we are a multithreaded program
- curl_global_init(CURL_GLOBAL_ALL);
- memset(&ftpfs, 0, sizeof(ftpfs));
- // Set some default values
- ftpfs.curl_version = curl_version_info(CURLVERSION_NOW);
- ftpfs.safe_nobody = ftpfs.curl_version->version_num > CURLFTPFS_BAD_NOBODY;
- ftpfs.blksize = 4096;
- ftpfs.disable_epsv = 1;
- ftpfs.multiconn = 1;
- ftpfs.attached_to_multi = 0;
- if (fuse_opt_parse(&args, &ftpfs, ftpfs_opts, ftpfs_opt_proc) == -1) {
- fprintf(stderr, "fuse_opt_parse() error\n");
- exit(1);
- }
- if (!ftpfs.host) {
- fprintf(stderr, "missing host\n");
- fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
- exit(1);
- }
- if (!ftpfs.iocharset) {
- ftpfs.iocharset = "UTF8";
- }
- if (ftpfs.codepage) {
- convert_charsets(ftpfs.iocharset, ftpfs.codepage, &ftpfs.host);
- }
- easy = curl_easy_init();
- if (easy == NULL) {
- fprintf(stderr, "Error initializing libcurl\n");
- exit(1);
- }
- res = cache_parse_options(&args);
- if (res == -1)
- exit(1);
- checkpasswd("host", &ftpfs.user);
- checkpasswd("proxy", &ftpfs.proxy_user);
- if (ftpfs.transform_symlinks && !ftpfs.mountpoint) {
- fprintf(stderr, "cannot transform symlinks: no mountpoint given\n");
- exit(1);
- }
- if (!ftpfs.transform_symlinks)
- ftpfs.symlink_prefix_len = 0;
- else if (realpath(ftpfs.mountpoint, ftpfs.symlink_prefix) != NULL)
- ftpfs.symlink_prefix_len = strlen(ftpfs.symlink_prefix);
- else {
- perror("unable to normalize mount path");
- exit(1);
- }
- set_common_curl_stuff(easy);
- curl_easy_setopt_or_die(easy, CURLOPT_WRITEDATA, NULL);
- curl_easy_setopt_or_die(easy, CURLOPT_NOBODY, ftpfs.safe_nobody);
- curl_res = curl_easy_perform(easy);
- if (curl_res != 0) {
- fprintf(stderr, "Error connecting to ftp: %s\n", error_buf);
- exit(1);
- }
- curl_easy_setopt_or_die(easy, CURLOPT_NOBODY, 0);
- ftpfs.multi = curl_multi_init();
- if (ftpfs.multi == NULL) {
- fprintf(stderr, "Error initializing libcurl multi\n");
- exit(1);
- }
- ftpfs.connection = easy;
- pthread_mutex_init(&ftpfs.lock, NULL);
- // Set the filesystem name to show the current server
- tmp = g_strdup_printf("-ofsname=curlftpfs#%s", ftpfs.host);
- fuse_opt_insert_arg(&args, 1, tmp);
- g_free(tmp);
- fuse_main(args.argc, args.argv, cache_wrap(&ftpfs_oper), NULL);
- cancel_previous_multi();
- curl_multi_cleanup(ftpfs.multi);
- curl_easy_cleanup(easy);
- curl_global_cleanup();
- fuse_opt_free_args(&args);
- return res;
- }
|