12 Commits

Author SHA1 Message Date
242be02050 path_utils.c: fix mismatched printf specifier 2024-12-27 00:39:59 +00:00
1f737a42fb include some missing includes 2024-12-27 00:38:52 +00:00
8a4635e811 cache.c: fix some compiler warnings 2024-12-27 00:12:06 +00:00
d0affffc5c ftpfs.c: remove unused variable "range" 2024-12-27 00:00:09 +00:00
7b7d769d59 ftpfs_utimens: update for fuse3, which uses timespec instead of utimbuf 2024-12-27 00:00:09 +00:00
2e7b724b2b ftpfs.c: include utime.h for struct utimbuf definition 2024-12-27 00:00:09 +00:00
ed6a9cee3d ftpfs.c: fix several compiler warnings, especially around printf specifiers 2024-12-27 00:00:09 +00:00
761dac3d64 ftpfs_readdir: specify all parameters to filler
else they may be filled with garbage instead
2024-10-23 10:03:00 +00:00
8f2d6449f7 op_return: also log the inner error, since we use that for matching against timeouts 2024-10-12 01:19:23 +00:00
ee70864395 propagate curl errors in ftpfs_getattr,ftpfs_readlink
this should address a hang, where certain timeouts ('server response timeout') wouldn't trigger fuse_exit
2024-10-12 00:55:08 +00:00
ff6f1a45dd new options: stderr_fd, stderr_path
the mount subsystem is screwy and lots of things will eat logs, so it's nice to be able to directly log to a path
2024-10-11 23:58:09 +00:00
a02006d8ec curl_easy_setopt: error path: log the option being set 2024-08-20 09:35:22 +00:00
9 changed files with 130 additions and 99 deletions

View File

