aboutsummaryrefslogtreecommitdiff
path: root/lib/fuse.c
diff options
context:
space:
mode:
authorGravatar Miklos Szeredi <miklos@szeredi.hu>2005-04-07 15:40:21 +0000
committerGravatar Miklos Szeredi <miklos@szeredi.hu>2005-04-07 15:40:21 +0000
commitab9745641373d35235b8c0d124f1c355908b575f (patch)
tree4ad92b37432ff7dcbcdd0ba57b85dfe00f89c505 /lib/fuse.c
parent670c78a3134f2233b018fa931e1a7e69e4ab11fd (diff)
fix
Diffstat (limited to 'lib/fuse.c')
-rw-r--r--lib/fuse.c500
1 files changed, 224 insertions, 276 deletions
diff --git a/lib/fuse.c b/lib/fuse.c
index 40230af..82adeb2 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -21,6 +21,7 @@
#include <assert.h>
#include <stdint.h>
#include <sys/param.h>
+#include <sys/uio.h>
/* FUSE flags: */
@@ -69,17 +70,15 @@ struct node {
};
struct fuse_dirhandle {
+ pthread_mutex_t lock;
struct fuse *fuse;
unsigned char *contents;
+ int allocated;
unsigned len;
+ unsigned needlen;
int filled;
unsigned long fh;
-};
-
-struct fuse_dirbuf {
- struct fuse *fuse;
- char *buf;
- unsigned len;
+ int error;
};
struct fuse_cmd {
@@ -90,6 +89,19 @@ struct fuse_cmd {
static struct fuse_context *(*fuse_getcontext)(void) = NULL;
+#ifndef USE_UCLIBC
+#define mutex_init(mut) pthread_mutex_init(mut, NULL)
+#else
+static void mutex_init(pthread_mutex_t mut)
+{
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
+ pthread_mutex_init(mut, &attr);
+ pthread_mutexattr_destroy(&attr);
+}
+#endif
+
static const char *opname(enum fuse_opcode opcode)
{
switch (opcode) {
@@ -453,103 +465,22 @@ static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
#endif
}
-static int fill_dir5(void *buf, const char *name, int type, ino_t ino,
- off_t off)
-{
- size_t namelen = strlen(name);
- size_t size;
- struct fuse_dirbuf *db = (struct fuse_dirbuf *) buf;
- struct fuse_dirent *dirent;
-
- if (db->len <= FUSE_NAME_OFFSET)
- return 1;
-
- dirent = (struct fuse_dirent *) db->buf;
- if ((db->fuse->flags & FUSE_USE_INO))
- dirent->ino = ino;
- else
- dirent->ino = (unsigned long) -1;
- dirent->off = off;
- dirent->namelen = namelen;
- dirent->type = type;
- size = FUSE_DIRENT_SIZE(dirent);
- if (db->len < size)
- return 1;
-
- strncpy(dirent->name, name, namelen);
- db->len -= size;
- db->buf += size;
- return db->len > FUSE_NAME_OFFSET ? 0 : 1;
-}
-
-static int fill_dir_compat5(struct fuse_dirhandle *dh, const char *name,
- int type, ino_t ino)
+static size_t iov_length(const struct iovec *iov, size_t count)
{
- size_t namelen = strlen(name);
- size_t entsize = sizeof(struct fuse_dirhandle) + namelen + 8;
- struct fuse_dirent_compat5 *dirent;
-
- if (namelen > FUSE_NAME_MAX)
- namelen = FUSE_NAME_MAX;
-
- dh->contents = realloc(dh->contents, dh->len + entsize);
- if (dh->contents == NULL)
- return -ENOMEM;
-
- dirent = (struct fuse_dirent_compat5 *) (dh->contents + dh->len);
- memset(dirent, 0, entsize);
- if ((dh->fuse->flags & FUSE_USE_INO))
- dirent->ino = ino;
- else
- dirent->ino = (unsigned long) -1;
- dirent->namelen = namelen;
- strncpy(dirent->name, name, namelen);
- dirent->type = type;
- dh->len += FUSE_DIRENT_SIZE_COMPAT5(dirent);
- return 0;
-}
-
-static int fill_dir_new(struct fuse_dirhandle *dh, const char *name, int type,
- ino_t ino)
-{
- size_t namelen = strlen(name);
- size_t entsize = sizeof(struct fuse_dirhandle) + namelen + 8;
- struct fuse_dirent *dirent;
-
- if (namelen > FUSE_NAME_MAX)
- namelen = FUSE_NAME_MAX;
-
- dh->contents = realloc(dh->contents, dh->len + entsize);
- if (dh->contents == NULL)
- return -ENOMEM;
-
- dirent = (struct fuse_dirent *) (dh->contents + dh->len);
- memset(dirent, 0, entsize);
- if ((dh->fuse->flags & FUSE_USE_INO))
- dirent->ino = ino;
- else
- dirent->ino = (unsigned long) -1;
- dirent->namelen = namelen;
- strncpy(dirent->name, name, namelen);
- dirent->type = type;
- dh->len += FUSE_DIRENT_SIZE(dirent);
- dirent->off = dh->len;
- return 0;
-}
+ size_t seg;
+ size_t ret = 0;
-static int fill_dir(struct fuse_dirhandle *dh, const char *name, int type,
- ino_t ino)
-{
- if (dh->fuse->major == 5)
- return fill_dir_compat5(dh, name, type, ino);
- else
- return fill_dir_new(dh, name, type, ino);
+ for (seg = 0; seg < count; seg++)
+ ret += iov[seg].iov_len;
+ return ret;
}
-static int send_reply_raw(struct fuse *f, char *outbuf, size_t outsize)
+static int send_reply_raw(struct fuse *f, const struct iovec iov[],
+ size_t count)
{
int res;
- struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
+ unsigned outsize = iov_length(iov, count);
+ struct fuse_out_header *out = (struct fuse_out_header *) iov[0].iov_base;
out->len = outsize;
if ((f->flags & FUSE_DEBUG)) {
@@ -563,7 +494,7 @@ static int send_reply_raw(struct fuse *f, char *outbuf, size_t outsize)
increased long after the operation is done */
fuse_inc_avail(f);
- res = write(f->fd, outbuf, outsize);
+ res = writev(f->fd, iov, count);
if (res == -1) {
/* ENOENT means the operation was interrupted */
if (!f->exited && errno != ENOENT)
@@ -576,37 +507,26 @@ static int send_reply_raw(struct fuse *f, char *outbuf, size_t outsize)
static int send_reply(struct fuse *f, struct fuse_in_header *in, int error,
void *arg, size_t argsize)
{
- int res;
- char *outbuf;
- size_t outsize;
- struct fuse_out_header *out;
+ struct fuse_out_header out;
+ struct iovec iov[2];
+ size_t count;
if (error <= -1000 || error > 0) {
fprintf(stderr, "fuse: bad error value: %i\n", error);
error = -ERANGE;
}
- if (error)
- argsize = 0;
-
- outsize = sizeof(struct fuse_out_header) + argsize;
- outbuf = (char *) malloc(outsize);
- if (outbuf == NULL) {
- fprintf(stderr, "fuse: failed to allocate reply buffer\n");
- res = -ENOMEM;
- } else {
- out = (struct fuse_out_header *) outbuf;
- memset(out, 0, sizeof(struct fuse_out_header));
- out->unique = in->unique;
- out->error = error;
- if (argsize != 0)
- memcpy(outbuf + sizeof(struct fuse_out_header), arg, argsize);
-
- res = send_reply_raw(f, outbuf, outsize);
- free(outbuf);
+ out.unique = in->unique;
+ out.error = error;
+ count = 1;
+ iov[0].iov_base = &out;
+ iov[0].iov_len = sizeof(struct fuse_out_header);
+ if (argsize && !error) {
+ count++;
+ iov[1].iov_base = arg;
+ iov[1].iov_len = argsize;
}
-
- return res;
+ return send_reply_raw(f, iov, count);
}
static int is_open(struct fuse *f, nodeid_t dir, const char *name)
@@ -887,23 +807,6 @@ static void do_readlink(struct fuse *f, struct fuse_in_header *in)
send_reply(f, in, res, link, res == 0 ? strlen(link) : 0);
}
-static int common_getdir(struct fuse *f, struct fuse_in_header *in,
- struct fuse_dirhandle *dh)
-{
- int res;
- char *path;
-
- res = -ENOENT;
- path = get_path(f, in->nodeid);
- if (path != NULL) {
- res = -ENOSYS;
- if (f->op.getdir)
- res = f->op.getdir(path, dh, fill_dir);
- free(path);
- }
- return res;
-}
-
static void do_mknod(struct fuse *f, struct fuse_in_header *in,
struct fuse_mknod_in *inarg)
{
@@ -1237,52 +1140,47 @@ static void do_read(struct fuse *f, struct fuse_in_header *in,
{
int res;
char *path;
- char *outbuf = (char *) malloc(sizeof(struct fuse_out_header) + arg->size);
- if (outbuf == NULL)
- send_reply(f, in, -ENOMEM, NULL, 0);
- else {
- struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
- char *buf = outbuf + sizeof(struct fuse_out_header);
- size_t size;
- size_t outsize;
- struct fuse_file_info fi;
+ size_t size;
+ char *buf;
+ struct fuse_file_info fi;
- memset(&fi, 0, sizeof(fi));
- fi.fh = arg->fh;
+ buf = (char *) malloc(arg->size);
+ if (buf == NULL) {
+ send_reply(f, in, -ENOMEM, NULL, 0);
+ return;
+ }
- res = -ENOENT;
- path = get_path(f, in->nodeid);
- if (path != NULL) {
- if (f->flags & FUSE_DEBUG) {
- printf("READ[%lu] %u bytes from %llu\n",
- (unsigned long) arg->fh, arg->size, arg->offset);
- fflush(stdout);
- }
+ memset(&fi, 0, sizeof(fi));
+ fi.fh = arg->fh;
- res = -ENOSYS;
- if (f->op.read)
- res = f->op.read(path, buf, arg->size, arg->offset, &fi);
- free(path);
+ res = -ENOENT;
+ path = get_path(f, in->nodeid);
+ if (path != NULL) {
+ if (f->flags & FUSE_DEBUG) {
+ printf("READ[%lu] %u bytes from %llu\n",
+ (unsigned long) arg->fh, arg->size, arg->offset);
+ fflush(stdout);
}
- size = 0;
- if (res >= 0) {
- size = res;
- res = 0;
- if (f->flags & FUSE_DEBUG) {
- printf(" READ[%lu] %u bytes\n", (unsigned long) arg->fh,
- size);
- fflush(stdout);
- }
- }
- memset(out, 0, sizeof(struct fuse_out_header));
- out->unique = in->unique;
- out->error = res;
- outsize = sizeof(struct fuse_out_header) + size;
+ res = -ENOSYS;
+ if (f->op.read)
+ res = f->op.read(path, buf, arg->size, arg->offset, &fi);
+ free(path);
+ }
- send_reply_raw(f, outbuf, outsize);
- free(outbuf);
+ size = 0;
+ if (res >= 0) {
+ size = res;
+ res = 0;
+ if (f->flags & FUSE_DEBUG) {
+ printf(" READ[%lu] %u bytes\n", (unsigned long) arg->fh,
+ size);
+ fflush(stdout);
+ }
}
+
+ send_reply(f, in, res, buf, size);
+ free(buf);
}
static void do_write(struct fuse *f, struct fuse_in_header *in,
@@ -1313,6 +1211,7 @@ static void do_write(struct fuse *f, struct fuse_in_header *in,
free(path);
}
+ memset(&outarg, 0, sizeof(outarg));
if (res >= 0) {
outarg.size = res;
res = 0;
@@ -1443,26 +1342,19 @@ static void do_getxattr_read(struct fuse *f, struct fuse_in_header *in,
const char *name, size_t size)
{
int res;
- char *outbuf = (char *) malloc(sizeof(struct fuse_out_header) + size);
- if (outbuf == NULL)
+ char *value = (char *) malloc(size);
+ if (value == NULL) {
send_reply(f, in, -ENOMEM, NULL, 0);
- else {
- struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
- char *value = outbuf + sizeof(struct fuse_out_header);
-
- res = common_getxattr(f, in, name, value, size);
- size = 0;
- if (res > 0) {
- size = res;
- res = 0;
- }
- memset(out, 0, sizeof(struct fuse_out_header));
- out->unique = in->unique;
- out->error = res;
-
- send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size);
- free(outbuf);
+ return;
}
+ res = common_getxattr(f, in, name, value, size);
+ size = 0;
+ if (res > 0) {
+ size = res;
+ res = 0;
+ }
+ send_reply(f, in, res, value, size);
+ free(value);
}
static void do_getxattr_size(struct fuse *f, struct fuse_in_header *in,
@@ -1471,6 +1363,7 @@ static void do_getxattr_size(struct fuse *f, struct fuse_in_header *in,
int res;
struct fuse_getxattr_out arg;
+ memset(&arg, 0, sizeof(arg));
res = common_getxattr(f, in, name, NULL, 0);
if (res >= 0) {
arg.size = res;
@@ -1511,26 +1404,19 @@ static void do_listxattr_read(struct fuse *f, struct fuse_in_header *in,
size_t size)
{
int res;
- char *outbuf = (char *) malloc(sizeof(struct fuse_out_header) + size);
- if (outbuf == NULL)
+ char *list = (char *) malloc(size);
+ if (list == NULL) {
send_reply(f, in, -ENOMEM, NULL, 0);
- else {
- struct fuse_out_header *out = (struct fuse_out_header *) outbuf;
- char *list = outbuf + sizeof(struct fuse_out_header);
-
- res = common_listxattr(f, in, list, size);
- size = 0;
- if (res > 0) {
- size = res;
- res = 0;
- }
- memset(out, 0, sizeof(struct fuse_out_header));
- out->unique = in->unique;
- out->error = res;
-
- send_reply_raw(f, outbuf, sizeof(struct fuse_out_header) + size);
- free(outbuf);
+ return;
}
+ res = common_listxattr(f, in, list, size);
+ size = 0;
+ if (res > 0) {
+ size = res;
+ res = 0;
+ }
+ send_reply(f, in, res, list, size);
+ free(list);
}
static void do_listxattr_size(struct fuse *f, struct fuse_in_header *in)
@@ -1538,6 +1424,7 @@ static void do_listxattr_size(struct fuse *f, struct fuse_in_header *in)
int res;
struct fuse_getxattr_out arg;
+ memset(&arg, 0, sizeof(arg));
res = common_listxattr(f, in, NULL, 0);
if (res >= 0) {
arg.size = res;
@@ -1631,6 +1518,7 @@ static void do_opendir(struct fuse *f, struct fuse_in_header *in,
dh->contents = NULL;
dh->len = 0;
dh->filled = 0;
+ mutex_init(&dh->lock);
memset(&outarg, 0, sizeof(outarg));
outarg.fh = (unsigned long) dh;
@@ -1657,6 +1545,7 @@ static void do_opendir(struct fuse *f, struct fuse_in_header *in,
cancelled */
if(f->op.releasedir)
f->op.releasedir(path, &fi);
+ pthread_mutex_destroy(&dh->lock);
free(dh);
}
pthread_mutex_unlock(&f->lock);
@@ -1665,76 +1554,143 @@ static void do_opendir(struct fuse *f, struct fuse_in_header *in,
free(dh);
}
free(path);
- } else
+ } else
send_reply(f, in, 0, &outarg, SIZEOF_COMPAT(f, fuse_open_out));
}
-static void do_readdir(struct fuse *f, struct fuse_in_header *in,
- struct fuse_read_in *arg)
+static int fill_dir_common(struct fuse_dirhandle *dh, const char *name,
+ int type, ino_t ino, off_t off)
{
- int res;
- struct fuse_dirhandle *dh = get_dirhandle(arg->fh);
- struct fuse_out_header *out;
- char *outbuf;
- char *buf;
- size_t size = 0;
- size_t outsize;
+ unsigned namelen = strlen(name);
+ unsigned entlen;
+ unsigned entsize;
+ unsigned padlen;
+ unsigned newlen;
+ unsigned char *newptr;
- outbuf = (char *) malloc(sizeof(struct fuse_out_header) + arg->size);
- if (outbuf == NULL) {
- send_reply(f, in, -ENOMEM, NULL, 0);
- return;
+ if (!(dh->fuse->flags & FUSE_USE_INO))
+ ino = (ino_t) -1;
+
+ if (namelen > FUSE_NAME_MAX)
+ namelen = FUSE_NAME_MAX;
+ else if (!namelen) {
+ dh->error = -EIO;
+ return 1;
}
- buf = outbuf + sizeof(struct fuse_out_header);
- if (f->op.readdir && f->major != 5) {
- struct fuse_dirbuf db;
- struct fuse_file_info fi;
- char *path;
+ entlen = (dh->fuse->major == 5 ?
+ FUSE_NAME_OFFSET_COMPAT5 : FUSE_NAME_OFFSET) + namelen;
+ entsize = FUSE_DIRENT_ALIGN(entlen);
+ padlen = entsize - entlen;
+ newlen = dh->len + entsize;
+ if (off && dh->fuse->major != 5) {
+ dh->filled = 0;
+ if (newlen > dh->needlen)
+ return 1;
+ }
+
+ newptr = realloc(dh->contents, newlen);
+ if (!newptr) {
+ dh->error = -ENOMEM;
+ return 1;
+ }
+ dh->contents = newptr;
+ if (dh->fuse->major == 5) {
+ struct fuse_dirent_compat5 *dirent;
+ dirent = (struct fuse_dirent_compat5 *) (dh->contents + dh->len);
+ dirent->ino = ino;
+ dirent->namelen = namelen;
+ dirent->type = type;
+ strncpy(dirent->name, name, namelen);
+ } else {
+ struct fuse_dirent *dirent;
+ dirent = (struct fuse_dirent *) (dh->contents + dh->len);
+ dirent->ino = ino;
+ dirent->off = off ? off : newlen;
+ dirent->namelen = namelen;
+ dirent->type = type;
+ strncpy(dirent->name, name, namelen);
+ }
+ if (padlen)
+ memset(dh->contents + dh->len + entlen, 0, padlen);
+ dh->len = newlen;
+ return 0;
+}
- db.fuse = f;
- db.buf = buf;
- db.len = arg->size;
+static int fill_dir(void *buf, const char *name, const struct stat *stat,
+ off_t off)
+{
+ int type = stat ? (stat->st_mode & 0170000) >> 12 : 0;
+ ino_t ino = stat ? stat->st_ino : (ino_t) -1;
+ return fill_dir_common(buf, name, type, ino, off);
+}
+
+static int fill_dir_old(struct fuse_dirhandle *dh, const char *name, int type,
+ ino_t ino)
+{
+ fill_dir_common(dh, name, type, ino, 0);
+ return dh->error;
+}
+
+static int readdir_fill(struct fuse *f, struct fuse_in_header *in,
+ struct fuse_read_in *arg, struct fuse_dirhandle *dh)
+{
+ int err = -ENOENT;
+ char *path = get_path(f, in->nodeid);
+ if (path != NULL) {
+ struct fuse_file_info fi;
memset(&fi, 0, sizeof(fi));
fi.fh = dh->fh;
- path = get_path(f, in->nodeid);
- res = f->op.readdir(path ? path : "-", &db, fill_dir5, arg->offset, &fi);
+ dh->len = 0;
+ dh->error = 0;
+ dh->needlen = arg->size;
+ dh->filled = 1;
+ err = -ENOSYS;
+ if (f->op.readdir) {
+ off_t offset = f->major == 5 ? 0 : arg->offset;
+ err = f->op.readdir(path, dh, fill_dir, offset, &fi);
+ } else if (f->op.getdir)
+ err = f->op.getdir(path, dh, fill_dir_old);
+ if (!err)
+ err = dh->error;
+ if (err)
+ dh->filled = 0;
free(path);
- if (res)
- goto err;
+ }
+ return err;
+}
- size = arg->size - db.len;
- } else {
- if (!dh->filled) {
- res = common_getdir(f, in, dh);
- if (res)
- goto err;
- dh->filled = 1;
- }
+static void do_readdir(struct fuse *f, struct fuse_in_header *in,
+ struct fuse_read_in *arg)
+{
+ int err = 0;
+ struct fuse_dirhandle *dh = get_dirhandle(arg->fh);
+ size_t size = 0;
+ unsigned char *buf = NULL;
+ pthread_mutex_lock(&dh->lock);
+ if (!dh->filled) {
+ err = readdir_fill(f, in, arg, dh);
+ if (err)
+ goto out;
+ }
+ if (dh->filled) {
if (arg->offset < dh->len) {
size = arg->size;
if (arg->offset + size > dh->len)
size = dh->len - arg->offset;
-
- memcpy(buf, dh->contents + arg->offset, size);
+ buf = dh->contents + arg->offset;
}
+ } else {
+ size = dh->len;
+ buf = dh->contents;
}
- out = (struct fuse_out_header *) outbuf;
- memset(out, 0, sizeof(struct fuse_out_header));
- out->unique = in->unique;
- out->error = 0;
- outsize = sizeof(struct fuse_out_header) + size;
-
- send_reply_raw(f, outbuf, outsize);
- free(outbuf);
- return;
- err:
- send_reply(f, in, res, NULL, 0);
- free(outbuf);
+ out:
+ send_reply(f, in, err, buf, size);
+ pthread_mutex_unlock(&dh->lock);
}
static void do_releasedir(struct fuse *f, struct fuse_in_header *in,
@@ -1744,7 +1700,7 @@ static void do_releasedir(struct fuse *f, struct fuse_in_header *in,
if (f->op.releasedir) {
char *path;
struct fuse_file_info fi;
-
+
memset(&fi, 0, sizeof(fi));
fi.fh = dh->fh;
@@ -1752,6 +1708,9 @@ static void do_releasedir(struct fuse *f, struct fuse_in_header *in,
f->op.releasedir(path ? path : "-", &fi);
free(path);
}
+ pthread_mutex_lock(&dh->lock);
+ pthread_mutex_unlock(&dh->lock);
+ pthread_mutex_destroy(&dh->lock);
free(dh->contents);
free(dh);
send_reply(f, in, 0, NULL, 0);
@@ -2115,19 +2074,8 @@ struct fuse *fuse_new_common(int fd, const char *opts,
goto out_free_name_table;
}
-#ifndef USE_UCLIBC
- pthread_mutex_init(&f->lock, NULL);
- pthread_mutex_init(&f->worker_lock, NULL);
-#else
- {
- pthread_mutexattr_t attr;
- pthread_mutexattr_init(&attr);
- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ADAPTIVE_NP);
- pthread_mutex_init(&f->lock, &attr);
- pthread_mutex_init(&f->worker_lock, &attr);
- pthread_mutexattr_destroy(&attr);
- }
-#endif
+ mutex_init(&f->lock);
+ mutex_init(&f->worker_lock);
f->numworker = 0;
f->numavail = 0;
memcpy(&f->op, op, op_size);