Compare commits
15 Commits
maintenanc
...
master
Author | SHA1 | Date | |
---|---|---|---|
242be02050 | |||
1f737a42fb | |||
8a4635e811 | |||
d0affffc5c | |||
7b7d769d59 | |||
2e7b724b2b | |||
ed6a9cee3d | |||
761dac3d64 | |||
8f2d6449f7 | |||
ee70864395 | |||
ff6f1a45dd | |||
a02006d8ec | |||
3d81b832e5 | |||
5c68307010 | |||
a431e8615e |
@@ -5,7 +5,7 @@ DIST_SUBDIRS = $(SUBDIRS) tests
|
||||
|
||||
LIBICONV = @LIBICONV@
|
||||
|
||||
AM_CPPFLAGS = -DFUSE_USE_VERSION=26
|
||||
AM_CPPFLAGS = -DFUSE_USE_VERSION=316
|
||||
AM_LDFLAGS = $(LIBICONV)
|
||||
|
||||
bin_PROGRAMS = curlftpfs
|
||||
|
@@ -215,7 +215,7 @@ sysconfdir = @sysconfdir@
|
||||
target_alias = @target_alias@
|
||||
SUBDIRS = compat doc
|
||||
DIST_SUBDIRS = $(SUBDIRS) tests
|
||||
AM_CPPFLAGS = -DFUSE_USE_VERSION=26
|
||||
AM_CPPFLAGS = -DFUSE_USE_VERSION=316
|
||||
AM_LDFLAGS = $(LIBICONV)
|
||||
curlftpfs_SOURCES = ftpfs.c
|
||||
@FUSE_OPT_COMPAT_FALSE@curlftpfs_LDADD = libcurlftpfs.a
|
||||
|
150
cache.c
150
cache.c
@@ -38,13 +38,6 @@ struct node {
|
||||
time_t valid;
|
||||
};
|
||||
|
||||
struct fuse_cache_dirhandle {
|
||||
const char *path;
|
||||
fuse_dirh_t h;
|
||||
fuse_dirfil_t filler;
|
||||
GPtrArray *dir;
|
||||
};
|
||||
|
||||
static void free_node(gpointer node_)
|
||||
{
|
||||
struct node *node = (struct node *) node_;
|
||||
@@ -112,8 +105,9 @@ static void cache_invalidate_dir(const char *path)
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
}
|
||||
|
||||
static void cache_do_rename(const char *from, const char *to)
|
||||
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);
|
||||
cache_purge(from);
|
||||
cache_purge(to);
|
||||
@@ -136,11 +130,9 @@ static struct node *cache_get(const char *path)
|
||||
void cache_add_attr(const char *path, const struct stat *stbuf)
|
||||
{
|
||||
struct node *node;
|
||||
time_t now;
|
||||
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
node = cache_get(path);
|
||||
now = time(NULL);
|
||||
if (stbuf) {
|
||||
node->stat = *stbuf;
|
||||
node->not_found = 0;
|
||||
@@ -157,11 +149,9 @@ void cache_add_attr(const char *path, const struct stat *stbuf)
|
||||
void cache_add_dir(const char *path, char **dir)
|
||||
{
|
||||
struct node *node;
|
||||
time_t now;
|
||||
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
node = cache_get(path);
|
||||
now = time(NULL);
|
||||
g_strfreev(node->dir);
|
||||
node->dir = dir;
|
||||
node->not_found = 0;
|
||||
@@ -182,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)
|
||||
{
|
||||
struct node *node;
|
||||
time_t now;
|
||||
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
node = cache_get(path);
|
||||
now = time(NULL);
|
||||
g_free(node->link);
|
||||
node->link = g_strndup(link, my_strnlen(link, size-1));
|
||||
node->not_found = 0;
|
||||
@@ -218,11 +206,11 @@ static int cache_get_attr(const char *path, struct stat *stbuf)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_getattr(const char *path, struct stat *stbuf)
|
||||
static int cache_getattr(const char *path, struct stat *stbuf, struct fuse_file_info *fi)
|
||||
{
|
||||
int err = cache_get_attr(path, stbuf);
|
||||
if (err == -EAGAIN) {
|
||||
err = cache.next_oper->oper.getattr(path, stbuf);
|
||||
err = cache.next_oper->oper.getattr(path, stbuf, fi);
|
||||
if (!err)
|
||||
cache_add_attr(path, stbuf);
|
||||
else if (err == -ENOENT)
|
||||
@@ -255,71 +243,6 @@ static int cache_readlink(const char *path, char *buf, size_t size)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_dirfill(fuse_cache_dirh_t ch, const char *name,
|
||||
const struct stat *stbuf)
|
||||
{
|
||||
int err = ch->filler(ch->h, name, 0, 0);
|
||||
if (!err) {
|
||||
char *fullpath;
|
||||
g_ptr_array_add(ch->dir, g_strdup(name));
|
||||
fullpath = g_strdup_printf("%s/%s", !ch->path[1] ? "" : ch->path, name);
|
||||
cache_add_attr(fullpath, stbuf);
|
||||
g_free(fullpath);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
|
||||
{
|
||||
struct fuse_cache_dirhandle ch;
|
||||
int err;
|
||||
char **dir;
|
||||
struct node *node;
|
||||
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
node = cache_lookup(path);
|
||||
if (node != NULL && node->dir != NULL) {
|
||||
time_t now = time(NULL);
|
||||
if (node->dir_valid - now >= 0) {
|
||||
for(dir = node->dir; *dir != NULL; dir++)
|
||||
filler(h, *dir, 0, 0);
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
|
||||
ch.path = path;
|
||||
ch.h = h;
|
||||
ch.filler = filler;
|
||||
ch.dir = g_ptr_array_new();
|
||||
err = cache.next_oper->cache_getdir(path, &ch, cache_dirfill);
|
||||
g_ptr_array_add(ch.dir, NULL);
|
||||
dir = (char **) ch.dir->pdata;
|
||||
if (!err)
|
||||
cache_add_dir(path, dir);
|
||||
else
|
||||
g_strfreev(dir);
|
||||
g_ptr_array_free(ch.dir, FALSE);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_unity_dirfill(fuse_cache_dirh_t ch, const char *name,
|
||||
const struct stat *stbuf)
|
||||
{
|
||||
(void) stbuf;
|
||||
return ch->filler(ch->h, name, 0, 0);
|
||||
}
|
||||
|
||||
static int cache_unity_getdir(const char *path, fuse_dirh_t h,
|
||||
fuse_dirfil_t filler)
|
||||
{
|
||||
struct fuse_cache_dirhandle ch;
|
||||
ch.h = h;
|
||||
ch.filler = filler;
|
||||
return cache.next_oper->cache_getdir(path, &ch, cache_unity_dirfill);
|
||||
}
|
||||
|
||||
static int cache_mknod(const char *path, mode_t mode, dev_t rdev)
|
||||
{
|
||||
int err = cache.next_oper->oper.mknod(path, mode, rdev);
|
||||
@@ -360,11 +283,11 @@ static int cache_symlink(const char *from, const char *to)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_rename(const char *from, const char *to)
|
||||
static int cache_rename(const char *from, const char *to, unsigned int flags)
|
||||
{
|
||||
int err = cache.next_oper->oper.rename(from, to);
|
||||
int err = cache.next_oper->oper.rename(from, to, flags);
|
||||
if (!err)
|
||||
cache_do_rename(from, to);
|
||||
cache_do_rename(from, to, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -378,33 +301,33 @@ static int cache_link(const char *from, const char *to)
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_chmod(const char *path, mode_t mode)
|
||||
static int cache_chmod(const char *path, mode_t mode, struct fuse_file_info* fi)
|
||||
{
|
||||
int err = cache.next_oper->oper.chmod(path, mode);
|
||||
int err = cache.next_oper->oper.chmod(path, mode, fi);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_chown(const char *path, uid_t uid, gid_t gid)
|
||||
static int cache_chown(const char *path, uid_t uid, gid_t gid, struct fuse_file_info* fi)
|
||||
{
|
||||
int err = cache.next_oper->oper.chown(path, uid, gid);
|
||||
int err = cache.next_oper->oper.chown(path, uid, gid, fi);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_truncate(const char *path, off_t size)
|
||||
static int cache_truncate(const char *path, off_t size, struct fuse_file_info *fi)
|
||||
{
|
||||
int err = cache.next_oper->oper.truncate(path, size);
|
||||
int err = cache.next_oper->oper.truncate(path, size, fi);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_utime(const char *path, struct utimbuf *buf)
|
||||
static int cache_utimens(const char *path, const struct timespec *buf, struct fuse_file_info* fi)
|
||||
{
|
||||
int err = cache.next_oper->oper.utime(path, buf);
|
||||
int err = cache.next_oper->oper.utimens(path, buf, fi);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
@@ -419,7 +342,6 @@ static int cache_write(const char *path, const char *buf, size_t size,
|
||||
return res;
|
||||
}
|
||||
|
||||
#if FUSE_VERSION >= 25
|
||||
static int cache_create(const char *path, mode_t mode,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
@@ -429,39 +351,12 @@ static int cache_create(const char *path, mode_t mode,
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_ftruncate(const char *path, off_t size,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
int err = cache.next_oper->oper.ftruncate(path, size, fi);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_fgetattr(const char *path, struct stat *stbuf,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
int err = cache_get_attr(path, stbuf);
|
||||
if (err == -EAGAIN) {
|
||||
err = cache.next_oper->oper.fgetattr(path, stbuf, fi);
|
||||
if (!err)
|
||||
cache_add_attr(path, stbuf);
|
||||
else if (err == -ENOENT)
|
||||
cache_add_attr(path, NULL);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void cache_unity_fill(struct fuse_cache_operations *oper,
|
||||
struct fuse_operations *cache_oper)
|
||||
{
|
||||
#if FUSE_VERSION >= 23
|
||||
cache_oper->init = oper->oper.init;
|
||||
#endif
|
||||
cache_oper->getattr = oper->oper.getattr;
|
||||
cache_oper->readlink = oper->oper.readlink;
|
||||
cache_oper->getdir = cache_unity_getdir;
|
||||
cache_oper->mknod = oper->oper.mknod;
|
||||
cache_oper->mkdir = oper->oper.mkdir;
|
||||
cache_oper->symlink = oper->oper.symlink;
|
||||
@@ -472,7 +367,7 @@ static void cache_unity_fill(struct fuse_cache_operations *oper,
|
||||
cache_oper->chmod = oper->oper.chmod;
|
||||
cache_oper->chown = oper->oper.chown;
|
||||
cache_oper->truncate = oper->oper.truncate;
|
||||
cache_oper->utime = oper->oper.utime;
|
||||
cache_oper->utimens = oper->oper.utimens;
|
||||
cache_oper->open = oper->oper.open;
|
||||
cache_oper->read = oper->oper.read;
|
||||
cache_oper->write = oper->oper.write;
|
||||
@@ -484,11 +379,8 @@ static void cache_unity_fill(struct fuse_cache_operations *oper,
|
||||
cache_oper->getxattr = oper->oper.getxattr;
|
||||
cache_oper->listxattr = oper->oper.listxattr;
|
||||
cache_oper->removexattr = oper->oper.removexattr;
|
||||
#if FUSE_VERSION >= 25
|
||||
cache_oper->create = oper->oper.create;
|
||||
cache_oper->ftruncate = oper->oper.ftruncate;
|
||||
cache_oper->fgetattr = oper->oper.fgetattr;
|
||||
#endif
|
||||
cache_oper->readdir = oper->oper.readdir;
|
||||
}
|
||||
|
||||
struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
|
||||
@@ -500,7 +392,6 @@ struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
|
||||
if (cache.on) {
|
||||
cache_oper.getattr = oper->oper.getattr ? cache_getattr : NULL;
|
||||
cache_oper.readlink = oper->oper.readlink ? cache_readlink : NULL;
|
||||
cache_oper.getdir = oper->cache_getdir ? cache_getdir : NULL;
|
||||
cache_oper.mknod = oper->oper.mknod ? cache_mknod : NULL;
|
||||
cache_oper.mkdir = oper->oper.mkdir ? cache_mkdir : NULL;
|
||||
cache_oper.symlink = oper->oper.symlink ? cache_symlink : NULL;
|
||||
@@ -511,13 +402,10 @@ struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
|
||||
cache_oper.chmod = oper->oper.chmod ? cache_chmod : NULL;
|
||||
cache_oper.chown = oper->oper.chown ? cache_chown : NULL;
|
||||
cache_oper.truncate = oper->oper.truncate ? cache_truncate : NULL;
|
||||
cache_oper.utime = oper->oper.utime ? cache_utime : NULL;
|
||||
cache_oper.utimens = oper->oper.utimens ? cache_utimens : NULL;
|
||||
cache_oper.write = oper->oper.write ? cache_write : NULL;
|
||||
#if FUSE_VERSION >= 25
|
||||
cache_oper.create = oper->oper.create ? cache_create : NULL;
|
||||
cache_oper.ftruncate = oper->oper.ftruncate ? cache_ftruncate : NULL;
|
||||
cache_oper.fgetattr = oper->oper.fgetattr ? cache_fgetattr : NULL;
|
||||
#endif
|
||||
// TODO: cache `readdir`
|
||||
pthread_mutex_init(&cache.lock, NULL);
|
||||
cache.table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
|
||||
free_node);
|
||||
|
7
cache.h
7
cache.h
@@ -12,22 +12,15 @@
|
||||
#include <fuse.h>
|
||||
#include <fuse_opt.h>
|
||||
|
||||
#ifndef FUSE_VERSION
|
||||
#define FUSE_VERSION (FUSE_MAJOR_VERSION * 10 + FUSE_MINOR_VERSION)
|
||||
#endif
|
||||
|
||||
#define DEFAULT_CACHE_TIMEOUT 10
|
||||
#define MAX_CACHE_SIZE 10000
|
||||
#define MIN_CACHE_CLEAN_INTERVAL 5
|
||||
#define CACHE_CLEAN_INTERVAL 60
|
||||
|
||||
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_operations oper;
|
||||
int (*cache_getdir) (const char *, fuse_cache_dirh_t, fuse_cache_dirfil_t);
|
||||
};
|
||||
|
||||
struct fuse_operations *cache_init(struct fuse_cache_operations *oper);
|
||||
|
11
configure.ac
11
configure.ac
@@ -11,15 +11,20 @@ AC_PROG_LIBTOOL
|
||||
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
|
||||
|
||||
PKG_CHECK_MODULES(GLIB, [glib-2.0])
|
||||
PKG_CHECK_MODULES(FUSE, [fuse >= 2.2])
|
||||
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"])])
|
||||
if test "$libcurl_protocol_FTP" != yes; then
|
||||
AC_MSG_ERROR(["We need libcurl with support for FTP protocol."])
|
||||
fi
|
||||
|
||||
CFLAGS="$CFLAGS -Wall -W -Wno-sign-compare -D_REENTRANT $GLIB_CFLAGS $FUSE_CFLAGS $LIBCURL_CPPFLAGS"
|
||||
LIBS="$GLIB_LIBS $FUSE_LIBS $LIBCURL"
|
||||
# _POSIX_C_SOURCE>=200809L causes glibc to provide `dprintf` in stdio.h
|
||||
# - <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
|
||||
AC_CHECK_FUNC([fuse_opt_parse], [have_fuse_opt_parse=yes])
|
||||
|
@@ -21,6 +21,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
|
||||
#include "cache.h"
|
||||
#include "ftpfs.h"
|
||||
#include "charset_utils.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,
|
||||
const char* name, struct stat* sbuf,
|
||||
char* linkbuf, int linklen,
|
||||
fuse_cache_dirh_t h, fuse_cache_dirfil_t filler) {
|
||||
void *dbuf, fuse_fill_dir_t filler) {
|
||||
char *file;
|
||||
char *link;
|
||||
const char *start = list;
|
||||
@@ -253,9 +254,9 @@ int parse_dir(const char* list, const char* dir,
|
||||
free(reallink);
|
||||
}
|
||||
|
||||
if (h && filler) {
|
||||
if (dbuf && filler) {
|
||||
DEBUG(1, "filler: %s\n", file);
|
||||
filler(h, file, &stat_buf);
|
||||
filler(dbuf, file, &stat_buf, 0, 0);
|
||||
} else {
|
||||
DEBUG(1, "cache_add_attr: %s\n", full_path);
|
||||
cache_add_attr(full_path, &stat_buf);
|
||||
|
@@ -9,11 +9,11 @@
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
#include "cache.h"
|
||||
#include <fuse.h>
|
||||
|
||||
int parse_dir(const char* list, const char* dir,
|
||||
const char* name, struct stat* sbuf,
|
||||
char* linkbuf, int linklen,
|
||||
fuse_cache_dirh_t h, fuse_cache_dirfil_t filler);
|
||||
void *dbuf, fuse_fill_dir_t filler);
|
||||
|
||||
#endif /* __CURLFTPFS_FTPFS_LS_H__ */
|
||||
|
323
ftpfs.c
323
ftpfs.c
@@ -8,6 +8,7 @@
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <bsd/readpassphrase.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.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->p = (uint8_t *) realloc(buf->p, buf->size);
|
||||
if (!buf->p) {
|
||||
fprintf(stderr, "ftpfs: memory allocation failed\n");
|
||||
dprintf(ftpfs.stderr_fd, "ftpfs: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
@@ -129,6 +130,8 @@ enum {
|
||||
|
||||
static struct fuse_opt ftpfs_opts[] = {
|
||||
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("disable_epsv", disable_epsv, 1),
|
||||
FTPFS_OPT("enable_epsv", disable_epsv, 0),
|
||||
@@ -174,6 +177,7 @@ static struct fuse_opt ftpfs_opts[] = {
|
||||
FTPFS_OPT("codepage=%s", codepage, 0),
|
||||
FTPFS_OPT("iocharset=%s", iocharset, 0),
|
||||
FTPFS_OPT("nomulticonn", multiconn, 0),
|
||||
FTPFS_OPT("exit_after_connect", exit_after_connect, 1),
|
||||
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
@@ -208,7 +212,7 @@ static void cancel_previous_multi()
|
||||
CURLMcode curlMCode = curl_multi_remove_handle(ftpfs.multi, ftpfs.connection);
|
||||
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);
|
||||
}
|
||||
ftpfs.attached_to_multi = 0;
|
||||
@@ -221,11 +225,12 @@ static int op_return(int err, char * operation)
|
||||
DEBUG(2, "%s successful\n", operation);
|
||||
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
|
||||
|| strstr(error_buf, "timeout") != NULL
|
||||
|| strstr(error_buf, "time-out") != NULL) {
|
||||
// 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);
|
||||
}
|
||||
return err;
|
||||
@@ -261,18 +266,25 @@ static size_t read_data(void *ptr, size_t size, size_t nmemb, void *data) {
|
||||
do {\
|
||||
CURLcode res = curl_easy_setopt(handle, option, __VA_ARGS__);\
|
||||
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);\
|
||||
}\
|
||||
}while(0)
|
||||
|
||||
static int ftpfs_getdir(const char* path, fuse_cache_dirh_t h,
|
||||
fuse_cache_dirfil_t filler) {
|
||||
static int ftpfs_readdir(const char* path, void *dbuf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi,
|
||||
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.
|
||||
(void) offset;
|
||||
// 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;
|
||||
CURLcode curl_res;
|
||||
char* dir_path = get_fulldir_path(path);
|
||||
|
||||
DEBUG(1, "ftpfs_getdir: %s\n", dir_path);
|
||||
DEBUG(1, "ftpfs_readdir: %s\n", dir_path);
|
||||
struct buffer buf;
|
||||
buf_init(&buf);
|
||||
|
||||
@@ -289,15 +301,17 @@ static int ftpfs_getdir(const char* path, fuse_cache_dirh_t h,
|
||||
} else {
|
||||
buf_null_terminate(&buf);
|
||||
parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
|
||||
NULL, NULL, NULL, 0, h, filler);
|
||||
NULL, NULL, NULL, 0, dbuf, filler);
|
||||
}
|
||||
|
||||
free(dir_path);
|
||||
buf_free(&buf);
|
||||
return op_return(err, "ftpfs_getdir");
|
||||
return op_return(err, "ftpfs_readdir");
|
||||
}
|
||||
|
||||
static int ftpfs_getattr(const char* path, struct stat* sbuf) {
|
||||
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;
|
||||
CURLcode curl_res;
|
||||
char* dir_path = get_dir_path(path);
|
||||
@@ -315,13 +329,15 @@ static int ftpfs_getattr(const char* path, struct stat* sbuf) {
|
||||
|
||||
if (curl_res != 0) {
|
||||
DEBUG(1, "%s\n", error_buf);
|
||||
}
|
||||
buf_null_terminate(&buf);
|
||||
err = -EIO;
|
||||
} else {
|
||||
buf_null_terminate(&buf);
|
||||
|
||||
char* name = strrchr(path, '/');
|
||||
++name;
|
||||
err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
|
||||
name, sbuf, NULL, 0, NULL, NULL);
|
||||
char* name = strrchr(path, '/');
|
||||
++name;
|
||||
err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
|
||||
name, sbuf, NULL, 0, NULL, NULL);
|
||||
}
|
||||
|
||||
free(dir_path);
|
||||
buf_free(&buf);
|
||||
@@ -344,12 +360,12 @@ static size_t ftpfs_read_chunk(const char* full_path, char* rbuf,
|
||||
int err = 0;
|
||||
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);
|
||||
|
||||
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) ||
|
||||
offset < fh->buf.begin_offset ||
|
||||
@@ -361,7 +377,7 @@ static size_t ftpfs_read_chunk(const char* full_path, char* rbuf,
|
||||
!check_running()) {
|
||||
DEBUG(1, "We need to restart the connection %p\n", ftpfs.connection);
|
||||
DEBUG(2, "current_fh=%p fh=%p\n", ftpfs.current_fh, fh);
|
||||
DEBUG(2, "buf.begin_offset=%lld offset=%lld\n", fh->buf.begin_offset, offset);
|
||||
DEBUG(2, "buf.begin_offset=%ld offset=%ld\n", fh->buf.begin_offset, offset);
|
||||
|
||||
buf_clear(&fh->buf);
|
||||
fh->buf.begin_offset = offset;
|
||||
@@ -380,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);
|
||||
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);
|
||||
}
|
||||
ftpfs.attached_to_multi = 1;
|
||||
@@ -510,9 +526,9 @@ int write_thread_ctr = 0;
|
||||
|
||||
static void *ftpfs_write_thread(void *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);
|
||||
@@ -563,7 +579,7 @@ static int start_write_thread(struct ftpfs_file *fh)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -577,14 +593,14 @@ static int start_write_thread(struct ftpfs_file *fh)
|
||||
|
||||
fh->write_conn = curl_easy_init();
|
||||
if (fh->write_conn == NULL) {
|
||||
fprintf(stderr, "Error initializing libcurl\n");
|
||||
dprintf(ftpfs.stderr_fd, "Error initializing libcurl\n");
|
||||
return 0;
|
||||
} else {
|
||||
int err;
|
||||
set_common_curl_stuff(fh->write_conn);
|
||||
err = pthread_create(&fh->thread_id, NULL, ftpfs_write_thread, fh);
|
||||
if (err) {
|
||||
fprintf(stderr, "failed to create thread: %s\n", strerror(err));
|
||||
dprintf(ftpfs.stderr_fd, "failed to create thread: %s\n", strerror(err));
|
||||
/* FIXME: destroy curl_easy */
|
||||
return 0;
|
||||
}
|
||||
@@ -632,23 +648,23 @@ static void free_ftpfs_file(struct ftpfs_file *fh) {
|
||||
free(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,
|
||||
// modify it in memory and then upload it as a whole as most FTP servers
|
||||
// don't support resume for uploads.
|
||||
pthread_mutex_lock(&ftpfs.lock);
|
||||
cancel_previous_multi();
|
||||
curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, fh->full_path);
|
||||
curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &fh->buf);
|
||||
CURLcode curl_res = curl_easy_perform(ftpfs.connection);
|
||||
pthread_mutex_unlock(&ftpfs.lock);
|
||||
|
||||
if (curl_res != 0) {
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
// static int buffer_file(struct ftpfs_file *fh) {
|
||||
// // 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
|
||||
// // don't support resume for uploads.
|
||||
// pthread_mutex_lock(&ftpfs.lock);
|
||||
// cancel_previous_multi();
|
||||
// curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_URL, fh->full_path);
|
||||
// curl_easy_setopt_or_die(ftpfs.connection, CURLOPT_WRITEDATA, &fh->buf);
|
||||
// CURLcode curl_res = curl_easy_perform(ftpfs.connection);
|
||||
// pthread_mutex_unlock(&ftpfs.lock);
|
||||
//
|
||||
// if (curl_res != 0) {
|
||||
// return -EACCES;
|
||||
// }
|
||||
//
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
static int create_empty_file(const char * path)
|
||||
{
|
||||
@@ -674,7 +690,7 @@ static int create_empty_file(const char * path)
|
||||
}
|
||||
|
||||
static int ftpfs_mknod(const char* path, mode_t mode, dev_t rdev);
|
||||
static int ftpfs_chmod(const char* path, mode_t mode);
|
||||
static int ftpfs_chmod(const char* path, mode_t mode, struct fuse_file_info* fi);
|
||||
|
||||
static char * flags_to_string(int flags)
|
||||
{
|
||||
@@ -698,13 +714,13 @@ static char * flags_to_string(int flags)
|
||||
static int test_exists(const char* path)
|
||||
{
|
||||
struct stat sbuf;
|
||||
return ftpfs_getattr(path, &sbuf);
|
||||
return ftpfs_getattr(path, &sbuf, NULL);
|
||||
}
|
||||
|
||||
static __off_t test_size(const char* path)
|
||||
{
|
||||
struct stat sbuf;
|
||||
int err = ftpfs_getattr(path, &sbuf);
|
||||
int err = ftpfs_getattr(path, &sbuf, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
return sbuf.st_size;
|
||||
@@ -750,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);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -794,7 +810,7 @@ static int ftpfs_open_common(const char* path, mode_t mode,
|
||||
{
|
||||
sem_wait(&fh->ready);
|
||||
/* chmod makes only sense on O_CREAT */
|
||||
if (fi->flags & O_CREAT) ftpfs_chmod(path, mode);
|
||||
if (fi->flags & O_CREAT) ftpfs_chmod(path, mode, fi);
|
||||
sem_post(&fh->data_need);
|
||||
}
|
||||
else
|
||||
@@ -827,23 +843,21 @@ static int ftpfs_open(const char* path, struct fuse_file_info* fi) {
|
||||
return ftpfs_open_common(path, 0, fi);
|
||||
}
|
||||
|
||||
#if FUSE_VERSION >= 25
|
||||
static int ftpfs_create(const char* path, mode_t mode,
|
||||
struct fuse_file_info* fi) {
|
||||
return ftpfs_open_common(path, mode, fi);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int ftpfs_read(const char* path, char* rbuf, size_t size, off_t offset,
|
||||
struct fuse_file_info* fi) {
|
||||
int ret;
|
||||
struct ftpfs_file *fh = get_ftpfs_file(fi);
|
||||
|
||||
DEBUG(1, "ftpfs_read: %s size=%zu offset=%lld has_write_conn=%d pos=%lld\n", path, size, (long long) offset, fh->write_conn!=0, fh->pos);
|
||||
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)
|
||||
{
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -873,12 +887,13 @@ static int ftpfs_mknod(const char* path, mode_t mode, dev_t rdev) {
|
||||
err = create_empty_file(path);
|
||||
|
||||
if (!err)
|
||||
ftpfs_chmod(path, mode);
|
||||
ftpfs_chmod(path, mode, NULL);
|
||||
|
||||
return op_return(err, "ftpfs_mknod");
|
||||
}
|
||||
|
||||
static int ftpfs_chmod(const char* path, mode_t mode) {
|
||||
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;
|
||||
|
||||
// We can only process a subset of the mode - so strip
|
||||
@@ -917,10 +932,10 @@ static int ftpfs_chmod(const char* path, mode_t mode) {
|
||||
return op_return(err, "ftpfs_chmod");
|
||||
}
|
||||
|
||||
static int ftpfs_chown(const char* path, uid_t uid, gid_t gid) {
|
||||
static int ftpfs_chown(const char* path, uid_t uid, gid_t gid, struct fuse_file_info* fi) {
|
||||
int err = 0;
|
||||
|
||||
DEBUG(1, "ftpfs_chown: %d %d\n", (int)uid, (int)gid);
|
||||
DEBUG(1, "ftpfs_chown: %d %d %p\n", (int)uid, (int)gid, fi);
|
||||
|
||||
struct curl_slist* header = NULL;
|
||||
char* full_path = get_dir_path(path);
|
||||
@@ -957,28 +972,9 @@ static int ftpfs_chown(const char* path, uid_t uid, gid_t gid) {
|
||||
return op_return(err, "ftpfs_chown");
|
||||
}
|
||||
|
||||
static int ftpfs_truncate(const char* path, off_t offset) {
|
||||
DEBUG(1, "ftpfs_truncate: %s len=%lld\n", path, offset);
|
||||
/* we can't use ftpfs_mknod here, because we don't know the right permissions */
|
||||
if (offset == 0) return op_return(create_empty_file(path), "ftpfs_truncate");
|
||||
|
||||
/* fix openoffice problem, truncating exactly to file length */
|
||||
|
||||
__off_t size = (long long int)test_size(path);
|
||||
DEBUG(1, "ftpfs_truncate: %s check filesize=%lld\n", path, (long long int)size);
|
||||
|
||||
if (offset == size)
|
||||
return op_return(0, "ftpfs_ftruncate");
|
||||
|
||||
DEBUG(1, "ftpfs_truncate problem: %s offset != 0 or filesize=%lld != offset\n", path, (long long int)size);
|
||||
|
||||
|
||||
return op_return(-EPERM, "ftpfs_truncate");
|
||||
}
|
||||
|
||||
static int ftpfs_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);
|
||||
|
||||
if (offset == 0)
|
||||
@@ -1003,10 +999,33 @@ static int ftpfs_ftruncate(const char * path , off_t offset, struct fuse_file_in
|
||||
return op_return(-EPERM, "ftpfs_ftruncate");
|
||||
}
|
||||
|
||||
static int ftpfs_utime(const char* path, struct utimbuf* time) {
|
||||
static int ftpfs_truncate(const char* path, off_t offset, struct fuse_file_info* fi) {
|
||||
if (fi)
|
||||
return ftpfs_ftruncate(path, offset, fi);
|
||||
|
||||
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 */
|
||||
if (offset == 0) return op_return(create_empty_file(path), "ftpfs_truncate");
|
||||
|
||||
/* fix openoffice problem, truncating exactly to file length */
|
||||
|
||||
__off_t size = (long long int)test_size(path);
|
||||
DEBUG(1, "ftpfs_truncate: %s check filesize=%lld\n", path, (long long int)size);
|
||||
|
||||
if (offset == size)
|
||||
return op_return(0, "ftpfs_ftruncate");
|
||||
|
||||
DEBUG(1, "ftpfs_truncate problem: %s offset != 0 or filesize=%lld != offset\n", path, (long long int)size);
|
||||
|
||||
|
||||
return op_return(-EPERM, "ftpfs_truncate");
|
||||
}
|
||||
|
||||
static int ftpfs_utimens(const char* path, const struct timespec* time, struct fuse_file_info* fi) {
|
||||
(void) path;
|
||||
(void) time;
|
||||
return op_return(0, "ftpfs_utime");
|
||||
(void) fi;
|
||||
return op_return(0, "ftpfs_utimens");
|
||||
}
|
||||
|
||||
static int ftpfs_rmdir(const char* path) {
|
||||
@@ -1079,7 +1098,7 @@ static int ftpfs_mkdir(const char* path, mode_t mode) {
|
||||
free(cmd);
|
||||
|
||||
if (!err)
|
||||
ftpfs_chmod(path, mode);
|
||||
ftpfs_chmod(path, mode, NULL);
|
||||
|
||||
return op_return(err, "ftpfs_mkdir");
|
||||
}
|
||||
@@ -1123,7 +1142,7 @@ static int ftpfs_write(const char *path, const char *wbuf, size_t size,
|
||||
(void) path;
|
||||
struct ftpfs_file *fh = get_ftpfs_file(fi);
|
||||
|
||||
DEBUG(1, "ftpfs_write: %s size=%zu offset=%lld has_write_conn=%d pos=%lld\n", path, size, (long long) offset, fh->write_conn!=0, fh->pos);
|
||||
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)
|
||||
{
|
||||
@@ -1133,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)
|
||||
{
|
||||
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 */
|
||||
if (!fh->write_may_start)
|
||||
@@ -1141,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);
|
||||
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");
|
||||
}
|
||||
}
|
||||
@@ -1158,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)
|
||||
{
|
||||
/* 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);
|
||||
if (!success)
|
||||
@@ -1210,31 +1229,31 @@ static int ftpfs_flush(const char *path, struct fuse_file_info *fi) {
|
||||
int err = 0;
|
||||
struct ftpfs_file* fh = get_ftpfs_file(fi);
|
||||
|
||||
DEBUG(1, "ftpfs_flush: buf.len=%zu buf.pos=%lld write_conn=%d\n", fh->buf.len, fh->pos, fh->write_conn!=0);
|
||||
|
||||
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) {
|
||||
err = finish_write_thread(fh);
|
||||
if (err) return op_return(err, "ftpfs_flush");
|
||||
|
||||
|
||||
struct stat sbuf;
|
||||
|
||||
|
||||
/* check if the resulting file has the correct size
|
||||
this is important, because we use APPE for continuing
|
||||
writing after a premature flush */
|
||||
err = ftpfs_getattr(path, &sbuf);
|
||||
err = ftpfs_getattr(path, &sbuf, fi);
|
||||
if (err) return op_return(err, "ftpfs_flush");
|
||||
|
||||
|
||||
if (sbuf.st_size != fh->pos)
|
||||
{
|
||||
fh->write_fail_cause = -999;
|
||||
fprintf(stderr, "ftpfs_flush: check filesize problem: size=%lld expected=%lld\n", sbuf.st_size, fh->pos);
|
||||
return op_return(-EIO, "ftpfs_flush");
|
||||
fh->write_fail_cause = -999;
|
||||
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 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if (!fh->dirty) return 0;
|
||||
|
||||
return op_return(-EIO, "ftpfs_flush");
|
||||
@@ -1269,8 +1288,12 @@ static int ftpfs_release(const char* path, struct fuse_file_info* fi) {
|
||||
}
|
||||
|
||||
|
||||
static int ftpfs_rename(const char* from, const char* to) {
|
||||
DEBUG(1, "ftpfs_rename from %s to %s\n", from, to);
|
||||
static int ftpfs_rename(const char* from, const char* to, unsigned int flags) {
|
||||
DEBUG(1, "ftpfs_rename from %s to %s (%u)\n", from, to, flags);
|
||||
|
||||
if(flags != 0)
|
||||
return -EINVAL;
|
||||
|
||||
int err = 0;
|
||||
char* rnfr = g_strdup_printf("RNFR %s", from + 1);
|
||||
char* rnto = g_strdup_printf("RNTO %s", to + 1);
|
||||
@@ -1327,13 +1350,15 @@ static int ftpfs_readlink(const char *path, char *linkbuf, size_t size) {
|
||||
|
||||
if (curl_res != 0) {
|
||||
DEBUG(1, "%s\n", error_buf);
|
||||
}
|
||||
buf_null_terminate(&buf);
|
||||
err = -EIO;
|
||||
} else {
|
||||
buf_null_terminate(&buf);
|
||||
|
||||
char* name = strrchr(path, '/');
|
||||
++name;
|
||||
err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
|
||||
name, NULL, linkbuf, size, NULL, NULL);
|
||||
char* name = strrchr(path, '/');
|
||||
++name;
|
||||
err = parse_dir((char*)buf.p, dir_path + strlen(ftpfs.host) - 1,
|
||||
name, NULL, linkbuf, size, NULL, NULL);
|
||||
}
|
||||
|
||||
free(dir_path);
|
||||
buf_free(&buf);
|
||||
@@ -1341,7 +1366,6 @@ static int ftpfs_readlink(const char *path, char *linkbuf, size_t size) {
|
||||
return op_return(0, "ftpfs_readlink");
|
||||
}
|
||||
|
||||
#if FUSE_VERSION >= 25
|
||||
static int ftpfs_statfs(const char *path, struct statvfs *buf)
|
||||
{
|
||||
(void) path;
|
||||
@@ -1356,21 +1380,15 @@ static int ftpfs_statfs(const char *path, struct statvfs *buf)
|
||||
buf->f_ffree = 999999999;
|
||||
return op_return(0, "ftpfs_statfs");
|
||||
}
|
||||
#else
|
||||
static int ftpfs_statfs(const char *path, struct statfs *buf)
|
||||
{
|
||||
(void) path;
|
||||
|
||||
buf->f_namelen = 255;
|
||||
buf->f_bsize = ftpfs.blksize;
|
||||
buf->f_blocks = 999999999 * 2;
|
||||
buf->f_bfree = 999999999 * 2;
|
||||
buf->f_bavail = 999999999 * 2;
|
||||
buf->f_files = 999999999;
|
||||
buf->f_ffree = 999999999;
|
||||
return op_return(0, "ftpfs_statfs");
|
||||
}
|
||||
#endif
|
||||
// static void *ftpfs_init(struct fuse_conn_info *conn,
|
||||
// struct fuse_config *cfg)
|
||||
// {
|
||||
// // alternatively, mount with `-o readdirplus=no`
|
||||
// conn->want &= ~FUSE_CAP_READDIRPLUS;
|
||||
// // alternatively, mount with `-o sync_read`
|
||||
// conn->want &= ~FUSE_CAP_ASYNC_READ;
|
||||
// }
|
||||
|
||||
static struct fuse_cache_operations ftpfs_oper = {
|
||||
.oper = {
|
||||
@@ -1386,7 +1404,7 @@ static struct fuse_cache_operations ftpfs_oper = {
|
||||
.chmod = ftpfs_chmod,
|
||||
.chown = ftpfs_chown,
|
||||
.truncate = ftpfs_truncate,
|
||||
.utime = ftpfs_utime,
|
||||
.utimens = ftpfs_utimens,
|
||||
.open = ftpfs_open,
|
||||
.flush = ftpfs_flush,
|
||||
.fsync = ftpfs_fsync,
|
||||
@@ -1394,22 +1412,14 @@ static struct fuse_cache_operations ftpfs_oper = {
|
||||
.read = ftpfs_read,
|
||||
.write = ftpfs_write,
|
||||
.statfs = ftpfs_statfs,
|
||||
#if FUSE_VERSION >= 25
|
||||
.create = ftpfs_create,
|
||||
.ftruncate = ftpfs_ftruncate,
|
||||
// .fgetattr = ftpfs_fgetattr,
|
||||
#endif
|
||||
.readdir = ftpfs_readdir,
|
||||
},
|
||||
.cache_getdir = ftpfs_getdir,
|
||||
};
|
||||
|
||||
static int curlftpfs_fuse_main(struct fuse_args *args)
|
||||
{
|
||||
#if FUSE_VERSION >= 26
|
||||
return fuse_main(args->argc, args->argv, cache_init(&ftpfs_oper), NULL);
|
||||
#else
|
||||
return fuse_main(args->argc, args->argv, cache_init(&ftpfs_oper));
|
||||
#endif
|
||||
}
|
||||
|
||||
static int ftpfs_opt_proc(void* data, const char* arg, int key,
|
||||
@@ -1441,7 +1451,7 @@ static int ftpfs_opt_proc(void* data, const char* arg, int key,
|
||||
ftpfs.verbose = 1;
|
||||
return 0;
|
||||
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,
|
||||
ftpfs.curl_version->version,
|
||||
FUSE_MAJOR_VERSION,
|
||||
@@ -1464,6 +1474,8 @@ static void usage(const char* progname) {
|
||||
"\n"
|
||||
"FTP options:\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"
|
||||
" disable_epsv use PASV, without trying EPSV first (default)\n"
|
||||
" enable_epsv try EPSV before reverting to PASV\n"
|
||||
@@ -1508,6 +1520,7 @@ static void usage(const char* progname) {
|
||||
" utf8 try to transfer file list with utf-8 encoding\n"
|
||||
" codepage=STR set the codepage the server uses\n"
|
||||
" iocharset=STR set the charset used by the client\n"
|
||||
" exit_after_connect after connecting to the FTP server, instead of passing control to FUSE, exit\n"
|
||||
"\n"
|
||||
"CurlFtpFS cache options: \n"
|
||||
" cache=yes|no enable/disable cache (default: yes)\n"
|
||||
@@ -1593,7 +1606,7 @@ static void set_common_curl_stuff(CURL* easy) {
|
||||
* with version 7.15.4 */
|
||||
if (ftpfs.use_ssl > CURLFTPSSL_TRY &&
|
||||
ftpfs.curl_version->version_num <= CURLFTPFS_BAD_SSL) {
|
||||
fprintf(stderr,
|
||||
dprintf(ftpfs.stderr_fd,
|
||||
"WARNING: you are using libcurl %s.\n"
|
||||
"This version of libcurl does not respect the mandatory SSL flag.\n"
|
||||
"It will try to send the user and password even if the server doesn't support\n"
|
||||
@@ -1603,10 +1616,10 @@ static void set_common_curl_stuff(CURL* easy) {
|
||||
int i;
|
||||
const int time_to_wait = 10;
|
||||
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);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
dprintf(ftpfs.stderr_fd, "\n");
|
||||
}
|
||||
curl_easy_setopt_or_die(easy, CURLOPT_FTP_SSL, ftpfs.use_ssl);
|
||||
|
||||
@@ -1690,6 +1703,7 @@ static void checkpasswd(const char *kind, /* for what purpose */
|
||||
if(!ptr) {
|
||||
/* no password present, prompt for one */
|
||||
char *passwd;
|
||||
char passwd_buf[256];
|
||||
char prompt[256];
|
||||
size_t passwdlen;
|
||||
size_t userlen = strlen(*userpwd);
|
||||
@@ -1701,7 +1715,7 @@ static void checkpasswd(const char *kind, /* for what purpose */
|
||||
kind, *userpwd);
|
||||
|
||||
/* get password */
|
||||
passwd = getpass(prompt);
|
||||
passwd = readpassphrase(prompt, passwd_buf, 256, 0 /* flags */);
|
||||
passwdlen = strlen(passwd);
|
||||
|
||||
/* extend the allocated memory area to fit the password too */
|
||||
@@ -1718,23 +1732,6 @@ static void checkpasswd(const char *kind, /* for what purpose */
|
||||
}
|
||||
}
|
||||
|
||||
#if FUSE_VERSION == 25
|
||||
static int fuse_opt_insert_arg(struct fuse_args *args, int pos,
|
||||
const char *arg)
|
||||
{
|
||||
assert(pos <= args->argc);
|
||||
if (fuse_opt_add_arg(args, arg) == -1)
|
||||
return -1;
|
||||
|
||||
if (pos != args->argc - 1) {
|
||||
char *newarg = args->argv[args->argc - 1];
|
||||
memmove(&args->argv[pos + 1], &args->argv[pos],
|
||||
sizeof(char *) * (args->argc - pos - 1));
|
||||
args->argv[pos] = newarg;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int res;
|
||||
@@ -1759,9 +1756,20 @@ int main(int argc, char** argv) {
|
||||
if (fuse_opt_parse(&args, &ftpfs, ftpfs_opts, ftpfs_opt_proc) == -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) {
|
||||
fprintf(stderr, "missing host\n");
|
||||
fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
|
||||
dprintf(ftpfs.stderr_fd, "missing host\n");
|
||||
dprintf(ftpfs.stderr_fd, "see `%s -h' for usage\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -1775,7 +1783,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
easy = curl_easy_init();
|
||||
if (easy == NULL) {
|
||||
fprintf(stderr, "Error initializing libcurl\n");
|
||||
dprintf(ftpfs.stderr_fd, "Error initializing libcurl\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -1787,7 +1795,7 @@ int main(int argc, char** argv) {
|
||||
checkpasswd("proxy", &ftpfs.proxy_user);
|
||||
|
||||
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);
|
||||
}
|
||||
if (!ftpfs.transform_symlinks)
|
||||
@@ -1804,14 +1812,14 @@ int main(int argc, char** argv) {
|
||||
curl_easy_setopt_or_die(easy, CURLOPT_NOBODY, ftpfs.safe_nobody);
|
||||
curl_res = curl_easy_perform(easy);
|
||||
if (curl_res != 0) {
|
||||
fprintf(stderr, "Error connecting to ftp: %s\n", error_buf);
|
||||
dprintf(ftpfs.stderr_fd, "Error connecting to ftp: %s\n", error_buf);
|
||||
exit(1);
|
||||
}
|
||||
curl_easy_setopt_or_die(easy, CURLOPT_NOBODY, 0);
|
||||
|
||||
ftpfs.multi = curl_multi_init();
|
||||
if (ftpfs.multi == NULL) {
|
||||
fprintf(stderr, "Error initializing libcurl multi\n");
|
||||
dprintf(ftpfs.stderr_fd, "Error initializing libcurl multi\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -1823,7 +1831,10 @@ int main(int argc, char** argv) {
|
||||
fuse_opt_insert_arg(&args, 1, tmp);
|
||||
g_free(tmp);
|
||||
|
||||
res = curlftpfs_fuse_main(&args);
|
||||
|
||||
if (!ftpfs.exit_after_connect) {
|
||||
res = curlftpfs_fuse_main(&args);
|
||||
}
|
||||
|
||||
cancel_previous_multi();
|
||||
curl_multi_cleanup(ftpfs.multi);
|
||||
|
12
ftpfs.h
12
ftpfs.h
@@ -13,6 +13,7 @@
|
||||
#include <curl/easy.h>
|
||||
#include <pthread.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
|
||||
struct ftpfs {
|
||||
char* host;
|
||||
@@ -25,6 +26,8 @@ struct ftpfs {
|
||||
unsigned blksize;
|
||||
int verbose;
|
||||
int debug;
|
||||
char *stderr_path;
|
||||
int stderr_fd;
|
||||
int transform_symlinks;
|
||||
int disable_epsv;
|
||||
int skip_pasv_ip;
|
||||
@@ -67,6 +70,7 @@ struct ftpfs {
|
||||
char *codepage;
|
||||
char *iocharset;
|
||||
int multiconn;
|
||||
int exit_after_connect;
|
||||
};
|
||||
|
||||
extern struct ftpfs ftpfs;
|
||||
@@ -74,10 +78,10 @@ extern struct ftpfs ftpfs;
|
||||
#define DEBUG(level, args...) \
|
||||
do { if (level <= ftpfs.debug) {\
|
||||
int i = 0; \
|
||||
while (++i < level) fprintf(stderr, " "); \
|
||||
fprintf(stderr, "%ld ", time(NULL));\
|
||||
fprintf(stderr, __FILE__ ":%d ", __LINE__);\
|
||||
fprintf(stderr, args);\
|
||||
while (++i < level) dprintf(ftpfs.stderr_fd, " "); \
|
||||
dprintf(ftpfs.stderr_fd, "%ld ", time(NULL));\
|
||||
dprintf(ftpfs.stderr_fd, __FILE__ ":%d ", __LINE__);\
|
||||
dprintf(ftpfs.stderr_fd, args);\
|
||||
}\
|
||||
} while(0)
|
||||
|
||||
|
@@ -5038,7 +5038,7 @@ EOF
|
||||
/* -DDEBUG is fairly common in CFLAGS. */
|
||||
#undef DEBUG
|
||||
#if defined DEBUGWRAPPER
|
||||
# define DEBUG(format, ...) fprintf(stderr, format, __VA_ARGS__)
|
||||
# define DEBUG(format, ...) dprintf(ftpfs.stderr_fd, format, __VA_ARGS__)
|
||||
#else
|
||||
# define DEBUG(format, ...)
|
||||
#endif
|
||||
@@ -5291,9 +5291,9 @@ static void
|
||||
lt_error_core (int exit_status, const char * mode,
|
||||
const char * message, va_list ap)
|
||||
{
|
||||
fprintf (stderr, "%s: %s: ", program_name, mode);
|
||||
vfprintf (stderr, message, ap);
|
||||
fprintf (stderr, ".\n");
|
||||
dprintf (ftpfs.stderr_fd, "%s: %s: ", program_name, mode);
|
||||
vdprintf (ftpfs.stderr_fd, message, ap);
|
||||
dprintf (ftpfs.stderr_fd, ".\n");
|
||||
|
||||
if (exit_status >= 0)
|
||||
exit (exit_status);
|
||||
|
@@ -84,7 +84,7 @@ char* get_dir_path(const char* path) {
|
||||
|
||||
ret = g_strdup_printf("%s%.*s%s",
|
||||
ftpfs.host,
|
||||
lastdir - path,
|
||||
(int)(lastdir - path),
|
||||
path,
|
||||
lastdir - path ? "/" : "");
|
||||
|
||||
|
Reference in New Issue
Block a user