Compare commits

..

27 Commits

Author SHA1 Message Date
Miklos Szeredi b3aac4e035 whitespace fix 2005-03-04 11:22:31 +00:00
Miklos Szeredi 330c126684 fix 2005-03-04 11:19:41 +00:00
Miklos Szeredi 3cdf0a547a fix 2005-03-03 12:59:37 +00:00
Miklos Szeredi e233f82239 documentation fix 2005-02-27 19:39:04 +00:00
Miklos Szeredi 9693a8bf05 fix 2005-02-24 16:37:54 +00:00
Miklos Szeredi 80748b39e4 fix 2005-02-16 14:00:59 +00:00
Miklos Szeredi 8e303b9a01 fix 2005-02-14 17:22:08 +00:00
Miklos Szeredi 838fed1a3a fix 2005-02-09 15:12:51 +00:00
Miklos Szeredi 99acbfff7a fix 2005-02-09 13:45:11 +00:00
Miklos Szeredi 2705253d2c fix 2005-02-09 13:16:36 +00:00
Miklos Szeredi b318855315 fix 2005-02-09 09:46:04 +00:00
Miklos Szeredi da058cf9da fix 2005-02-08 16:47:54 +00:00
Miklos Szeredi a464d3ec2c option parsing 2005-02-08 16:15:58 +00:00
Miklos Szeredi d5696036a5 caching improvements 2005-02-08 11:15:46 +00:00
Miklos Szeredi 551e32210b *** empty log message *** 2005-02-07 17:09:27 +00:00
Miklos Szeredi 240b5f7bb5 fix 2005-02-07 17:08:27 +00:00
Miklos Szeredi 39b1a10bdb directory caching 2005-02-07 16:58:43 +00:00
Miklos Szeredi 386a7f67ab cleanup 2005-02-07 16:12:46 +00:00
Miklos Szeredi cdc0da7956 fix 2005-02-04 10:19:07 +00:00
Miklos Szeredi 733b3b1f4a *** empty log message *** 2005-02-04 10:15:46 +00:00
Miklos Szeredi 88ab18b8f8 release 1.0 2005-01-09 19:48:14 +00:00
Miklos Szeredi 716bfbf697 fix 2004-12-05 21:26:16 +00:00
Miklos Szeredi 7fb3a02ec5 *** empty log message *** 2004-12-04 21:20:50 +00:00
Miklos Szeredi b3483b6bb9 fix 2004-12-04 12:40:21 +00:00
Miklos Szeredi 6f3b9bfdd1 automakeify 2004-12-04 12:29:44 +00:00
Miklos Szeredi c73761963b fix 2004-11-29 19:13:15 +00:00
Miklos Szeredi 5dc1d26e70 optimizations 2004-11-26 12:17:39 +00:00
13 changed files with 1784 additions and 173 deletions
+16
View File
@@ -0,0 +1,16 @@
.deps
COPYING
INSTALL
Makefile
Makefile.in
aclocal.m4
*.cache
config.*
configure
compile
depcomp
install-sh
missing
sshfs
stamp*
.deps
+1
View File
@@ -0,0 +1 @@
Miklos Szeredi <miklos@szeredi.hu>
+56
View File
@@ -0,0 +1,56 @@
2005-03-03 Miklos Szeredi <miklos@szeredi.hu>
* Slightly optimize readahead. Still not clever enough to always
keep the pipe filled.
* Add 'sshfs_debug' option
2005-02-17 Miklos Szeredi <miklos@szeredi.hu>
* Parse 'max_read' mount option and if smaller than 65536 forward
to FUSE
2005-02-16 Miklos Szeredi <miklos@szeredi.hu>
* Added simple readahead (big performance gain in case of
sequential read pattern). Can be disabled with '-o no_readahead'
2005-02-14 Miklos Szeredi <miklos@szeredi.hu>
* 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'
* Re-added '-p PORT' as a convenience option, also '-C' works as
in ssh.
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
2005-02-07 Miklos Szeredi <miklos@szeredi.hu>
* Separate attribute caching to a separate layer
* Add caching of directory contents
2005-02-03 Miklos Szeredi <miklos@szeredi.hu>
Fix PKG_CONFIG_PATH setting in configure.ac (reported by Alpar
Juttner)
2005-01-09 Miklos Szeredi <miklos@szeredi.hu>
* Released 1.0
2004-12-04 Miklos Szeredi <miklos@szeredi.hu>
* Started ChangeLog
-21
View File
@@ -1,21 +0,0 @@
CC = gcc
CFLAGS := -Wall -W -g
LDLIBS := -lpthread -ldl -rdynamic
PKGCONFIG := env PKG_CONFIG_PATH=/usr/local/lib/pkgconfig pkg-config
FUSEVER := $(shell $(PKGCONFIG) --modversion fuse 2> /dev/null)
ifeq ($(FUSEVER),)
LDLIBS += -lfuse
else
CFLAGS += $(shell $(PKGCONFIG) --cflags fuse)
LDLIBS += $(shell $(PKGCONFIG) --libs fuse)
endif
CPPFLAGS := -D_FILE_OFFSET_BITS=64
#CPPFLAGS += -DDEBUG
sshfs: sshfs.o
clean:
rm -f *.o sshfs
+5
View File
@@ -0,0 +1,5 @@
## 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
+19
View File
@@ -0,0 +1,19 @@
What is new in 1.1
------------------
* Performance improvements:
- directory content caching
- symlink caching
- asynchronous writeback
- readahead
* Fixed '-p' option
What is new in 1.0
------------------
* Initial release
+71
View File
@@ -0,0 +1,71 @@
Abstract
========
This is a filesystem client based on the SSH File Transfer Protocol.
Since most SSH servers already support this protocol it is very easy
to set up: i.e. on the server side there's nothing to do. On the
client side mounting the filesystem is as easy as logging into the
server with ssh.
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 ;)
- Multithreading: more than one request can be on it's way to the
server
- Allowing large reads (max 64k)
- Caching directory contents
How to mount a filesystem
=========================
Once sshfs is installed (see next section) running it is very simple:
sshfs hostname: mountpoint
Note, that it's recommended to run it as user, not as root. For this
to work the mountpoint must be owned by the user. If the username is
different on the host you are connecting to, then use the
"username@host:" form. If you need to enter a password sshfs will ask
for it (actually it just runs ssh which ask for the password if
needed). You can also specify a directory after the ":". The default
is the home directory.
Also many ssh options can be specified (see the manual pages for
sftp(1) and ssh_config(5)), including the remote port number
('-oport=PORT')
To unmount the filesystem:
fusermount -u moutpoint
Installing
==========
First you need to download FUSE 2.2 or later from:
http://fuse.sourceforge.net
You also need to install the devel package for glib2.0. After
installing FUSE, compile sshfs the usual way:
./configure
make
make install (as root)
And you are ready to go.
Bugs and feature requests
=========================
Send bug reports to <miklos@szeredi.hu>.
Good luck!
Miklos Szeredi
+526
View File
@@ -0,0 +1,526 @@
/*
Caching file system proxy
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 "cache.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#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 node {
struct stat stat;
time_t stat_valid;
char **dir;
time_t dir_valid;
char *link;
time_t link_valid;
time_t valid;
};
struct fuse_cache_dirhandle {
const char *path;
fuse_dirh_t h;
fuse_dirfil_t filler;
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_;
g_strfreev(node->dir);
g_free(node);
}
static int cache_clean_entry(void *key_, struct node *node, time_t *now)
{
(void) key_;
if (*now > node->valid)
return TRUE;
else
return FALSE;
}
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;
}
}
static struct node *cache_lookup(const char *path)
{
return (struct node *) g_hash_table_lookup(cache, path);
}
static void cache_purge(const char *path)
{
g_hash_table_remove(cache, path);
}
static void cache_purge_parent(const char *path)
{
const char *s = strrchr(path, '/');
if (s) {
if (s == path)
g_hash_table_remove(cache, "/");
else {
char *parent = g_strndup(path, s - path);
cache_purge(parent);
g_free(parent);
}
}
}
static void cache_invalidate(const char *path)
{
pthread_mutex_lock(&cache_lock);
cache_purge(path);
pthread_mutex_unlock(&cache_lock);
}
static void cache_invalidate_dir(const char *path)
{
pthread_mutex_lock(&cache_lock);
cache_purge(path);
cache_purge_parent(path);
pthread_mutex_unlock(&cache_lock);
}
static void cache_do_rename(const char *from, const char *to)
{
pthread_mutex_lock(&cache_lock);
cache_purge(from);
cache_purge(to);
cache_purge_parent(from);
cache_purge_parent(to);
pthread_mutex_unlock(&cache_lock);
}
static struct node *cache_get(const char *path)
{
struct node *node = cache_lookup(path);
if (node == NULL) {
char *pathcopy = g_strdup(path);
node = g_new0(struct node, 1);
g_hash_table_insert(cache, pathcopy, node);
}
return node;
}
static 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);
node->stat = *stbuf;
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);
}
static 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->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);
}
static size_t my_strnlen(const char *s, size_t maxsize)
{
const char *p;
for (p = s; maxsize && *p; maxsize--, p++);
return p - s;
}
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);
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;
if (node->link_valid > node->valid)
node->valid = node->link_valid;
cache_clean();
pthread_mutex_unlock(&cache_lock);
}
static int cache_getattr(const char *path, struct stat *stbuf)
{
struct node *node;
int err;
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;
}
}
pthread_mutex_unlock(&cache_lock);
err = next_oper->oper.getattr(path, stbuf);
if (!err)
cache_add_attr(path, stbuf);
return err;
}
static int cache_readlink(const char *path, char *buf, size_t size)
{
struct node *node;
int err;
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);
return 0;
}
}
pthread_mutex_unlock(&cache_lock);
err = next_oper->oper.readlink(path, buf, size);
if (!err)
cache_add_link(path, buf, 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) {
g_ptr_array_add(ch->dir, g_strdup(name));
char *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 = 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 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);
if (!err)
cache_invalidate_dir(path);
return err;
}
static int cache_mkdir(const char *path, mode_t mode)
{
int err = next_oper->oper.mkdir(path, mode);
if (!err)
cache_invalidate_dir(path);
return err;
}
static int cache_unlink(const char *path)
{
int err = next_oper->oper.unlink(path);
if (!err)
cache_invalidate_dir(path);
return err;
}
static int cache_rmdir(const char *path)
{
int err = next_oper->oper.rmdir(path);
if (!err)
cache_invalidate_dir(path);
return err;
}
static int cache_symlink(const char *from, const char *to)
{
int err = next_oper->oper.symlink(from, to);
if (!err)
cache_invalidate_dir(to);
return err;
}
static int cache_rename(const char *from, const char *to)
{
int err = next_oper->oper.rename(from, to);
if (!err)
cache_do_rename(from, to);
return err;
}
static int cache_link(const char *from, const char *to)
{
int err = next_oper->oper.link(from, to);
if (!err) {
cache_invalidate(from);
cache_invalidate_dir(to);
}
return err;
}
static int cache_chmod(const char *path, mode_t mode)
{
int err = next_oper->oper.chmod(path, mode);
if (!err)
cache_invalidate(path);
return err;
}
static int cache_chown(const char *path, uid_t uid, gid_t gid)
{
int err = next_oper->oper.chown(path, uid, gid);
if (!err)
cache_invalidate(path);
return err;
}
static int cache_truncate(const char *path, off_t size)
{
int err = next_oper->oper.truncate(path, size);
if (!err)
cache_invalidate(path);
return err;
}
static int cache_utime(const char *path, struct utimbuf *buf)
{
int err = next_oper->oper.utime(path, buf);
if (!err)
cache_invalidate(path);
return err;
}
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);
if (res >= 0)
cache_invalidate(path);
return res;
}
static void cache_unity_fill(struct fuse_cache_operations *oper,
struct fuse_operations *cache_oper)
{
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;
cache_oper->unlink = oper->oper.unlink;
cache_oper->rmdir = oper->oper.rmdir;
cache_oper->rename = oper->oper.rename;
cache_oper->link = oper->oper.link;
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->open = oper->oper.open;
cache_oper->read = oper->oper.read;
cache_oper->write = oper->oper.write;
cache_oper->flush = oper->oper.flush;
cache_oper->release = oper->oper.release;
cache_oper->fsync = oper->oper.fsync;
cache_oper->statfs = oper->oper.statfs;
cache_oper->setxattr = oper->oper.setxattr;
cache_oper->getxattr = oper->oper.getxattr;
cache_oper->listxattr = oper->oper.listxattr;
cache_oper->removexattr = oper->oper.removexattr;
}
struct fuse_operations *cache_init(struct fuse_cache_operations *oper)
{
static struct fuse_operations cache_oper;
next_oper = oper;
cache_unity_fill(oper, &cache_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;
cache_oper.unlink = oper->oper.unlink ? cache_unlink : NULL;
cache_oper.rmdir = oper->oper.rmdir ? cache_rmdir : NULL;
cache_oper.rename = oper->oper.rename ? cache_rename : NULL;
cache_oper.link = oper->oper.link ? cache_link : NULL;
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.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) {
fprintf(stderr, "failed to create cache\n");
return NULL;
}
}
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 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)
{
struct opt *o = &cache_opts[sel];
if (!o->present)
return 0;
if (opt_get_unsigned(o, timeoutp) == -1)
return -1;
return 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;
}
+22
View File
@@ -0,0 +1,22 @@
/*
Caching file system proxy
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 <fuse.h>
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);
int cache_parse_options(int *argcp, char *argv[]);
+12
View File
@@ -0,0 +1,12 @@
AC_INIT(sshfs-fuse, 1.1)
AM_INIT_AUTOMAKE
AM_CONFIG_HEADER(config.h)
AC_PROG_CC
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"
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
+121
View File
@@ -0,0 +1,121 @@
/*
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;
}
+18
View File
@@ -0,0 +1,18 @@
/*
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);
+917 -152
View File
File diff suppressed because it is too large Load Diff