Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3cfb3a6dab | |||
| 790bb8dd36 | |||
| accc218cdb | |||
| 8468900705 | |||
| b60a970602 | |||
| 27c1b7b09d | |||
| 6f0233e11e | |||
| 89da93d07e | |||
| cb4239036a | |||
| 40393a4b0b | |||
| a6e0d2ddd9 | |||
| 1cb9aba6ea | |||
| 3883b4eb0c | |||
| f1f738d5b8 | |||
| d2a3ed327e | |||
| cd6a924cdc | |||
| 258c5ff728 | |||
| 747ffa12f6 | |||
| bad7f96bad | |||
| a162ae9bd4 | |||
| 95116f4b12 | |||
| 3f854ace05 | |||
| 6a6476b52b | |||
| 57a490e12a | |||
| eddbd3967b | |||
| 891a3e5d87 | |||
| 5dd6469a84 | |||
| aa44e01549 | |||
| e916cdd633 | |||
| 0096ea860e | |||
| 1f3cdff4ed | |||
| c3b0185deb | |||
| 7e97377d52 | |||
| adba209812 | |||
| 5377a9a571 | |||
| cea6b546d1 | |||
| 032d201d02 | |||
| de2f4aabd1 | |||
| 642d16587c | |||
| 8bba944723 | |||
| ad284c89a7 | |||
| 9e1c63ecb9 | |||
| 9f6bf0da77 | |||
| 81928eda76 | |||
| 1ff673ab5e | |||
| 6d0e39592d | |||
| 0bcb97098a | |||
| 8e2bfd7c4b | |||
| d5f5babffd | |||
| 42076a8575 | |||
| 0358c96422 | |||
| 205f9ac27e | |||
| 95381c9bb4 | |||
| 42676ca0f6 | |||
| 5098495dbb | |||
| adf379b056 | |||
| 82d15b68d4 | |||
| 2d959740ea | |||
| 28495b3bb2 | |||
| 61a154807b | |||
| c67cf9b98b | |||
| effd586502 | |||
| b1ddd5f430 |
@@ -1,3 +1,186 @@
|
||||
2006-02-20 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.5
|
||||
|
||||
2006-02-19 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix compilation with old FUSE versions. Report by Kurt George
|
||||
Gjerde
|
||||
|
||||
2006-02-16 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Block TERM/INT/HUP/QUIT signals in sshfs reading thread, so they
|
||||
will always be received by the FUSE main thread. Fixes the
|
||||
"double ^C" problem seen on FreeBSD.
|
||||
|
||||
* Use 'auto_cache' option if available, so that file cache is not
|
||||
thrown away on each open. This should make repeated reading of a
|
||||
file much much faster.
|
||||
|
||||
2006-01-31 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix problems with nodelay workaround on FreeBSD.
|
||||
|
||||
2006-01-30 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix data consitency bug if readahead is enabled and writes are
|
||||
intermixed with reads. Solution is far from optimal, since it
|
||||
will prevent readahead in the above situation. If used with FUSE
|
||||
>= 2.6.0 with Linux-2.6.X, readahead will be done by the kernel,
|
||||
and hence there will be no performance penalty. Bug reported and
|
||||
test program written by Wolfgang Köbler. Further testing on
|
||||
FreeBSD by Csaba Henk
|
||||
|
||||
2006-01-29 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add '-olarge_read' option for Linux-2.4.*. This should
|
||||
generally improve download performance
|
||||
|
||||
2006-01-27 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add workaround (enabled by default) for ssh clients not setting
|
||||
TCP_NODELAY on the network connection. Currently this is all
|
||||
known versions of openssh. This may improve download speed in
|
||||
some circumstances
|
||||
|
||||
* Make it possible to idividually disable workarounds with a "no"
|
||||
prefix
|
||||
|
||||
* Change '-otransform_symlinks' not to prefix with the mountpoint
|
||||
(which is fragile wrt. moving/binding the mount). Rather convert
|
||||
absolute symlinks to relative if possible. Does not yet work if
|
||||
base path is itself relative
|
||||
|
||||
2006-01-25 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Use TCP_NODELAY socket option for direct connection. This may
|
||||
improve download speed in some circumstances
|
||||
|
||||
2006-01-20 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Update to fuse API 2.6
|
||||
|
||||
* Don't do readahead in sshfs if it's done in kernel
|
||||
|
||||
2006-01-14 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.4
|
||||
|
||||
2006-01-09 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Added 'transform_symlinks' option to "fix-up" absolute symlinks.
|
||||
Patch by Paul Jarc
|
||||
|
||||
* Add option parsing implementation, so linking with older than
|
||||
2.5.0 libfuse still works.
|
||||
|
||||
2005-12-09 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Use new option parsing interface of FUSE
|
||||
|
||||
2005-11-28 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Set statvfs::f_frsize
|
||||
|
||||
2005-11-23 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix warnings on 64bit systems. Reported by D. R. Evans
|
||||
|
||||
2005-11-16 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Replace EPROTO with the more portable EIO
|
||||
|
||||
* Fix bug in option parsing. Reported by Csaba Henk
|
||||
|
||||
2005-10-29 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Use FUSE version 25 if available
|
||||
|
||||
* Add <sys/socket.h> include. Reported by Csaba Henk
|
||||
|
||||
2005-10-29 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.3
|
||||
|
||||
2005-10-28 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add atomic create+open and ftruncate operation. This should fix
|
||||
issues with 'cp' and other programs failing with "Permission
|
||||
denied". To be effective, needs FUSE version 2.5 and kernel
|
||||
version 2.6.15 (just a guess, since neither of them is released
|
||||
yet).
|
||||
|
||||
2005-10-27 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add support for SSH protocol version 1. Bug reported by Miklos
|
||||
Bagi Jr.
|
||||
|
||||
2005-10-26 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix typo (ConnectTimeout -> ConnectionTimeout) in ssh options.
|
||||
Bug reported by Miklos Bagi Jr.
|
||||
|
||||
2005-10-19 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* GNOME Nautilus fails to copy file to sshfs filesystem, because
|
||||
FUSE returns zero free space. So instead return huge (999999999
|
||||
kbytes) amount of free space, yet it should be obvious that the
|
||||
number is artificial. Bug report by Peter Kronheimer
|
||||
|
||||
2005-10-18 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add remote uid detection and translation ('idmap=user' option).
|
||||
Idea and implementation details worked out by Jean-Marc Valin
|
||||
|
||||
2005-10-17 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add one more missing lock.
|
||||
|
||||
* Add workaround for failure to rename to an existing file. Based
|
||||
on patch by Michael Best
|
||||
|
||||
2005-10-15 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Protect request ID allocation with mutex. Bug report by Tvrtko
|
||||
Ursulin
|
||||
|
||||
2005-08-17 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Try to calculate approximate disk usage of files from their
|
||||
size, so that 'du' returns meaningful results. Problem reported
|
||||
by Bernd Amend.
|
||||
|
||||
2005-08-15 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.2
|
||||
|
||||
2005-08-13 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add 'reconnect' option, which tries to reconnect to the server
|
||||
when the connection is broken. If a password is required for
|
||||
connection, it is recommended that you install ssh-askpass, and
|
||||
set the SSH_ASKPASS environment variable (see 'man ssh' for more
|
||||
details).
|
||||
|
||||
2005-05-05 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Work around missing truncate() support in some older sftp
|
||||
servers (only works for zero size truncate). Thanks to Eduard
|
||||
Czimbalmos for the bug report and help with testing
|
||||
|
||||
2005-04-13 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Fix compilation with gcc-2.95. Reported by David A. Gershman
|
||||
|
||||
2005-03-08 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Make it work on server version 2 (e.g. Sun_SSH_1.0.1). Report
|
||||
and testing by Pieter J. Kersten
|
||||
|
||||
2005-03-04 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.1
|
||||
|
||||
2005-03-03 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Slightly optimize readahead. Still not clever enough to always
|
||||
@@ -19,7 +202,7 @@
|
||||
|
||||
* Added asynchronous writeback (big performance gain) and made
|
||||
this the default. Can be disabled with '-o sshfs_sync'
|
||||
|
||||
|
||||
2005-02-09 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Added option to start arbitary command instead of 'ssh'
|
||||
@@ -30,11 +213,11 @@
|
||||
2005-02-08 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Add caching of symlinks
|
||||
|
||||
|
||||
* Add support for many ssh options to be passed to ssh
|
||||
|
||||
* Port number can now actually be specified with "-o port=PORT",
|
||||
bug spotted by Nadia Maksymiw
|
||||
bug spotted by Andrew Ukrainec
|
||||
|
||||
2005-02-07 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
@@ -46,7 +229,7 @@
|
||||
|
||||
Fix PKG_CONFIG_PATH setting in configure.ac (reported by Alpar
|
||||
Juttner)
|
||||
|
||||
|
||||
2005-01-09 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* Released 1.0
|
||||
|
||||
+24
-2
@@ -1,5 +1,27 @@
|
||||
## Process this file with automake to produce Makefile.in
|
||||
|
||||
bin_PROGRAMS = sshfs
|
||||
sshfs_SOURCES = sshfs.c cache.c opts.c cache.h opts.h
|
||||
sshfs_CPPFLAGS = -DFUSE_USE_VERSION=22
|
||||
|
||||
sshfs_SOURCES = sshfs.c cache.c cache.h
|
||||
if FUSE_OPT_COMPAT
|
||||
sshfs_SOURCES += compat/fuse_opt.c compat/fuse_opt.h
|
||||
endif
|
||||
|
||||
sshfs_LDADD = $(SSHFS_LIBS)
|
||||
sshfs_CFLAGS = $(SSHFS_CFLAGS)
|
||||
sshfs_CPPFLAGS = -D_REENTRANT -DFUSE_USE_VERSION=26 -DLIBDIR=\"$(libdir)\"
|
||||
|
||||
EXTRA_DIST = sshnodelay.c
|
||||
CLEANFILES = sshnodelay.so
|
||||
|
||||
all-local: sshnodelay.so
|
||||
|
||||
install-exec-local: sshnodelay.so
|
||||
test -z "$(libdir)" || $(mkdir_p) "$(DESTDIR)$(libdir)"
|
||||
$(INSTALL) -m 755 sshnodelay.so "$(DESTDIR)$(libdir)/sshnodelay.so"
|
||||
|
||||
uninstall-local:
|
||||
rm -f "$(DESTDIR)$(libdir)/sshnodelay.so"
|
||||
|
||||
sshnodelay.so:
|
||||
$(CC) -Wall -W -s --shared $(sshnodelay_libs) sshnodelay.c -o sshnodelay.so
|
||||
|
||||
@@ -1,3 +1,48 @@
|
||||
What is new in 1.5
|
||||
------------------
|
||||
|
||||
* Improvements to read performance. Now both read and write
|
||||
throughput should be very close to 'scp'
|
||||
|
||||
* If used with FUSE 2.6.0 or later, then perform better data caching.
|
||||
This should show dramatic speed improvements when a file is opened
|
||||
more than once
|
||||
|
||||
* Bug fixes
|
||||
|
||||
What is new in 1.4
|
||||
------------------
|
||||
|
||||
* Updated to version 25 of libfuse API
|
||||
|
||||
* This means that the 'cp' of readonly file to sshfs bug is finally
|
||||
solved (as long as using libfuse 2.5.0 or later *and* Linux 2.6.15
|
||||
or later)
|
||||
|
||||
* Sshfs now works on FreeBSD
|
||||
|
||||
* Added option to "transform" absolute symbolic links
|
||||
|
||||
What is new in 1.3
|
||||
------------------
|
||||
|
||||
* Add workaround for failure to rename to an existing file
|
||||
|
||||
* Simple user ID mapping
|
||||
|
||||
* Estimate disk usage of files based on size
|
||||
|
||||
* Report "infinite" disk space
|
||||
|
||||
* Bug fixes
|
||||
|
||||
What is new in 1.2
|
||||
------------------
|
||||
|
||||
* Better compatibility with different sftp servers
|
||||
|
||||
* Automatic reconnect (optional)
|
||||
|
||||
What is new in 1.1
|
||||
------------------
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ The idea of sshfs was taken from the SSHFS filesystem distributed with
|
||||
LUFS, which I found very useful. There were some limitations of that
|
||||
codebase, so I rewrote it. Features of this implementation are:
|
||||
|
||||
- Based on FUSE (the best userspace filesystem framework for linux ;)
|
||||
- Based on FUSE (the best userspace filesystem framework for Linux ;)
|
||||
|
||||
- Multithreading: more than one request can be on it's way to the
|
||||
server
|
||||
@@ -20,6 +20,15 @@ codebase, so I rewrote it. Features of this implementation are:
|
||||
|
||||
- Caching directory contents
|
||||
|
||||
- Reconnect on failure
|
||||
|
||||
Latest version
|
||||
==============
|
||||
|
||||
The latest version and more information can be found on
|
||||
|
||||
http://fuse.sourceforge.net/sshfs.html
|
||||
|
||||
|
||||
How to mount a filesystem
|
||||
=========================
|
||||
@@ -61,11 +70,5 @@ installing FUSE, compile sshfs the usual way:
|
||||
|
||||
And you are ready to go.
|
||||
|
||||
|
||||
Bugs and feature requests
|
||||
=========================
|
||||
|
||||
Send bug reports to <miklos@szeredi.hu>.
|
||||
|
||||
Good luck!
|
||||
Miklos Szeredi
|
||||
If checking out from CVS for the first time also do 'autoreconf -i'
|
||||
before doing './configure'.
|
||||
|
||||
@@ -14,17 +14,23 @@
|
||||
#include <glib.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "opts.h"
|
||||
|
||||
#define DEFAULT_CACHE_TIMEOUT 20
|
||||
#define MAX_CACHE_SIZE 10000
|
||||
#define MIN_CACHE_CLEAN_INTERVAL 5
|
||||
#define CACHE_CLEAN_INTERVAL 60
|
||||
|
||||
static int cache_on = 1;
|
||||
static unsigned cache_stat_timeout = DEFAULT_CACHE_TIMEOUT;
|
||||
static unsigned cache_dir_timeout = DEFAULT_CACHE_TIMEOUT;
|
||||
static unsigned cache_link_timeout = DEFAULT_CACHE_TIMEOUT;
|
||||
struct cache {
|
||||
int on;
|
||||
unsigned stat_timeout;
|
||||
unsigned dir_timeout;
|
||||
unsigned link_timeout;
|
||||
struct fuse_cache_operations *next_oper;
|
||||
GHashTable *table;
|
||||
pthread_mutex_t lock;
|
||||
time_t last_cleaned;
|
||||
};
|
||||
|
||||
static struct cache cache;
|
||||
|
||||
struct node {
|
||||
struct stat stat;
|
||||
@@ -43,11 +49,6 @@ struct fuse_cache_dirhandle {
|
||||
GPtrArray *dir;
|
||||
};
|
||||
|
||||
static struct fuse_cache_operations *next_oper;
|
||||
static GHashTable *cache;
|
||||
static pthread_mutex_t cache_lock;
|
||||
static time_t last_cleaned;
|
||||
|
||||
static void free_node(gpointer node_)
|
||||
{
|
||||
struct node *node = (struct node *) node_;
|
||||
@@ -67,22 +68,23 @@ static int cache_clean_entry(void *key_, struct node *node, time_t *now)
|
||||
static void cache_clean(void)
|
||||
{
|
||||
time_t now = time(NULL);
|
||||
if (now > last_cleaned + MIN_CACHE_CLEAN_INTERVAL &&
|
||||
(g_hash_table_size(cache) > MAX_CACHE_SIZE ||
|
||||
now > last_cleaned + CACHE_CLEAN_INTERVAL)) {
|
||||
g_hash_table_foreach_remove(cache, (GHRFunc) cache_clean_entry, &now);
|
||||
last_cleaned = now;
|
||||
if (now > cache.last_cleaned + MIN_CACHE_CLEAN_INTERVAL &&
|
||||
(g_hash_table_size(cache.table) > MAX_CACHE_SIZE ||
|
||||
now > cache.last_cleaned + CACHE_CLEAN_INTERVAL)) {
|
||||
g_hash_table_foreach_remove(cache.table,
|
||||
(GHRFunc) cache_clean_entry, &now);
|
||||
cache.last_cleaned = now;
|
||||
}
|
||||
}
|
||||
|
||||
static struct node *cache_lookup(const char *path)
|
||||
{
|
||||
return (struct node *) g_hash_table_lookup(cache, path);
|
||||
return (struct node *) g_hash_table_lookup(cache.table, path);
|
||||
}
|
||||
|
||||
static void cache_purge(const char *path)
|
||||
{
|
||||
g_hash_table_remove(cache, path);
|
||||
g_hash_table_remove(cache.table, path);
|
||||
}
|
||||
|
||||
static void cache_purge_parent(const char *path)
|
||||
@@ -90,7 +92,7 @@ static void cache_purge_parent(const char *path)
|
||||
const char *s = strrchr(path, '/');
|
||||
if (s) {
|
||||
if (s == path)
|
||||
g_hash_table_remove(cache, "/");
|
||||
g_hash_table_remove(cache.table, "/");
|
||||
else {
|
||||
char *parent = g_strndup(path, s - path);
|
||||
cache_purge(parent);
|
||||
@@ -99,29 +101,29 @@ static void cache_purge_parent(const char *path)
|
||||
}
|
||||
}
|
||||
|
||||
static void cache_invalidate(const char *path)
|
||||
void cache_invalidate(const char *path)
|
||||
{
|
||||
pthread_mutex_lock(&cache_lock);
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
cache_purge(path);
|
||||
pthread_mutex_unlock(&cache_lock);
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
}
|
||||
|
||||
static void cache_invalidate_dir(const char *path)
|
||||
{
|
||||
pthread_mutex_lock(&cache_lock);
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
cache_purge(path);
|
||||
cache_purge_parent(path);
|
||||
pthread_mutex_unlock(&cache_lock);
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
}
|
||||
|
||||
static void cache_do_rename(const char *from, const char *to)
|
||||
{
|
||||
pthread_mutex_lock(&cache_lock);
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
cache_purge(from);
|
||||
cache_purge(to);
|
||||
cache_purge_parent(from);
|
||||
cache_purge_parent(to);
|
||||
pthread_mutex_unlock(&cache_lock);
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
}
|
||||
|
||||
static struct node *cache_get(const char *path)
|
||||
@@ -130,25 +132,25 @@ static struct node *cache_get(const char *path)
|
||||
if (node == NULL) {
|
||||
char *pathcopy = g_strdup(path);
|
||||
node = g_new0(struct node, 1);
|
||||
g_hash_table_insert(cache, pathcopy, node);
|
||||
g_hash_table_insert(cache.table, pathcopy, node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
static 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;
|
||||
time_t now;
|
||||
|
||||
pthread_mutex_lock(&cache_lock);
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
node = cache_get(path);
|
||||
now = time(NULL);
|
||||
node->stat = *stbuf;
|
||||
node->stat_valid = time(NULL) + cache_stat_timeout;
|
||||
node->stat_valid = time(NULL) + cache.stat_timeout;
|
||||
if (node->stat_valid > node->valid)
|
||||
node->valid = node->stat_valid;
|
||||
cache_clean();
|
||||
pthread_mutex_unlock(&cache_lock);
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
}
|
||||
|
||||
static void cache_add_dir(const char *path, char **dir)
|
||||
@@ -156,16 +158,16 @@ static void cache_add_dir(const char *path, char **dir)
|
||||
struct node *node;
|
||||
time_t now;
|
||||
|
||||
pthread_mutex_lock(&cache_lock);
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
node = cache_get(path);
|
||||
now = time(NULL);
|
||||
g_strfreev(node->dir);
|
||||
node->dir = dir;
|
||||
node->dir_valid = time(NULL) + cache_dir_timeout;
|
||||
node->dir_valid = time(NULL) + cache.dir_timeout;
|
||||
if (node->dir_valid > node->valid)
|
||||
node->valid = node->dir_valid;
|
||||
cache_clean();
|
||||
pthread_mutex_unlock(&cache_lock);
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
}
|
||||
|
||||
static size_t my_strnlen(const char *s, size_t maxsize)
|
||||
@@ -180,38 +182,43 @@ static void cache_add_link(const char *path, const char *link, size_t size)
|
||||
struct node *node;
|
||||
time_t now;
|
||||
|
||||
pthread_mutex_lock(&cache_lock);
|
||||
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->link_valid = time(NULL) + cache_link_timeout;
|
||||
node->link_valid = time(NULL) + cache.link_timeout;
|
||||
if (node->link_valid > node->valid)
|
||||
node->valid = node->link_valid;
|
||||
cache_clean();
|
||||
pthread_mutex_unlock(&cache_lock);
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
}
|
||||
|
||||
static int cache_getattr(const char *path, struct stat *stbuf)
|
||||
static int cache_get_attr(const char *path, struct stat *stbuf)
|
||||
{
|
||||
struct node *node;
|
||||
int err;
|
||||
|
||||
pthread_mutex_lock(&cache_lock);
|
||||
int err = -EAGAIN;
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
node = cache_lookup(path);
|
||||
if (node != NULL) {
|
||||
time_t now = time(NULL);
|
||||
if (node->stat_valid - now >= 0) {
|
||||
*stbuf = node->stat;
|
||||
pthread_mutex_unlock(&cache_lock);
|
||||
return 0;
|
||||
err = 0;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&cache_lock);
|
||||
err = next_oper->oper.getattr(path, stbuf);
|
||||
if (!err)
|
||||
cache_add_attr(path, stbuf);
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int cache_getattr(const char *path, struct stat *stbuf)
|
||||
{
|
||||
int err = cache_get_attr(path, stbuf);
|
||||
if (err) {
|
||||
err = cache.next_oper->oper.getattr(path, stbuf);
|
||||
if (!err)
|
||||
cache_add_attr(path, stbuf);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -220,19 +227,19 @@ static int cache_readlink(const char *path, char *buf, size_t size)
|
||||
struct node *node;
|
||||
int err;
|
||||
|
||||
pthread_mutex_lock(&cache_lock);
|
||||
pthread_mutex_lock(&cache.lock);
|
||||
node = cache_lookup(path);
|
||||
if (node != NULL) {
|
||||
time_t now = time(NULL);
|
||||
if (node->link_valid - now >= 0) {
|
||||
strncpy(buf, node->link, size-1);
|
||||
buf[size-1] = '\0';
|
||||
pthread_mutex_unlock(&cache_lock);
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&cache_lock);
|
||||
err = next_oper->oper.readlink(path, buf, size);
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
err = cache.next_oper->oper.readlink(path, buf, size);
|
||||
if (!err)
|
||||
cache_add_link(path, buf, size);
|
||||
|
||||
@@ -244,9 +251,9 @@ static int cache_dirfill(fuse_cache_dirh_t ch, const char *name,
|
||||
{
|
||||
int err = ch->filler(ch->h, name, 0, 0);
|
||||
if (!err) {
|
||||
char *fullpath;
|
||||
g_ptr_array_add(ch->dir, g_strdup(name));
|
||||
char *fullpath = g_strdup_printf("%s/%s",
|
||||
!ch->path[1] ? "" : ch->path, name);
|
||||
fullpath = g_strdup_printf("%s/%s", !ch->path[1] ? "" : ch->path, name);
|
||||
cache_add_attr(fullpath, stbuf);
|
||||
g_free(fullpath);
|
||||
}
|
||||
@@ -260,24 +267,24 @@ static int cache_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler)
|
||||
char **dir;
|
||||
struct node *node;
|
||||
|
||||
pthread_mutex_lock(&cache_lock);
|
||||
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);
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
pthread_mutex_unlock(&cache_lock);
|
||||
pthread_mutex_unlock(&cache.lock);
|
||||
|
||||
ch.path = path;
|
||||
ch.h = h;
|
||||
ch.filler = filler;
|
||||
ch.dir = g_ptr_array_new();
|
||||
err = next_oper->cache_getdir(path, &ch, cache_dirfill);
|
||||
err = cache.next_oper->cache_getdir(path, &ch, cache_dirfill);
|
||||
g_ptr_array_add(ch.dir, NULL);
|
||||
dir = (char **) ch.dir->pdata;
|
||||
if (!err)
|
||||
@@ -301,12 +308,12 @@ static int cache_unity_getdir(const char *path, fuse_dirh_t h,
|
||||
struct fuse_cache_dirhandle ch;
|
||||
ch.h = h;
|
||||
ch.filler = filler;
|
||||
return next_oper->cache_getdir(path, &ch, cache_unity_dirfill);
|
||||
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 = next_oper->oper.mknod(path, mode, rdev);
|
||||
int err = cache.next_oper->oper.mknod(path, mode, rdev);
|
||||
if (!err)
|
||||
cache_invalidate_dir(path);
|
||||
return err;
|
||||
@@ -314,7 +321,7 @@ static int cache_mknod(const char *path, mode_t mode, dev_t rdev)
|
||||
|
||||
static int cache_mkdir(const char *path, mode_t mode)
|
||||
{
|
||||
int err = next_oper->oper.mkdir(path, mode);
|
||||
int err = cache.next_oper->oper.mkdir(path, mode);
|
||||
if (!err)
|
||||
cache_invalidate_dir(path);
|
||||
return err;
|
||||
@@ -322,7 +329,7 @@ static int cache_mkdir(const char *path, mode_t mode)
|
||||
|
||||
static int cache_unlink(const char *path)
|
||||
{
|
||||
int err = next_oper->oper.unlink(path);
|
||||
int err = cache.next_oper->oper.unlink(path);
|
||||
if (!err)
|
||||
cache_invalidate_dir(path);
|
||||
return err;
|
||||
@@ -330,7 +337,7 @@ static int cache_unlink(const char *path)
|
||||
|
||||
static int cache_rmdir(const char *path)
|
||||
{
|
||||
int err = next_oper->oper.rmdir(path);
|
||||
int err = cache.next_oper->oper.rmdir(path);
|
||||
if (!err)
|
||||
cache_invalidate_dir(path);
|
||||
return err;
|
||||
@@ -338,7 +345,7 @@ static int cache_rmdir(const char *path)
|
||||
|
||||
static int cache_symlink(const char *from, const char *to)
|
||||
{
|
||||
int err = next_oper->oper.symlink(from, to);
|
||||
int err = cache.next_oper->oper.symlink(from, to);
|
||||
if (!err)
|
||||
cache_invalidate_dir(to);
|
||||
return err;
|
||||
@@ -346,7 +353,7 @@ static int cache_symlink(const char *from, const char *to)
|
||||
|
||||
static int cache_rename(const char *from, const char *to)
|
||||
{
|
||||
int err = next_oper->oper.rename(from, to);
|
||||
int err = cache.next_oper->oper.rename(from, to);
|
||||
if (!err)
|
||||
cache_do_rename(from, to);
|
||||
return err;
|
||||
@@ -354,7 +361,7 @@ static int cache_rename(const char *from, const char *to)
|
||||
|
||||
static int cache_link(const char *from, const char *to)
|
||||
{
|
||||
int err = next_oper->oper.link(from, to);
|
||||
int err = cache.next_oper->oper.link(from, to);
|
||||
if (!err) {
|
||||
cache_invalidate(from);
|
||||
cache_invalidate_dir(to);
|
||||
@@ -364,7 +371,7 @@ static int cache_link(const char *from, const char *to)
|
||||
|
||||
static int cache_chmod(const char *path, mode_t mode)
|
||||
{
|
||||
int err = next_oper->oper.chmod(path, mode);
|
||||
int err = cache.next_oper->oper.chmod(path, mode);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
@@ -372,7 +379,7 @@ static int cache_chmod(const char *path, mode_t mode)
|
||||
|
||||
static int cache_chown(const char *path, uid_t uid, gid_t gid)
|
||||
{
|
||||
int err = next_oper->oper.chown(path, uid, gid);
|
||||
int err = cache.next_oper->oper.chown(path, uid, gid);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
@@ -380,7 +387,7 @@ static int cache_chown(const char *path, uid_t uid, gid_t gid)
|
||||
|
||||
static int cache_truncate(const char *path, off_t size)
|
||||
{
|
||||
int err = next_oper->oper.truncate(path, size);
|
||||
int err = cache.next_oper->oper.truncate(path, size);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
@@ -388,7 +395,7 @@ static int cache_truncate(const char *path, off_t size)
|
||||
|
||||
static int cache_utime(const char *path, struct utimbuf *buf)
|
||||
{
|
||||
int err = next_oper->oper.utime(path, buf);
|
||||
int err = cache.next_oper->oper.utime(path, buf);
|
||||
if (!err)
|
||||
cache_invalidate(path);
|
||||
return err;
|
||||
@@ -397,15 +404,50 @@ static int cache_utime(const char *path, struct utimbuf *buf)
|
||||
static int cache_write(const char *path, const char *buf, size_t size,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
{
|
||||
int res = next_oper->oper.write(path, buf, size, offset, fi);
|
||||
int res = cache.next_oper->oper.write(path, buf, size, offset, fi);
|
||||
if (res >= 0)
|
||||
cache_invalidate(path);
|
||||
return res;
|
||||
}
|
||||
|
||||
#if FUSE_VERSION >= 25
|
||||
static int cache_create(const char *path, mode_t mode,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
int err = cache.next_oper->oper.create(path, mode, fi);
|
||||
if (!err)
|
||||
cache_invalidate_dir(path);
|
||||
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) {
|
||||
err = cache.next_oper->oper.fgetattr(path, stbuf, fi);
|
||||
if (!err)
|
||||
cache_add_attr(path, stbuf);
|
||||
}
|
||||
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;
|
||||
@@ -431,15 +473,20 @@ 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
|
||||
}
|
||||
|
||||
struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
|
||||
{
|
||||
static struct fuse_operations cache_oper;
|
||||
next_oper = oper;
|
||||
cache.next_oper = oper;
|
||||
|
||||
cache_unity_fill(oper, &cache_oper);
|
||||
if (cache_on) {
|
||||
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;
|
||||
@@ -455,10 +502,15 @@ struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
|
||||
cache_oper.truncate = oper->oper.truncate ? cache_truncate : NULL;
|
||||
cache_oper.utime = oper->oper.utime ? cache_utime : NULL;
|
||||
cache_oper.write = oper->oper.write ? cache_write : NULL;
|
||||
pthread_mutex_init(&cache_lock, NULL);
|
||||
cache = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
|
||||
free_node);
|
||||
if (cache == 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
|
||||
pthread_mutex_init(&cache.lock, NULL);
|
||||
cache.table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
|
||||
free_node);
|
||||
if (cache.table == NULL) {
|
||||
fprintf(stderr, "failed to create cache\n");
|
||||
return NULL;
|
||||
}
|
||||
@@ -466,61 +518,24 @@ struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
|
||||
return &cache_oper;
|
||||
}
|
||||
|
||||
enum {
|
||||
COPT_CACHE,
|
||||
COPT_TIMEOUT,
|
||||
COPT_STAT_TIMEOUT,
|
||||
COPT_DIR_TIMEOUT,
|
||||
COPT_LINK_TIMEOUT,
|
||||
COPT_LAST /* Last entry in this list! */
|
||||
static const struct fuse_opt cache_opts[] = {
|
||||
{ "cache=yes", offsetof(struct cache, on), 1 },
|
||||
{ "cache=no", offsetof(struct cache, on), 0 },
|
||||
{ "cache_timeout=%u", offsetof(struct cache, stat_timeout), 0 },
|
||||
{ "cache_timeout=%u", offsetof(struct cache, dir_timeout), 0 },
|
||||
{ "cache_timeout=%u", offsetof(struct cache, link_timeout), 0 },
|
||||
{ "cache_stat_timeout=%u", offsetof(struct cache, stat_timeout), 0 },
|
||||
{ "cache_dir_timeout=%u", offsetof(struct cache, dir_timeout), 0 },
|
||||
{ "cache_link_timeout=%u", offsetof(struct cache, link_timeout), 0 },
|
||||
FUSE_OPT_END
|
||||
};
|
||||
|
||||
static struct opt cache_opts[] = {
|
||||
[COPT_CACHE] = { .optname = "cache" },
|
||||
[COPT_TIMEOUT] = { .optname = "cache_timeout" },
|
||||
[COPT_STAT_TIMEOUT] = { .optname = "cache_stat_timeout" },
|
||||
[COPT_DIR_TIMEOUT] = { .optname = "cache_dir_timeout" },
|
||||
[COPT_LINK_TIMEOUT] = { .optname = "cache_link_timeout" },
|
||||
[COPT_LAST] = { .optname = NULL }
|
||||
};
|
||||
|
||||
static int get_timeout(int sel, unsigned *timeoutp)
|
||||
int cache_parse_options(struct fuse_args *args)
|
||||
{
|
||||
struct opt *o = &cache_opts[sel];
|
||||
if (!o->present)
|
||||
return 0;
|
||||
if (opt_get_unsigned(o, timeoutp) == -1)
|
||||
return -1;
|
||||
return 1;
|
||||
}
|
||||
cache.stat_timeout = DEFAULT_CACHE_TIMEOUT;
|
||||
cache.dir_timeout = DEFAULT_CACHE_TIMEOUT;
|
||||
cache.link_timeout = DEFAULT_CACHE_TIMEOUT;
|
||||
cache.on = 1;
|
||||
|
||||
int cache_parse_options(int *argcp, char *argv[])
|
||||
{
|
||||
unsigned timeout;
|
||||
int res;
|
||||
process_options(argcp, argv, cache_opts, 1);
|
||||
if (cache_opts[COPT_CACHE].present) {
|
||||
char *val = cache_opts[COPT_CACHE].value;
|
||||
if (!val || !val[0] ||
|
||||
(strcmp(val, "yes") != 0 && strcmp(val, "no") != 0)) {
|
||||
fprintf(stderr, "Invalid or missing value for 'cache' option\n");
|
||||
return -1;
|
||||
}
|
||||
if (strcmp(val, "yes") == 0)
|
||||
cache_on = 1;
|
||||
else
|
||||
cache_on = 0;
|
||||
}
|
||||
if ((res = get_timeout(COPT_TIMEOUT, &timeout)) == -1)
|
||||
return -1;
|
||||
if (res == 1) {
|
||||
cache_stat_timeout = timeout;
|
||||
cache_dir_timeout = timeout;
|
||||
cache_link_timeout = timeout;
|
||||
}
|
||||
if(get_timeout(COPT_STAT_TIMEOUT, &cache_stat_timeout) == -1 ||
|
||||
get_timeout(COPT_DIR_TIMEOUT, &cache_dir_timeout) == -1 ||
|
||||
get_timeout(COPT_LINK_TIMEOUT, &cache_link_timeout) == -1)
|
||||
return -1;
|
||||
return 0;
|
||||
return fuse_opt_parse(args, &cache, cache_opts, NULL);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
*/
|
||||
|
||||
#include <fuse.h>
|
||||
#include <fuse_opt.h>
|
||||
|
||||
#ifndef FUSE_VERSION
|
||||
#define FUSE_VERSION (FUSE_MAJOR_VERSION * 10 + FUSE_MINOR_VERSION)
|
||||
#endif
|
||||
|
||||
typedef struct fuse_cache_dirhandle *fuse_cache_dirh_t;
|
||||
typedef int (*fuse_cache_dirfil_t) (fuse_cache_dirh_t h, const char *name,
|
||||
@@ -15,8 +20,9 @@ typedef int (*fuse_cache_dirfil_t) (fuse_cache_dirh_t h, const char *name,
|
||||
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);
|
||||
int cache_parse_options(int *argcp, char *argv[]);
|
||||
int cache_parse_options(struct fuse_args *args);
|
||||
void cache_add_attr(const char *path, const struct stat *stbuf);
|
||||
void cache_invalidate(const char *path);
|
||||
|
||||
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2006 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU LGPL.
|
||||
See the file COPYING.LIB
|
||||
*/
|
||||
|
||||
#include "fuse_opt.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
struct fuse_opt_context {
|
||||
void *data;
|
||||
const struct fuse_opt *opt;
|
||||
fuse_opt_proc_t proc;
|
||||
int argctr;
|
||||
int argc;
|
||||
char **argv;
|
||||
struct fuse_args outargs;
|
||||
char *opts;
|
||||
int nonopt;
|
||||
};
|
||||
|
||||
void fuse_opt_free_args(struct fuse_args *args)
|
||||
{
|
||||
if (args && args->argv && args->allocated) {
|
||||
int i;
|
||||
for (i = 0; i < args->argc; i++)
|
||||
free(args->argv[i]);
|
||||
free(args->argv);
|
||||
args->argv = NULL;
|
||||
args->allocated = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int alloc_failed(void)
|
||||
{
|
||||
fprintf(stderr, "fuse: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fuse_opt_add_arg(struct fuse_args *args, const char *arg)
|
||||
{
|
||||
char **newargv;
|
||||
char *newarg;
|
||||
|
||||
assert(!args->argv || args->allocated);
|
||||
|
||||
newargv = realloc(args->argv, (args->argc + 2) * sizeof(char *));
|
||||
newarg = newargv ? strdup(arg) : NULL;
|
||||
if (!newargv || !newarg)
|
||||
return alloc_failed();
|
||||
|
||||
args->argv = newargv;
|
||||
args->allocated = 1;
|
||||
args->argv[args->argc++] = newarg;
|
||||
args->argv[args->argc] = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int next_arg(struct fuse_opt_context *ctx, const char *opt)
|
||||
{
|
||||
if (ctx->argctr + 1 >= ctx->argc) {
|
||||
fprintf(stderr, "fuse: missing argument after `%s'\n", opt);
|
||||
return -1;
|
||||
}
|
||||
ctx->argctr++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_arg(struct fuse_opt_context *ctx, const char *arg)
|
||||
{
|
||||
return fuse_opt_add_arg(&ctx->outargs, arg);
|
||||
}
|
||||
|
||||
int fuse_opt_add_opt(char **opts, const char *opt)
|
||||
{
|
||||
char *newopts;
|
||||
if (!*opts)
|
||||
newopts = strdup(opt);
|
||||
else {
|
||||
unsigned oldlen = strlen(*opts);
|
||||
newopts = realloc(*opts, oldlen + 1 + strlen(opt) + 1);
|
||||
if (newopts) {
|
||||
newopts[oldlen] = ',';
|
||||
strcpy(newopts + oldlen + 1, opt);
|
||||
}
|
||||
}
|
||||
if (!newopts)
|
||||
return alloc_failed();
|
||||
|
||||
*opts = newopts;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_opt(struct fuse_opt_context *ctx, const char *opt)
|
||||
{
|
||||
return fuse_opt_add_opt(&ctx->opts, opt);
|
||||
}
|
||||
|
||||
static int call_proc(struct fuse_opt_context *ctx, const char *arg, int key,
|
||||
int iso)
|
||||
{
|
||||
if (key == FUSE_OPT_KEY_DISCARD)
|
||||
return 0;
|
||||
|
||||
if (key != FUSE_OPT_KEY_KEEP && ctx->proc) {
|
||||
int res = ctx->proc(ctx->data, arg, key, &ctx->outargs);
|
||||
if (res == -1 || !res)
|
||||
return res;
|
||||
}
|
||||
if (iso)
|
||||
return add_opt(ctx, arg);
|
||||
else
|
||||
return add_arg(ctx, arg);
|
||||
}
|
||||
|
||||
static int match_template(const char *t, const char *arg, unsigned *sepp)
|
||||
{
|
||||
int arglen = strlen(arg);
|
||||
const char *sep = strchr(t, '=');
|
||||
sep = sep ? sep : strchr(t, ' ');
|
||||
if (sep && (!sep[1] || sep[1] == '%')) {
|
||||
int tlen = sep - t;
|
||||
if (sep[0] == '=')
|
||||
tlen ++;
|
||||
if (arglen >= tlen && strncmp(arg, t, tlen) == 0) {
|
||||
*sepp = sep - t;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
if (strcmp(t, arg) == 0) {
|
||||
*sepp = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fuse_opt *find_opt(const struct fuse_opt *opt,
|
||||
const char *arg, unsigned *sepp)
|
||||
{
|
||||
for (; opt && opt->templ; opt++)
|
||||
if (match_template(opt->templ, arg, sepp))
|
||||
return opt;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int fuse_opt_match(const struct fuse_opt *opts, const char *opt)
|
||||
{
|
||||
unsigned dummy;
|
||||
return find_opt(opts, opt, &dummy) ? 1 : 0;
|
||||
}
|
||||
|
||||
static int process_opt_param(void *var, const char *format, const char *param,
|
||||
const char *arg)
|
||||
{
|
||||
assert(format[0] == '%');
|
||||
if (format[1] == 's') {
|
||||
char *copy = strdup(param);
|
||||
if (!copy)
|
||||
return alloc_failed();
|
||||
|
||||
*(char **) var = copy;
|
||||
} else {
|
||||
if (sscanf(param, format, var) != 1) {
|
||||
fprintf(stderr, "fuse: invalid parameter in option `%s'\n", arg);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_opt(struct fuse_opt_context *ctx,
|
||||
const struct fuse_opt *opt, unsigned sep,
|
||||
const char *arg, int iso)
|
||||
{
|
||||
if (opt->offset == -1U) {
|
||||
if (call_proc(ctx, arg, opt->value, iso) == -1)
|
||||
return -1;
|
||||
} else {
|
||||
void *var = ctx->data + opt->offset;
|
||||
if (sep && opt->templ[sep + 1]) {
|
||||
const char *param = arg + sep;
|
||||
if (opt->templ[sep] == '=')
|
||||
param ++;
|
||||
if (process_opt_param(var, opt->templ + sep + 1,
|
||||
param, arg) == -1)
|
||||
return -1;
|
||||
} else
|
||||
*(int *)var = opt->value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_opt_sep_arg(struct fuse_opt_context *ctx,
|
||||
const struct fuse_opt *opt, unsigned sep,
|
||||
const char *arg, int iso)
|
||||
{
|
||||
int res;
|
||||
char *newarg;
|
||||
char *param;
|
||||
|
||||
if (next_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
|
||||
param = ctx->argv[ctx->argctr];
|
||||
newarg = malloc(sep + strlen(param) + 1);
|
||||
if (!newarg)
|
||||
return alloc_failed();
|
||||
|
||||
memcpy(newarg, arg, sep);
|
||||
strcpy(newarg + sep, param);
|
||||
res = process_opt(ctx, opt, sep, newarg, iso);
|
||||
free(newarg);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int process_gopt(struct fuse_opt_context *ctx, const char *arg, int iso)
|
||||
{
|
||||
unsigned sep;
|
||||
const struct fuse_opt *opt = find_opt(ctx->opt, arg, &sep);
|
||||
if (opt) {
|
||||
for (; opt; opt = find_opt(opt + 1, arg, &sep)) {
|
||||
int res;
|
||||
if (sep && opt->templ[sep] == ' ' && !arg[sep])
|
||||
res = process_opt_sep_arg(ctx, opt, sep, arg, iso);
|
||||
else
|
||||
res = process_opt(ctx, opt, sep, arg, iso);
|
||||
if (res == -1)
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
} else
|
||||
return call_proc(ctx, arg, FUSE_OPT_KEY_OPT, iso);
|
||||
}
|
||||
|
||||
static int process_real_option_group(struct fuse_opt_context *ctx, char *opts)
|
||||
{
|
||||
char *sep;
|
||||
|
||||
do {
|
||||
int res;
|
||||
sep = strchr(opts, ',');
|
||||
if (sep)
|
||||
*sep = '\0';
|
||||
res = process_gopt(ctx, opts, 1);
|
||||
if (res == -1)
|
||||
return -1;
|
||||
opts = sep + 1;
|
||||
} while (sep);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_option_group(struct fuse_opt_context *ctx, const char *opts)
|
||||
{
|
||||
int res;
|
||||
char *copy;
|
||||
const char *sep = strchr(opts, ',');
|
||||
if (!sep)
|
||||
return process_gopt(ctx, opts, 1);
|
||||
|
||||
copy = strdup(opts);
|
||||
if (!copy) {
|
||||
fprintf(stderr, "fuse: memory allocation failed\n");
|
||||
return -1;
|
||||
}
|
||||
res = process_real_option_group(ctx, copy);
|
||||
free(copy);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int process_one(struct fuse_opt_context *ctx, const char *arg)
|
||||
{
|
||||
if (ctx->nonopt || arg[0] != '-')
|
||||
return call_proc(ctx, arg, FUSE_OPT_KEY_NONOPT, 0);
|
||||
else if (arg[1] == 'o') {
|
||||
if (arg[2])
|
||||
return process_option_group(ctx, arg + 2);
|
||||
else {
|
||||
if (next_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
|
||||
return process_option_group(ctx, ctx->argv[ctx->argctr]);
|
||||
}
|
||||
} else if (arg[1] == '-' && !arg[2]) {
|
||||
if (add_arg(ctx, arg) == -1)
|
||||
return -1;
|
||||
ctx->nonopt = ctx->outargs.argc;
|
||||
return 0;
|
||||
} else
|
||||
return process_gopt(ctx, arg, 0);
|
||||
}
|
||||
|
||||
static int opt_parse(struct fuse_opt_context *ctx)
|
||||
{
|
||||
if (ctx->argc) {
|
||||
if (add_arg(ctx, ctx->argv[0]) == -1)
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (ctx->argctr = 1; ctx->argctr < ctx->argc; ctx->argctr++)
|
||||
if (process_one(ctx, ctx->argv[ctx->argctr]) == -1)
|
||||
return -1;
|
||||
|
||||
if (ctx->opts) {
|
||||
if (fuse_opt_insert_arg(&ctx->outargs, 1, "-o") == -1 ||
|
||||
fuse_opt_insert_arg(&ctx->outargs, 2, ctx->opts) == -1)
|
||||
return -1;
|
||||
}
|
||||
if (ctx->nonopt && ctx->nonopt == ctx->outargs.argc) {
|
||||
free(ctx->outargs.argv[ctx->outargs.argc - 1]);
|
||||
ctx->outargs.argv[--ctx->outargs.argc] = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int fuse_opt_parse(struct fuse_args *args, void *data,
|
||||
const struct fuse_opt opts[], fuse_opt_proc_t proc)
|
||||
{
|
||||
int res;
|
||||
struct fuse_opt_context ctx = {
|
||||
.data = data,
|
||||
.opt = opts,
|
||||
.proc = proc,
|
||||
};
|
||||
|
||||
if (!args || !args->argv || !args->argc)
|
||||
return 0;
|
||||
|
||||
ctx.argc = args->argc;
|
||||
ctx.argv = args->argv;
|
||||
|
||||
res = opt_parse(&ctx);
|
||||
if (res != -1) {
|
||||
struct fuse_args tmp = *args;
|
||||
*args = ctx.outargs;
|
||||
ctx.outargs = tmp;
|
||||
}
|
||||
free(ctx.opts);
|
||||
fuse_opt_free_args(&ctx.outargs);
|
||||
return res;
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
/*
|
||||
FUSE: Filesystem in Userspace
|
||||
Copyright (C) 2001-2006 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
#ifndef _FUSE_OPT_H_
|
||||
#define _FUSE_OPT_H_
|
||||
|
||||
/* This file defines the option parsing interface of FUSE */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Option description
|
||||
*
|
||||
* This structure describes a single option, and and action associated
|
||||
* with it, in case it matches.
|
||||
*
|
||||
* More than one such match may occur, in which case the action for
|
||||
* each match is executed.
|
||||
*
|
||||
* There are three possible actions in case of a match:
|
||||
*
|
||||
* i) An integer (int or unsigned) variable determined by 'offset' is
|
||||
* set to 'value'
|
||||
*
|
||||
* ii) The processing function is called, with 'value' as the key
|
||||
*
|
||||
* iii) An integer (any) or string (char *) variable determined by
|
||||
* 'offset' is set to the value of an option parameter
|
||||
*
|
||||
* 'offset' should normally be either set to
|
||||
*
|
||||
* - 'offsetof(struct foo, member)' actions i) and iii)
|
||||
*
|
||||
* - -1 action ii)
|
||||
*
|
||||
* The 'offsetof()' macro is defined in the <stddef.h> header.
|
||||
*
|
||||
* The template determines which options match, and also have an
|
||||
* effect on the action. Normally the action is either i) or ii), but
|
||||
* if a format is present in the template, then action iii) is
|
||||
* performed.
|
||||
*
|
||||
* The types of templates are:
|
||||
*
|
||||
* 1) "-x", "-foo", "--foo", "--foo-bar", etc. These match only
|
||||
* themselves. Invalid values are "--" and anything beginning
|
||||
* with "-o"
|
||||
*
|
||||
* 2) "foo", "foo-bar", etc. These match "-ofoo", "-ofoo-bar" or
|
||||
* the relevant option in a comma separated option list
|
||||
*
|
||||
* 3) "bar=", "--foo=", etc. These are variations of 1) and 2)
|
||||
* which have a parameter
|
||||
*
|
||||
* 4) "bar=%s", "--foo=%lu", etc. Same matching as above but perform
|
||||
* action iii).
|
||||
*
|
||||
* 5) "-x ", etc. Matches either "-xparam" or "-x param" as
|
||||
* two separate arguments
|
||||
*
|
||||
* 6) "-x %s", etc. Combination of 4) and 5)
|
||||
*
|
||||
* If the format is "%s", memory is allocated for the string unlike
|
||||
* with scanf().
|
||||
*/
|
||||
struct fuse_opt {
|
||||
/** Matching template and optional parameter formatting */
|
||||
const char *templ;
|
||||
|
||||
/**
|
||||
* Offset of variable within 'data' parameter of fuse_opt_parse()
|
||||
* or -1
|
||||
*/
|
||||
unsigned long offset;
|
||||
|
||||
/**
|
||||
* Value to set the variable to, or to be passed as 'key' to the
|
||||
* processing function. Ignored if template has a format
|
||||
*/
|
||||
int value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Key option. In case of a match, the processing function will be
|
||||
* called with the specified key.
|
||||
*/
|
||||
#define FUSE_OPT_KEY(templ, key) { templ, -1U, key }
|
||||
|
||||
/**
|
||||
* Last option. An array of 'struct fuse_opt' must end with a NULL
|
||||
* template value
|
||||
*/
|
||||
#define FUSE_OPT_END { .templ = NULL }
|
||||
|
||||
/**
|
||||
* Argument list
|
||||
*/
|
||||
struct fuse_args {
|
||||
/** Argument count */
|
||||
int argc;
|
||||
|
||||
/** Argument vector. NULL terminated */
|
||||
char **argv;
|
||||
|
||||
/** Is 'argv' allocated? */
|
||||
int allocated;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializer for 'struct fuse_args'
|
||||
*/
|
||||
#define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }
|
||||
|
||||
/**
|
||||
* Key value passed to the processing function if an option did not
|
||||
* match any template
|
||||
*/
|
||||
#define FUSE_OPT_KEY_OPT -1
|
||||
|
||||
/**
|
||||
* Key value passed to the processing function for all non-options
|
||||
*
|
||||
* Non-options are the arguments beginning with a charater other than
|
||||
* '-' or all arguments after the special '--' option
|
||||
*/
|
||||
#define FUSE_OPT_KEY_NONOPT -2
|
||||
|
||||
/**
|
||||
* Special key value for options to keep
|
||||
*
|
||||
* Argument is not passed to processing function, but behave as if the
|
||||
* processing function returned 1
|
||||
*/
|
||||
#define FUSE_OPT_KEY_KEEP -3
|
||||
|
||||
/**
|
||||
* Special key value for options to discard
|
||||
*
|
||||
* Argument is not passed to processing function, but behave as if the
|
||||
* processing function returned zero
|
||||
*/
|
||||
#define FUSE_OPT_KEY_DISCARD -4
|
||||
|
||||
/**
|
||||
* Processing function
|
||||
*
|
||||
* This function is called if
|
||||
* - option did not match any 'struct fuse_opt'
|
||||
* - argument is a non-option
|
||||
* - option did match and offset was set to -1
|
||||
*
|
||||
* The 'arg' parameter will always contain the whole argument or
|
||||
* option including the parameter if exists. A two-argument option
|
||||
* ("-x foo") is always converted to single arguemnt option of the
|
||||
* form "-xfoo" before this function is called.
|
||||
*
|
||||
* Options of the form '-ofoo' are passed to this function without the
|
||||
* '-o' prefix.
|
||||
*
|
||||
* The return value of this function determines whether this argument
|
||||
* is to be inserted into the output argument vector, or discarded.
|
||||
*
|
||||
* @param data is the user data passed to the fuse_opt_parse() function
|
||||
* @param arg is the whole argument or option
|
||||
* @param key determines why the processing function was called
|
||||
* @param outargs the current output argument list
|
||||
* @return -1 on error, 0 if arg is to be discarded, 1 if arg should be kept
|
||||
*/
|
||||
typedef int (*fuse_opt_proc_t)(void *data, const char *arg, int key,
|
||||
struct fuse_args *outargs);
|
||||
|
||||
/**
|
||||
* Option parsing function
|
||||
*
|
||||
* If 'args' was returned from a previous call to fuse_opt_parse() or
|
||||
* it was constructed from
|
||||
*
|
||||
* A NULL 'args' is equivalent to an empty argument vector
|
||||
*
|
||||
* A NULL 'opts' is equivalent to an 'opts' array containing a single
|
||||
* end marker
|
||||
*
|
||||
* A NULL 'proc' is equivalent to a processing function always
|
||||
* returning '1'
|
||||
*
|
||||
* @param args is the input and output argument list
|
||||
* @param data is the user data
|
||||
* @param opts is the option description array
|
||||
* @param proc is the processing function
|
||||
* @return -1 on error, 0 on success
|
||||
*/
|
||||
int fuse_opt_parse(struct fuse_args *args, void *data,
|
||||
const struct fuse_opt opts[], fuse_opt_proc_t proc);
|
||||
|
||||
/**
|
||||
* Add an option to a comma separated option list
|
||||
*
|
||||
* @param opts is a pointer to an option list, may point to a NULL value
|
||||
* @param opt is the option to add
|
||||
* @return -1 on allocation error, 0 on success
|
||||
*/
|
||||
int fuse_opt_add_opt(char **opts, const char *opt);
|
||||
|
||||
/**
|
||||
* Add an argument to a NULL terminated argument vector
|
||||
*
|
||||
* @param args is the structure containing the current argument list
|
||||
* @param arg is the new argument to add
|
||||
* @return -1 on allocation error, 0 on success
|
||||
*/
|
||||
int fuse_opt_add_arg(struct fuse_args *args, const char *arg);
|
||||
|
||||
/**
|
||||
* Add an argument at the specified position in a NULL terminated
|
||||
* argument vector
|
||||
*
|
||||
* Adds the argument to the N-th position. This is useful for adding
|
||||
* options at the beggining of the array which must not come after the
|
||||
* special '--' option.
|
||||
*
|
||||
* @param args is the structure containing the current argument list
|
||||
* @param pos is the position at which to add the argument
|
||||
* @param arg is the new argument to add
|
||||
* @return -1 on allocation error, 0 on success
|
||||
*/
|
||||
int fuse_opt_insert_arg(struct fuse_args *args, int pos, const char *arg);
|
||||
|
||||
/**
|
||||
* Free the contents of argument list
|
||||
*
|
||||
* The structure itself is not freed
|
||||
*
|
||||
* @param args is the structure containing the argument list
|
||||
*/
|
||||
void fuse_opt_free_args(struct fuse_args *args);
|
||||
|
||||
|
||||
/**
|
||||
* Check if an option matches
|
||||
*
|
||||
* @param opts is the option description array
|
||||
* @param opt is the option to match
|
||||
* @return 1 if a match is found, 0 if not
|
||||
*/
|
||||
int fuse_opt_match(const struct fuse_opt opts[], const char *opt);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _FUSE_OPT_H_ */
|
||||
+14
-3
@@ -1,12 +1,23 @@
|
||||
AC_INIT(sshfs-fuse, 1.1)
|
||||
AC_INIT(sshfs-fuse, 1.5)
|
||||
AM_INIT_AUTOMAKE
|
||||
AM_CONFIG_HEADER(config.h)
|
||||
|
||||
AC_PROG_CC
|
||||
CFLAGS="$CFLAGS -Wall -W"
|
||||
LIBS=
|
||||
AC_SEARCH_LIBS(dlsym, [dl])
|
||||
sshnodelay_libs=$LIBS
|
||||
AC_SUBST(sshnodelay_libs)
|
||||
LIBS=
|
||||
|
||||
export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig:$PKG_CONFIG_PATH
|
||||
PKG_CHECK_MODULES(SSHFS, [fuse >= 2.2 glib-2.0])
|
||||
CFLAGS="$CFLAGS -Wall -W -D_REENTRANT $SSHFS_CFLAGS"
|
||||
LIBS="$SSHFS_LIBS"
|
||||
have_fuse_opt_parse=no
|
||||
AC_CHECK_FUNC([fuse_opt_parse], [have_fuse_opt_parse=yes])
|
||||
if test "$have_fuse_opt_parse" = no; then
|
||||
CFLAGS="$CFLAGS -Icompat"
|
||||
fi
|
||||
AM_CONDITIONAL(FUSE_OPT_COMPAT, test "$have_fuse_opt_parse" = no)
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
/*
|
||||
Mount option parsing
|
||||
Copyright (C) 2004 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
#include "opts.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <glib.h>
|
||||
|
||||
static int process_option(char *arg, struct opt opts[], int case_sensitive)
|
||||
{
|
||||
int i;
|
||||
char *eq = strchr(arg, '=');
|
||||
if (eq)
|
||||
*eq = '\0';
|
||||
|
||||
for (i = 0; opts[i].optname != NULL; i++) {
|
||||
if (case_sensitive) {
|
||||
if (strcmp(opts[i].optname, arg) == 0)
|
||||
break;
|
||||
} else if (strcasecmp(opts[i].optname, arg) == 0)
|
||||
break;
|
||||
}
|
||||
if (opts[i].optname == NULL) {
|
||||
if (eq)
|
||||
*eq = '=';
|
||||
return 0;
|
||||
}
|
||||
opts[i].present = 1;
|
||||
if (eq) {
|
||||
if (opts[i].value)
|
||||
g_free(opts[i].value);
|
||||
opts[i].value = g_strdup(eq+1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int process_option_group(char *arg, struct opt opts[],
|
||||
int case_sensitive)
|
||||
{
|
||||
int remove = 1;
|
||||
char *prevcomma = NULL;
|
||||
while (1) {
|
||||
int remove_one;
|
||||
char *comma = strchr(arg, ',');
|
||||
if (comma)
|
||||
*comma = '\0';
|
||||
remove_one = process_option(arg, opts, case_sensitive);
|
||||
if (remove_one) {
|
||||
if (comma)
|
||||
memmove(arg, comma + 1, strlen(comma + 1) + 1);
|
||||
} else {
|
||||
remove = 0;
|
||||
if (comma)
|
||||
arg = comma + 1;
|
||||
}
|
||||
if (!remove_one && prevcomma)
|
||||
*prevcomma = ',';
|
||||
if (!comma)
|
||||
break;
|
||||
prevcomma = comma;
|
||||
}
|
||||
return remove;
|
||||
}
|
||||
|
||||
void process_options(int *argcp, char *argv[], struct opt opts[],
|
||||
int case_sensitive)
|
||||
{
|
||||
int argctr;
|
||||
int newargctr;
|
||||
|
||||
for (argctr = 1, newargctr = 1; argctr < *argcp; argctr++) {
|
||||
char *arg = argv[argctr];
|
||||
int removed = 0;
|
||||
if (arg[0] == '-' && arg[1] == 'o') {
|
||||
if (arg[2])
|
||||
removed = process_option_group(arg+2, opts, case_sensitive);
|
||||
else {
|
||||
if (argctr + 1 < *argcp) {
|
||||
argctr++;
|
||||
arg = argv[argctr];
|
||||
removed = process_option_group(arg, opts, case_sensitive);
|
||||
if (removed)
|
||||
g_free(argv[argctr-1]);
|
||||
else if (argctr != newargctr)
|
||||
argv[newargctr++] = argv[argctr-1];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (removed)
|
||||
g_free(arg);
|
||||
else {
|
||||
if(argctr != newargctr)
|
||||
argv[newargctr] = arg;
|
||||
newargctr++;
|
||||
}
|
||||
}
|
||||
*argcp = newargctr;
|
||||
}
|
||||
|
||||
int opt_get_unsigned(const struct opt *o, unsigned *valp)
|
||||
{
|
||||
char *end;
|
||||
unsigned val;
|
||||
if (!o->value || !o->value[0]) {
|
||||
fprintf(stderr, "Missing value for '%s' option\n", o->optname);
|
||||
return -1;
|
||||
}
|
||||
val = strtoul(o->value, &end, 0);
|
||||
if (end[0]) {
|
||||
fprintf(stderr, "Invalid value for '%s' option\n", o->optname);
|
||||
return -1;
|
||||
}
|
||||
*valp = val;
|
||||
return 0;
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
Mount option parsing
|
||||
Copyright (C) 2004 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
This program can be distributed under the terms of the GNU GPL.
|
||||
See the file COPYING.
|
||||
*/
|
||||
|
||||
struct opt {
|
||||
const char *optname;
|
||||
int present;
|
||||
char *value;
|
||||
};
|
||||
|
||||
void process_options(int *argcp, char *argv[], struct opt opts[],
|
||||
int case_sensitive);
|
||||
|
||||
int opt_get_unsigned(const struct opt *o, unsigned *valp);
|
||||
@@ -0,0 +1,18 @@
|
||||
#define _GNU_SOURCE
|
||||
#include <dlfcn.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
int connect(int sock, const struct sockaddr *addr, socklen_t addrlen)
|
||||
{
|
||||
int (*next_connect)(int, const struct sockaddr *, socklen_t) =
|
||||
dlsym(RTLD_NEXT, "connect");
|
||||
int res = next_connect(sock, addr, addrlen);
|
||||
if (!res && addr->sa_family == AF_INET) {
|
||||
int opt = 1;
|
||||
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
Reference in New Issue
Block a user