ftpfs.c 52 KB

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