@@ -107,6 +107,7 @@ static void cache_invalidate_dir(const char *path)
static void cache_do_rename(const char *from, const char *to, unsigned int flags) static void cache_do_rename(const char *from, const char *to, unsigned int flags)
{ {
(void) flags; //< TODO(colin): is this safe to ignore?
pthread_mutex_lock(&cache.lock); pthread_mutex_lock(&cache.lock);
cache_purge(from); cache_purge(from);
cache_purge(to); cache_purge(to);
@@ -129,11 +130,9 @@ static struct node *cache_get(const char *path)
void cache_add_attr(const char *path, const struct stat *stbuf) void cache_add_attr(const char *path, const struct stat *stbuf)
{ {
struct node *node; struct node *node;
time_t now;
pthread_mutex_lock(&cache.lock); pthread_mutex_lock(&cache.lock);
node = cache_get(path); node = cache_get(path);
now = time(NULL);
if (stbuf) { if (stbuf) {
node->stat = *stbuf; node->stat = *stbuf;
node->not_found = 0; node->not_found = 0;
@@ -150,11 +149,9 @@ void cache_add_attr(const char *path, const struct stat *stbuf)
void cache_add_dir(const char *path, char **dir) void cache_add_dir(const char *path, char **dir)
{ {
struct node *node; struct node *node;
time_t now;
pthread_mutex_lock(&cache.lock); pthread_mutex_lock(&cache.lock);
node = cache_get(path); node = cache_get(path);
now = time(NULL);
g_strfreev(node->dir); g_strfreev(node->dir);
node->dir = dir; node->dir = dir;
node->not_found = 0; node->not_found = 0;
@@ -175,11 +172,9 @@ static size_t my_strnlen(const char *s, size_t maxsize)
void cache_add_link(const char *path, const char *link, size_t size) void cache_add_link(const char *path, const char *link, size_t size)
{ {
struct node *node; struct node *node;
time_t now;
pthread_mutex_lock(&cache.lock); pthread_mutex_lock(&cache.lock);
node = cache_get(path); node = cache_get(path);
now = time(NULL);
g_free(node->link); g_free(node->link);
node->link = g_strndup(link, my_strnlen(link, size-1)); node->link = g_strndup(link, my_strnlen(link, size-1));
node->not_found = 0; node->not_found = 0;
@@ -330,7 +325,7 @@ static int cache_truncate(const char *path, off_t size, struct fuse_file_info *f
return err; return err;
} }
static int cache_utimens(const char *path, struct utimbuf *buf, struct fuse_file_info* fi) static int cache_utimens(const char *path, const struct timespec *buf, struct fuse_file_info* fi)
{ {
int err = cache.next_oper->oper.utimens(path, buf, fi); int err = cache.next_oper->oper.utimens(path, buf, fi);
if (!err) if (!err)

View File

@@ -18,8 +18,6 @@
#define CACHE_CLEAN_INTERVAL 60 #define CACHE_CLEAN_INTERVAL 60
typedef struct fuse_cache_dirhandle *fuse_cache_dirh_t; typedef struct fuse_cache_dirhandle *fuse_cache_dirh_t;
typedef int (*fuse_cache_dirfil_t) (fuse_cache_dirh_t h, const char *name,
const struct stat *stbuf);
struct fuse_cache_operations { struct fuse_cache_operations {
struct fuse_operations oper; struct fuse_operations oper;

View File

@@ -12,14 +12,19 @@ export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
PKG_CHECK_MODULES(GLIB, [glib-2.0]) PKG_CHECK_MODULES(GLIB, [glib-2.0])
PKG_CHECK_MODULES(FUSE, [fuse3 >= 3.16]) PKG_CHECK_MODULES(FUSE, [fuse3 >= 3.16])
PKG_CHECK_MODULES(BSD, [libbsd])
LIBCURL_CHECK_CONFIG([yes], [7.17.0], [], [AC_MSG_ERROR(["libcurl not found"])]) LIBCURL_CHECK_CONFIG([yes], [7.17.0], [], [AC_MSG_ERROR(["libcurl not found"])])
if test "$libcurl_protocol_FTP" != yes; then if test "$libcurl_protocol_FTP" != yes; then
AC_MSG_ERROR(["We need libcurl with support for FTP protocol."]) AC_MSG_ERROR(["We need libcurl with support for FTP protocol."])
fi fi
CFLAGS="$CFLAGS -Wall -W -Wno-sign-compare -D_REENTRANT $GLIB_CFLAGS $FUSE_CFLAGS $LIBCURL_CPPFLAGS" # _POSIX_C_SOURCE>=200809L causes glibc to provide `dprintf` in stdio.h
LIBS="$GLIB_LIBS $FUSE_LIBS $LIBCURL" # - <https://linux.die.net/man/3/dprintf>
# also used by time.h for strptime
# fuse3 requires _off_t to be 8 bytes, and advises to add _FILE_OFFSET_BITS=64 for 32bit platforms.
CFLAGS="$CFLAGS -Wall -W -Wno-sign-compare -D_REENTRANT -D_POSIX_C_SOURCE=200809L -D_FILE_OFFSET_BITS=64 $BSD_CFLAGS $GLIB_CFLAGS $FUSE_CFLAGS $LIBCURL_CPPFLAGS"
LIBS="$BSD_LIBS $GLIB_LIBS $FUSE_LIBS $LIBCURL"
have_fuse_opt_parse=no have_fuse_opt_parse=no
AC_CHECK_FUNC([fuse_opt_parse], [have_fuse_opt_parse=yes]) AC_CHECK_FUNC([fuse_opt_parse], [have_fuse_opt_parse=yes])

View File

@@ -21,6 +21,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <glib.h> #include <glib.h>
#include "cache.h"
#include "ftpfs.h" #include "ftpfs.h"
#include "charset_utils.h" #include "charset_utils.h"
#include "ftpfs-ls.h" #include "ftpfs-ls.h"
@@ -190,7 +191,7 @@ static int parse_dir_netware(const char *line,
int parse_dir(const char* list, const char* dir, int parse_dir(const char* list, const char* dir,
const char* name, struct stat* sbuf, const char* name, struct stat* sbuf,
char* linkbuf, int linklen, char* linkbuf, int linklen,
void *dbuf, fuse_cache_dirfil_t filler) { void *dbuf, fuse_fill_dir_t filler) {
char *file; char *file;
char *link; char *link;
const char *start = list; const char *start = list;
@@ -255,7 +256,7 @@ int parse_dir(const char* list, const char* dir,
if (dbuf && filler) { if (dbuf && filler) {
DEBUG(1, "filler: %s\n", file); DEBUG(1, "filler: %s\n", file);
filler(dbuf, file, &stat_buf); filler(dbuf, file, &stat_buf, 0, 0);
} else { } else {
DEBUG(1, "cache_add_attr: %s\n", full_path); DEBUG(1, "cache_add_attr: %s\n", full_path);
cache_add_attr(full_path, &stat_buf); cache_add_attr(full_path, &stat_buf);

View File

@@ -9,11 +9,11 @@
See the file COPYING. See the file COPYING.
*/ */
#include "cache.h" #include <fuse.h>
int parse_dir(const char* list, const char* dir, int parse_dir(const char* list, const char* dir,
const char* name, struct stat* sbuf, const char* name, struct stat* sbuf,
char* linkbuf, int linklen, char* linkbuf, int linklen,
void *dbuf, fuse_cache_dirfil_t filler); void *dbuf, fuse_fill_dir_t filler);
#endif /* __CURLFTPFS_FTPFS_LS_H__ */ #endif /* __CURLFTPFS_FTPFS_LS_H__ */

179
ftpfs.c
View File

@@ -8,6 +8,7 @@
#include "config.h" #include "config.h"
#include <bsd/readpassphrase.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <ctype.h> #include <ctype.h>
@@ -72,7 +73,7 @@ static int buf_resize(struct buffer *buf, size_t len)
buf->size = (buf->len + len + 63) & ~31; buf->size = (buf->len + len + 63) & ~31;
buf->p = (uint8_t *) realloc(buf->p, buf->size); buf->p = (uint8_t *) realloc(buf->p, buf->size);
if (!buf->p) { if (!buf->p) {
fprintf(stderr, "ftpfs: memory allocation failed\n"); dprintf(ftpfs.stderr_fd, "ftpfs: memory allocation failed\n");
return -1; return -1;
} }
return 0; return 0;
@@ -129,6 +130,8 @@ enum {
static struct fuse_opt ftpfs_opts[] = { static struct fuse_opt ftpfs_opts[] = {
FTPFS_OPT("ftpfs_debug=%u", debug, 0), FTPFS_OPT("ftpfs_debug=%u", debug, 0),
FTPFS_OPT("stderr_path=%s", stderr_path, 0),
FTPFS_OPT("stderr_fd=%u", stderr_fd, 2),
FTPFS_OPT("transform_symlinks", transform_symlinks, 1), FTPFS_OPT("transform_symlinks", transform_symlinks, 1),
FTPFS_OPT("disable_epsv", disable_epsv, 1), FTPFS_OPT("disable_epsv", disable_epsv, 1),
FTPFS_OPT("enable_epsv", disable_epsv, 0), FTPFS_OPT("enable_epsv", disable_epsv, 0),
@@ -209,7 +212,7 @@ static void cancel_previous_multi()
CURLMcode curlMCode = curl_multi_remove_handle(ftpfs.multi, ftpfs.connection); CURLMcode curlMCode = curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
if (curlMCode != CURLE_OK) if (curlMCode != CURLE_OK)
{ {
fprintf(stderr, "curl_multi_remove_handle problem: %d\n", curlMCode); dprintf(ftpfs.stderr_fd, "curl_multi_remove_handle problem: %d\n", curlMCode);
exit(1); exit(1);
} }
ftpfs.attached_to_multi = 0; ftpfs.attached_to_multi = 0;
@@ -222,11 +225,12 @@ static int op_return(int err, char * operation)
DEBUG(2, "%s successful\n", operation); DEBUG(2, "%s successful\n", operation);
return 0; return 0;
} }
fprintf(stderr, "ftpfs: operation %s failed because %s\n", operation, strerror(-err)); dprintf(ftpfs.stderr_fd, "ftpfs: operation %s failed because %s. inner: %s\n", operation, strerror(-err), error_buf);
if (strstr(error_buf, "timed out") != NULL if (strstr(error_buf, "timed out") != NULL
|| strstr(error_buf, "timeout") != NULL || strstr(error_buf, "timeout") != NULL
|| strstr(error_buf, "time-out") != NULL) { || strstr(error_buf, "time-out") != NULL) {
// try to match any curl error which would indiciate that the mount is offline. // try to match any curl error which would indiciate that the mount is offline.
dprintf(ftpfs.stderr_fd, "exiting due to timeout\n");
fuse_exit(fuse_get_context()->fuse); fuse_exit(fuse_get_context()->fuse);
} }
return err; return err;
@@ -262,7 +266,7 @@ static size_t read_data(void *ptr, size_t size, size_t nmemb, void *data) {
do {\ do {\
CURLcode res = curl_easy_setopt(handle, option, __VA_ARGS__);\ CURLcode res = curl_easy_setopt(handle, option, __VA_ARGS__);\
if (res != CURLE_OK) {\ if (res != CURLE_OK) {\
fprintf(stderr, "Error setting curl: %s\n", error_buf);\ dprintf(ftpfs.stderr_fd, "Error setting curl option %d: %s\n", option, error_buf);\
exit(1);\ exit(1);\
}\ }\
}while(0) }while(0)
@@ -271,7 +275,10 @@ static int ftpfs_readdir(const char* path, void *dbuf, fuse_fill_dir_t filler,
off_t offset, struct fuse_file_info *fi, off_t offset, struct fuse_file_info *fi,
enum fuse_readdir_flags flags) { enum fuse_readdir_flags flags) {
// N.B.: fuse.h tells us that we can ignore `offset` and pass 0 to the filler where it would expect an offset. // N.B.: fuse.h tells us that we can ignore `offset` and pass 0 to the filler where it would expect an offset.
(void) offset;
// fuse_cache_dirfil_t handle = (struct dir_handle*) fi->fh; //< set by us in `open` // fuse_cache_dirfil_t handle = (struct dir_handle*) fi->fh; //< set by us in `open`
(void) fi;
(void) flags; //< TODO(colin): is this safe to ignore?
int err = 0; int err = 0;
CURLcode curl_res; CURLcode curl_res;
@@ -303,6 +310,8 @@ static int ftpfs_readdir(const char* path, void *dbuf, fuse_fill_dir_t filler,
} }
static int ftpfs_getattr(const char* path, struct stat* sbuf, struct fuse_file_info* fi) { static int ftpfs_getattr(const char* path, struct stat* sbuf, struct fuse_file_info* fi) {
(void) fi; //< TODO(colin): is this safe to ignore?
int err; int err;
CURLcode curl_res; CURLcode curl_res;
char* dir_path = get_dir_path(path); char* dir_path = get_dir_path(path);
@@ -320,13 +329,15 @@ static int ftpfs_getattr(const char* path, struct stat* sbuf, struct fuse_file_i
if (curl_res != 0) { if (curl_res != 0) {
DEBUG(1, "%s\n", error_buf); DEBUG(1, "%s\n", error_buf);
} err = -EIO;
buf_null_terminate(&buf); } else {
buf_null_terminate(&buf);
char* name = strrchr(path, '/'); char* name = strrchr(path, '/');
++name; ++name;
err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1, err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
name, sbuf, NULL, 0, NULL, NULL); name, sbuf, NULL, 0, NULL, NULL);
}
free(dir_path); free(dir_path);
buf_free(&buf); buf_free(&buf);
@@ -349,12 +360,12 @@ static size_t ftpfs_read_chunk(const char* full_path, char* rbuf,
int err = 0; int err = 0;
struct ftpfs_file* fh = get_ftpfs_file(fi); struct ftpfs_file* fh = get_ftpfs_file(fi);
DEBUG(2, "ftpfs_read_chunk: %s %p %zu %lld %p %p\n", DEBUG(2, "ftpfs_read_chunk: %s %p %zu %ld %p %p\n",
full_path, rbuf, size, offset, fi, fh); full_path, rbuf, size, offset, fi, fh);
pthread_mutex_lock(&ftpfs.lock); pthread_mutex_lock(&ftpfs.lock);
DEBUG(2, "buffer size: %zu %lld\n", fh->buf.len, fh->buf.begin_offset); DEBUG(2, "buffer size: %zu %ld\n", fh->buf.len, fh->buf.begin_offset);
if ((fh->buf.len < size + offset - fh->buf.begin_offset) || if ((fh->buf.len < size + offset - fh->buf.begin_offset) ||
offset < fh->buf.begin_offset || offset < fh->buf.begin_offset ||
@@ -366,7 +377,7 @@ static size_t ftpfs_read_chunk(const char* full_path, char* rbuf,
!check_running()) { !check_running()) {
DEBUG(1, "We need to restart the connection %p\n", ftpfs.connection); 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, "current_fh=%p fh=%p\n", ftpfs.current_fh, fh);
DEBUG(2, "buf.begin_offset=%lld offset=%lld\n", fh->buf.begin_offset, offset); DEBUG(2, "buf.begin_offset=%ld offset=%ld\n", fh->buf.begin_offset, offset);
buf_clear(&fh->buf); buf_clear(&fh->buf);
fh->buf.begin_offset = offset; fh->buf.begin_offset = offset;
@@ -385,7 +396,7 @@ static size_t ftpfs_read_chunk(const char* full_path, char* rbuf,
CURLMcode curlMCode = curl_multi_add_handle(ftpfs.multi, ftpfs.connection); CURLMcode curlMCode = curl_multi_add_handle(ftpfs.multi, ftpfs.connection);
if (curlMCode != CURLE_OK) if (curlMCode != CURLE_OK)
{ {
fprintf(stderr, "curl_multi_add_handle problem: %d\n", curlMCode); dprintf(ftpfs.stderr_fd, "curl_multi_add_handle problem: %d\n", curlMCode);
exit(1); exit(1);
} }
ftpfs.attached_to_multi = 1; ftpfs.attached_to_multi = 1;
@@ -515,9 +526,9 @@ int write_thread_ctr = 0;
static void *ftpfs_write_thread(void *data) { static void *ftpfs_write_thread(void *data) {
struct ftpfs_file *fh = data; struct ftpfs_file *fh = data;
char range[15]; //char range[15];
DEBUG(2, "enter streaming write thread #%d path=%s pos=%lld\n", ++write_thread_ctr, fh->full_path, fh->pos); DEBUG(2, "enter streaming write thread #%d path=%s pos=%ld\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_URL, fh->full_path);
@@ -568,7 +579,7 @@ static int start_write_thread(struct ftpfs_file *fh)
{ {
if (fh->write_conn != NULL) if (fh->write_conn != NULL)
{ {
fprintf(stderr, "assert fh->write_conn == NULL failed!\n"); dprintf(ftpfs.stderr_fd, "assert fh->write_conn == NULL failed!\n");
exit(1); exit(1);
} }
@@ -582,14 +593,14 @@ static int start_write_thread(struct ftpfs_file *fh)
fh->write_conn = curl_easy_init(); fh->write_conn = curl_easy_init();
if (fh->write_conn == NULL) { if (fh->write_conn == NULL) {
fprintf(stderr, "Error initializing libcurl\n"); dprintf(ftpfs.stderr_fd, "Error initializing libcurl\n");
return 0; return 0;
} else { } else {
int err; int err;
set_common_curl_stuff(fh->write_conn); set_common_curl_stuff(fh->write_conn);
err = pthread_create(&fh->thread_id, NULL, ftpfs_write_thread, fh); err = pthread_create(&fh->thread_id, NULL, ftpfs_write_thread, fh);
if (err) { if (err) {
fprintf(stderr, "failed to create thread: %s\n", strerror(err)); dprintf(ftpfs.stderr_fd, "failed to create thread: %s\n", strerror(err));
/* FIXME: destroy curl_easy */ /* FIXME: destroy curl_easy */
return 0; return 0;
} }
@@ -637,23 +648,23 @@ static void free_ftpfs_file(struct ftpfs_file *fh) {
free(fh); free(fh);
} }
static int buffer_file(struct ftpfs_file *fh) { // static int buffer_file(struct ftpfs_file *fh) {
// If we want to write to the file, we have to load it all at once, // // If we want to write to the file, we have to load it all at once,
// modify it in memory and then upload it as a whole as most FTP servers // // modify it in memory and then upload it as a whole as most FTP servers
// don't support resume for uploads. // // don't support resume for uploads.
pthread_mutex_lock(&ftpfs.lock); // pthread_mutex_lock(&ftpfs.lock);
cancel_previous_multi(); // cancel_previous_multi();
curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, fh->full_path); // curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, fh->full_path);
curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &fh->buf); // curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &fh->buf);
CURLcode curl_res = curl_easy_perform(ftpfs.connection); // CURLcode curl_res = curl_easy_perform(ftpfs.connection);
pthread_mutex_unlock(&ftpfs.lock); // pthread_mutex_unlock(&ftpfs.lock);
//
if (curl_res != 0) { // if (curl_res != 0) {
return -EACCES; // return -EACCES;
} // }
//
return 0; // return 0;
} // }
static int create_empty_file(const char * path) static int create_empty_file(const char * path)
{ {
@@ -755,7 +766,7 @@ static int ftpfs_open_common(const char* path, mode_t mode,
size_t size = ftpfs_read_chunk(fh->full_path, NULL, 1, 0, fi, 0); size_t size = ftpfs_read_chunk(fh->full_path, NULL, 1, 0, fi, 0);
if (size == CURLFTPFS_BAD_READ) { if (size == CURLFTPFS_BAD_READ) {
DEBUG(1, "initial read failed size=%d\n", size); DEBUG(1, "initial read failed size=%zu\n", size);
err = -EACCES; err = -EACCES;
} }
} }
@@ -842,11 +853,11 @@ static int ftpfs_read(const char* path, char* rbuf, size_t size, off_t offset,
int ret; int ret;
struct ftpfs_file *fh = get_ftpfs_file(fi); 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); DEBUG(1, "ftpfs_read: %s size=%zu offset=%ld has_write_conn=%d pos=%ld\n", path, size, offset, fh->write_conn!=0, fh->pos);
if (fh->pos>0 || fh->write_conn!=NULL) 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"); dprintf(ftpfs.stderr_fd, "in read/write mode we cannot read from a file that has already been written to\n");
return op_return(-EIO, "ftpfs_read"); return op_return(-EIO, "ftpfs_read");
} }
@@ -882,6 +893,7 @@ static int ftpfs_mknod(const char* path, mode_t mode, dev_t rdev) {
} }
static int ftpfs_chmod(const char* path, mode_t mode, struct fuse_file_info* fi) { static int ftpfs_chmod(const char* path, mode_t mode, struct fuse_file_info* fi) {
(void) fi; //< TODO(colin): is this safe to ignore?
int err = 0; int err = 0;
// We can only process a subset of the mode - so strip // We can only process a subset of the mode - so strip
@@ -923,7 +935,7 @@ static int ftpfs_chmod(const char* path, mode_t mode, struct fuse_file_info* fi)
static int ftpfs_chown(const char* path, uid_t uid, gid_t gid, struct fuse_file_info* fi) { static int ftpfs_chown(const char* path, uid_t uid, gid_t gid, struct fuse_file_info* fi) {
int err = 0; int err = 0;
DEBUG(1, "ftpfs_chown: %d %d %u\n", (int)uid, (int)gid, fi); DEBUG(1, "ftpfs_chown: %d %d %p\n", (int)uid, (int)gid, fi);
struct curl_slist* header = NULL; struct curl_slist* header = NULL;
char* full_path = get_dir_path(path); char* full_path = get_dir_path(path);
@@ -962,7 +974,7 @@ static int ftpfs_chown(const char* path, uid_t uid, gid_t gid, struct fuse_file_
static int ftpfs_ftruncate(const char * path , off_t offset, struct fuse_file_info * fi) static int ftpfs_ftruncate(const char * path , off_t offset, struct fuse_file_info * fi)
{ {
DEBUG(1, "ftpfs_ftruncate: %s len=%lld\n", path, offset); DEBUG(1, "ftpfs_ftruncate: %s len=%ld\n", path, offset);
struct ftpfs_file *fh = get_ftpfs_file(fi); struct ftpfs_file *fh = get_ftpfs_file(fi);
if (offset == 0) if (offset == 0)
@@ -991,7 +1003,7 @@ static int ftpfs_truncate(const char* path, off_t offset, struct fuse_file_info*
if (fi) if (fi)
return ftpfs_ftruncate(path, offset, fi); return ftpfs_ftruncate(path, offset, fi);
DEBUG(1, "ftpfs_truncate: %s len=%lld\n", path, offset); DEBUG(1, "ftpfs_truncate: %s len=%ld\n", path, offset);
/* we can't use ftpfs_mknod here, because we don't know the right permissions */ /* 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"); if (offset == 0) return op_return(create_empty_file(path), "ftpfs_truncate");
@@ -1009,9 +1021,10 @@ static int ftpfs_truncate(const char* path, off_t offset, struct fuse_file_info*
return op_return(-EPERM, "ftpfs_truncate"); return op_return(-EPERM, "ftpfs_truncate");
} }
static int ftpfs_utimens(const char* path, struct utimbuf* time, struct fuse_file_info* fi) { static int ftpfs_utimens(const char* path, const struct timespec* time, struct fuse_file_info* fi) {
(void) path; (void) path;
(void) time; (void) time;
(void) fi;
return op_return(0, "ftpfs_utimens"); return op_return(0, "ftpfs_utimens");
} }
@@ -1129,7 +1142,7 @@ static int ftpfs_write(const char *path, const char *wbuf, size_t size,
(void) path; (void) path;
struct ftpfs_file *fh = get_ftpfs_file(fi); 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); DEBUG(1, "ftpfs_write: %s size=%zu offset=%ld has_write_conn=%d pos=%ld\n", path, size, offset, fh->write_conn!=0, fh->pos);
if (fh->write_fail_cause != CURLE_OK) if (fh->write_fail_cause != CURLE_OK)
{ {
@@ -1139,7 +1152,7 @@ static int ftpfs_write(const char *path, const char *wbuf, size_t size,
if (!fh->write_conn && fh->pos == 0 && offset == 0) if (!fh->write_conn && fh->pos == 0 && offset == 0)
{ {
DEBUG(1, "ftpfs_write: starting a streaming write at pos=%lld\n", fh->pos); DEBUG(1, "ftpfs_write: starting a streaming write at pos=%ld\n", fh->pos);
/* check if the file has been truncated to zero or has been newly created */ /* check if the file has been truncated to zero or has been newly created */
if (!fh->write_may_start) if (!fh->write_may_start)
@@ -1147,7 +1160,7 @@ static int ftpfs_write(const char *path, const char *wbuf, size_t size,
long long size = (long long int)test_size(path); long long size = (long long int)test_size(path);
if (size != 0) if (size != 0)
{ {
fprintf(stderr, "ftpfs_write: start writing with no previous truncate not allowed! size check rval=%lld\n", size); dprintf(ftpfs.stderr_fd, "ftpfs_write: start writing with no previous truncate not allowed! size check rval=%lld\n", size);
return op_return(-EIO, "ftpfs_write"); return op_return(-EIO, "ftpfs_write");
} }
} }
@@ -1164,7 +1177,7 @@ static int ftpfs_write(const char *path, const char *wbuf, size_t size,
if (!fh->write_conn && fh->pos >0 && offset == fh->pos) if (!fh->write_conn && fh->pos >0 && offset == fh->pos)
{ {
/* resume a streaming write */ /* resume a streaming write */
DEBUG(1, "ftpfs_write: resuming a streaming write at pos=%lld\n", fh->pos); DEBUG(1, "ftpfs_write: resuming a streaming write at pos=%ld\n", fh->pos);
int success = start_write_thread(fh); int success = start_write_thread(fh);
if (!success) if (!success)
@@ -1216,31 +1229,31 @@ static int ftpfs_flush(const char *path, struct fuse_file_info *fi) {
int err = 0; int err = 0;
struct ftpfs_file* fh = get_ftpfs_file(fi); 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); DEBUG(1, "ftpfs_flush: buf.len=%zu buf.pos=%ld write_conn=%d\n", fh->buf.len, fh->pos, fh->write_conn!=0);
if (fh->write_conn) { if (fh->write_conn) {
err = finish_write_thread(fh); err = finish_write_thread(fh);
if (err) return op_return(err, "ftpfs_flush"); if (err) return op_return(err, "ftpfs_flush");
struct stat sbuf; struct stat sbuf;
/* check if the resulting file has the correct size /* check if the resulting file has the correct size
this is important, because we use APPE for continuing this is important, because we use APPE for continuing
writing after a premature flush */ writing after a premature flush */
err = ftpfs_getattr(path, &sbuf, fi); err = ftpfs_getattr(path, &sbuf, fi);
if (err) return op_return(err, "ftpfs_flush"); if (err) return op_return(err, "ftpfs_flush");
if (sbuf.st_size != fh->pos) if (sbuf.st_size != fh->pos)
{ {
fh->write_fail_cause = -999; fh->write_fail_cause = -999;
fprintf(stderr, "ftpfs_flush: check filesize problem: size=%lld expected=%lld\n", sbuf.st_size, fh->pos); dprintf(ftpfs.stderr_fd, "ftpfs_flush: check filesize problem: size=%ld expected=%ld\n", sbuf.st_size, fh->pos);
return op_return(-EIO, "ftpfs_flush"); return op_return(-EIO, "ftpfs_flush");
} }
return 0; return 0;
} }
if (!fh->dirty) return 0; if (!fh->dirty) return 0;
return op_return(-EIO, "ftpfs_flush"); return op_return(-EIO, "ftpfs_flush");
@@ -1337,13 +1350,15 @@ static int ftpfs_readlink(const char *path, char *linkbuf, size_t size) {
if (curl_res != 0) { if (curl_res != 0) {
DEBUG(1, "%s\n", error_buf); DEBUG(1, "%s\n", error_buf);
} err = -EIO;
buf_null_terminate(&buf); } else {
buf_null_terminate(&buf);
char* name = strrchr(path, '/'); char* name = strrchr(path, '/');
++name; ++name;
err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1, err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
name, NULL, linkbuf, size, NULL, NULL); name, NULL, linkbuf, size, NULL, NULL);
}
free(dir_path); free(dir_path);
buf_free(&buf); buf_free(&buf);
@@ -1436,7 +1451,7 @@ static int ftpfs_opt_proc(void* data, const char* arg, int key,
ftpfs.verbose = 1; ftpfs.verbose = 1;
return 0; return 0;
case KEY_VERSION: case KEY_VERSION:
fprintf(stderr, "curlftpfs %s libcurl/%s fuse/%u.%u\n", dprintf(ftpfs.stderr_fd, "curlftpfs %s libcurl/%s fuse/%u.%u\n",
VERSION, VERSION,
ftpfs.curl_version->version, ftpfs.curl_version->version,
FUSE_MAJOR_VERSION, FUSE_MAJOR_VERSION,
@@ -1459,6 +1474,8 @@ static void usage(const char* progname) {
"\n" "\n"
"FTP options:\n" "FTP options:\n"
" ftpfs_debug print some debugging information\n" " ftpfs_debug print some debugging information\n"
" stderr_path=STR log stderr to path\n"
" stderr_fd=N log stderr to file descriptor\n"
" transform_symlinks prepend mountpoint to absolute symlink targets\n" " transform_symlinks prepend mountpoint to absolute symlink targets\n"
" disable_epsv use PASV, without trying EPSV first (default)\n" " disable_epsv use PASV, without trying EPSV first (default)\n"
" enable_epsv try EPSV before reverting to PASV\n" " enable_epsv try EPSV before reverting to PASV\n"
@@ -1589,7 +1606,7 @@ static void set_common_curl_stuff(CURL* easy) {
* with version 7.15.4 */ * with version 7.15.4 */
if (ftpfs.use_ssl > CURLFTPSSL_TRY && if (ftpfs.use_ssl > CURLFTPSSL_TRY &&
ftpfs.curl_version->version_num <= CURLFTPFS_BAD_SSL) { ftpfs.curl_version->version_num <= CURLFTPFS_BAD_SSL) {
fprintf(stderr, dprintf(ftpfs.stderr_fd,
"WARNING: you are using libcurl %s.\n" "WARNING: you are using libcurl %s.\n"
"This version of libcurl does not respect the mandatory SSL flag.\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" "It will try to send the user and password even if the server doesn't support\n"
@@ -1599,10 +1616,10 @@ static void set_common_curl_stuff(CURL* easy) {
int i; int i;
const int time_to_wait = 10; const int time_to_wait = 10;
for (i = 0; i < time_to_wait; i++) { for (i = 0; i < time_to_wait; i++) {
fprintf(stderr, "%d.. ", time_to_wait - i); dprintf(ftpfs.stderr_fd, "%d.. ", time_to_wait - i);
sleep(1); sleep(1);
} }
fprintf(stderr, "\n"); dprintf(ftpfs.stderr_fd, "\n");
} }
curl_easy_setopt_or_die(easy, CURLOPT_FTP_SSL, ftpfs.use_ssl); curl_easy_setopt_or_die(easy, CURLOPT_FTP_SSL, ftpfs.use_ssl);
@@ -1686,6 +1703,7 @@ static void checkpasswd(const char *kind, /* for what purpose */
if(!ptr) { if(!ptr) {
/* no password present, prompt for one */ /* no password present, prompt for one */
char *passwd; char *passwd;
char passwd_buf[256];
char prompt[256]; char prompt[256];
size_t passwdlen; size_t passwdlen;
size_t userlen = strlen(*userpwd); size_t userlen = strlen(*userpwd);
@@ -1697,7 +1715,7 @@ static void checkpasswd(const char *kind, /* for what purpose */
kind, *userpwd); kind, *userpwd);
/* get password */ /* get password */
passwd = getpass(prompt); passwd = readpassphrase(prompt, passwd_buf, 256, 0 /* flags */);
passwdlen = strlen(passwd); passwdlen = strlen(passwd);
/* extend the allocated memory area to fit the password too */ /* extend the allocated memory area to fit the password too */
@@ -1738,9 +1756,20 @@ int main(int argc, char** argv) {
if (fuse_opt_parse(&args, &ftpfs, ftpfs_opts, ftpfs_opt_proc) == -1) if (fuse_opt_parse(&args, &ftpfs, ftpfs_opts, ftpfs_opt_proc) == -1)
exit(1); exit(1);
if (ftpfs.stderr_path)
ftpfs.stderr_fd = open(ftpfs.stderr_path, O_RDWR|O_CREAT|O_APPEND, 0644);
if (ftpfs.stderr_fd == -1) {
fprintf(stderr, "failed to open stderr\n");
exit(1);
}
if (ftpfs.debug)
dprintf(ftpfs.stderr_fd, "logging configured\n");
if (!ftpfs.host) { if (!ftpfs.host) {
fprintf(stderr, "missing host\n"); dprintf(ftpfs.stderr_fd, "missing host\n");
fprintf(stderr, "see `%s -h' for usage\n", argv[0]); dprintf(ftpfs.stderr_fd, "see `%s -h' for usage\n", argv[0]);
exit(1); exit(1);
} }
@@ -1754,7 +1783,7 @@ int main(int argc, char** argv) {
easy = curl_easy_init(); easy = curl_easy_init();
if (easy == NULL) { if (easy == NULL) {
fprintf(stderr, "Error initializing libcurl\n"); dprintf(ftpfs.stderr_fd, "Error initializing libcurl\n");
exit(1); exit(1);
} }
@@ -1766,7 +1795,7 @@ int main(int argc, char** argv) {
checkpasswd("proxy", &ftpfs.proxy_user); checkpasswd("proxy", &ftpfs.proxy_user);
if (ftpfs.transform_symlinks && !ftpfs.mountpoint) { if (ftpfs.transform_symlinks && !ftpfs.mountpoint) {
fprintf(stderr, "cannot transform symlinks: no mountpoint given\n"); dprintf(ftpfs.stderr_fd, "cannot transform symlinks: no mountpoint given\n");
exit(1); exit(1);
} }
if (!ftpfs.transform_symlinks) if (!ftpfs.transform_symlinks)
@@ -1783,14 +1812,14 @@ int main(int argc, char** argv) {
curl_easy_setopt_or_die(easy, CURLOPT_NOBODY, ftpfs.safe_nobody); curl_easy_setopt_or_die(easy, CURLOPT_NOBODY, ftpfs.safe_nobody);
curl_res = curl_easy_perform(easy); curl_res = curl_easy_perform(easy);
if (curl_res != 0) { if (curl_res != 0) {
fprintf(stderr, "Error connecting to ftp: %s\n", error_buf); dprintf(ftpfs.stderr_fd, "Error connecting to ftp: %s\n", error_buf);
exit(1); exit(1);
} }
curl_easy_setopt_or_die(easy, CURLOPT_NOBODY, 0); curl_easy_setopt_or_die(easy, CURLOPT_NOBODY, 0);
ftpfs.multi = curl_multi_init(); ftpfs.multi = curl_multi_init();
if (ftpfs.multi == NULL) { if (ftpfs.multi == NULL) {
fprintf(stderr, "Error initializing libcurl multi\n"); dprintf(ftpfs.stderr_fd, "Error initializing libcurl multi\n");
exit(1); exit(1);
} }

11
ftpfs.h
View File

@@ -13,6 +13,7 @@
#include <curl/easy.h> #include <curl/easy.h>
#include <pthread.h> #include <pthread.h>
#include <pthread.h> #include <pthread.h>
#include <stdio.h>
struct ftpfs { struct ftpfs {
char* host; char* host;
@@ -25,6 +26,8 @@ struct ftpfs {
unsigned blksize; unsigned blksize;
int verbose; int verbose;
int debug; int debug;
char *stderr_path;
int stderr_fd;
int transform_symlinks; int transform_symlinks;
int disable_epsv; int disable_epsv;
int skip_pasv_ip; int skip_pasv_ip;
@@ -75,10 +78,10 @@ extern struct ftpfs ftpfs;
#define DEBUG(level, args...) \ #define DEBUG(level, args...) \
do { if (level <= ftpfs.debug) {\ do { if (level <= ftpfs.debug) {\
int i = 0; \ int i = 0; \
while (++i < level) fprintf(stderr, " "); \ while (++i < level) dprintf(ftpfs.stderr_fd, " "); \
fprintf(stderr, "%ld ", time(NULL));\ dprintf(ftpfs.stderr_fd, "%ld ", time(NULL));\
fprintf(stderr, __FILE__ ":%d ", __LINE__);\ dprintf(ftpfs.stderr_fd, __FILE__ ":%d ", __LINE__);\
fprintf(stderr, args);\ dprintf(ftpfs.stderr_fd, args);\
}\ }\
} while(0) } while(0)

View File

@@ -5038,7 +5038,7 @@ EOF
/* -DDEBUG is fairly common in CFLAGS. */ /* -DDEBUG is fairly common in CFLAGS. */
#undef DEBUG #undef DEBUG
#if defined DEBUGWRAPPER #if defined DEBUGWRAPPER
# define DEBUG(format, ...) fprintf(stderr, format, __VA_ARGS__) # define DEBUG(format, ...) dprintf(ftpfs.stderr_fd, format, __VA_ARGS__)
#else #else
# define DEBUG(format, ...) # define DEBUG(format, ...)
#endif #endif
@@ -5291,9 +5291,9 @@ static void
lt_error_core (int exit_status, const char * mode, lt_error_core (int exit_status, const char * mode,
const char * message, va_list ap) const char * message, va_list ap)
{ {
fprintf (stderr, "%s: %s: ", program_name, mode); dprintf (ftpfs.stderr_fd, "%s: %s: ", program_name, mode);
vfprintf (stderr, message, ap); vdprintf (ftpfs.stderr_fd, message, ap);
fprintf (stderr, ".\n"); dprintf (ftpfs.stderr_fd, ".\n");
if (exit_status >= 0) if (exit_status >= 0)
exit (exit_status); exit (exit_status);

View File

@@ -84,7 +84,7 @@ char* get_dir_path(const char* path) {
ret = g_strdup_printf("%s%.*s%s", ret = g_strdup_printf("%s%.*s%s",
ftpfs.host, ftpfs.host,
lastdir - path, (int)(lastdir - path),
path, path,
lastdir - path ? "/" : ""); lastdir - path ? "/" : "");