From: Miklos Szeredi Date: Thu, 7 Apr 2005 15:40:21 +0000 (+0000) Subject: fix X-Git-Tag: fuse_2_3_pre4~1 X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=ab9745641373d35235b8c0d124f1c355908b575f;p=qemu-gpiodev%2Flibfuse.git fix --- diff --git a/ChangeLog b/ChangeLog index 8811780..d32280f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2005-04-07 Miklos Szeredi + + * lib: finalized new readdir() interface, which now supersedes the + getdir() method. + 2005-04-03 Miklos Szeredi * Released 2.3-pre3 diff --git a/example/.cvsignore b/example/.cvsignore index f9c7884..80eb82b 100644 --- a/example/.cvsignore +++ b/example/.cvsignore @@ -2,6 +2,7 @@ Makefile.in Makefile .deps fusexmp +fusexmp_fh null hello .libs diff --git a/example/Makefile.am b/example/Makefile.am index e8c8a6d..be7a674 100644 --- a/example/Makefile.am +++ b/example/Makefile.am @@ -1,8 +1,9 @@ ## Process this file with automake to produce Makefile.in -noinst_PROGRAMS = fusexmp null hello +noinst_PROGRAMS = fusexmp fusexmp_fh null hello fusexmp_SOURCES = fusexmp.c +fusexmp_fh_SOURCES = fusexmp_fh.c null_SOURCES = null.c hello_SOURCES = hello.c diff --git a/example/fusexmp.c b/example/fusexmp.c index 3230c36..95d26b3 100644 --- a/example/fusexmp.c +++ b/example/fusexmp.c @@ -49,24 +49,30 @@ static int xmp_readlink(const char *path, char *buf, size_t size) } -static int xmp_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) +static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) { DIR *dp; struct dirent *de; - int res = 0; + + (void) offset; + (void) fi; dp = opendir(path); if(dp == NULL) return -errno; while((de = readdir(dp)) != NULL) { - res = filler(h, de->d_name, de->d_type, de->d_ino); - if(res != 0) + struct stat st; + memset(&st, 0, sizeof(st)); + st.st_ino = de->d_ino; + st.st_mode = de->d_type << 12; + if (filler(buf, de->d_name, &st, 0)) break; } closedir(dp); - return res; + return 0; } static int xmp_mknod(const char *path, mode_t mode, dev_t rdev) @@ -314,7 +320,7 @@ static int xmp_removexattr(const char *path, const char *name) static struct fuse_operations xmp_oper = { .getattr = xmp_getattr, .readlink = xmp_readlink, - .getdir = xmp_getdir, + .readdir = xmp_readdir, .mknod = xmp_mknod, .mkdir = xmp_mkdir, .symlink = xmp_symlink, diff --git a/example/fusexmp_fh.c b/example/fusexmp_fh.c new file mode 100644 index 0000000..8a40c9e --- /dev/null +++ b/example/fusexmp_fh.c @@ -0,0 +1,355 @@ +/* + FUSE: Filesystem in Userspace + Copyright (C) 2001-2005 Miklos Szeredi + + This program can be distributed under the terms of the GNU GPL. + See the file COPYING. +*/ + +#include + +#ifdef linux +/* For pread()/pwrite() */ +#define _XOPEN_SOURCE 500 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_SETXATTR +#include +#endif + +static int xmp_getattr(const char *path, struct stat *stbuf) +{ + int res; + + res = lstat(path, stbuf); + if(res == -1) + return -errno; + + return 0; +} + +static int xmp_readlink(const char *path, char *buf, size_t size) +{ + int res; + + res = readlink(path, buf, size - 1); + if(res == -1) + return -errno; + + buf[res] = '\0'; + return 0; +} + +static int xmp_opendir(const char *path, struct fuse_file_info *fi) +{ + DIR *dp = opendir(path); + if (dp == NULL) + return -errno; + + fi->fh = (unsigned long) dp; + return 0; +} + +static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) +{ + DIR *dp = (DIR *) fi->fh; + struct dirent *de; + + (void) path; + seekdir(dp, offset); + while ((de = readdir(dp)) != NULL) { + struct stat st; + memset(&st, 0, sizeof(st)); + st.st_ino = de->d_ino; + st.st_mode = de->d_type << 12; + if (filler(buf, de->d_name, &st, de->d_off)) + break; + } + + return 0; +} + +static int xmp_releasedir(const char *path, struct fuse_file_info *fi) +{ + DIR *dp = (DIR *) fi->fh; + (void) path; + closedir(dp); + return 0; +} + +static int xmp_mknod(const char *path, mode_t mode, dev_t rdev) +{ + int res; + + res = mknod(path, mode, rdev); + if(res == -1) + return -errno; + + return 0; +} + +static int xmp_mkdir(const char *path, mode_t mode) +{ + int res; + + res = mkdir(path, mode); + if(res == -1) + return -errno; + + return 0; +} + +static int xmp_unlink(const char *path) +{ + int res; + + res = unlink(path); + if(res == -1) + return -errno; + + return 0; +} + +static int xmp_rmdir(const char *path) +{ + int res; + + res = rmdir(path); + if(res == -1) + return -errno; + + return 0; +} + +static int xmp_symlink(const char *from, const char *to) +{ + int res; + + res = symlink(from, to); + if(res == -1) + return -errno; + + return 0; +} + +static int xmp_rename(const char *from, const char *to) +{ + int res; + + res = rename(from, to); + if(res == -1) + return -errno; + + return 0; +} + +static int xmp_link(const char *from, const char *to) +{ + int res; + + res = link(from, to); + if(res == -1) + return -errno; + + return 0; +} + +static int xmp_chmod(const char *path, mode_t mode) +{ + int res; + + res = chmod(path, mode); + if(res == -1) + return -errno; + + return 0; +} + +static int xmp_chown(const char *path, uid_t uid, gid_t gid) +{ + int res; + + res = lchown(path, uid, gid); + if(res == -1) + return -errno; + + return 0; +} + +static int xmp_truncate(const char *path, off_t size) +{ + int res; + + res = truncate(path, size); + if(res == -1) + return -errno; + + return 0; +} + +static int xmp_utime(const char *path, struct utimbuf *buf) +{ + int res; + + res = utime(path, buf); + if(res == -1) + return -errno; + + return 0; +} + + +static int xmp_open(const char *path, struct fuse_file_info *fi) +{ + int res; + + res = open(path, fi->flags); + if(res == -1) + return -errno; + + fi->fh = res; + return 0; +} + +static int xmp_read(const char *path, char *buf, size_t size, off_t offset, + struct fuse_file_info *fi) +{ + int res; + + (void) path; + res = pread(fi->fh, buf, size, offset); + if(res == -1) + res = -errno; + + return res; +} + +static int xmp_write(const char *path, const char *buf, size_t size, + off_t offset, struct fuse_file_info *fi) +{ + int res; + + (void) path; + res = pwrite(fi->fh, buf, size, offset); + if(res == -1) + res = -errno; + + return res; +} + +static int xmp_statfs(const char *path, struct statfs *stbuf) +{ + int res; + + res = statfs(path, stbuf); + if(res == -1) + return -errno; + + return 0; +} + +static int xmp_release(const char *path, struct fuse_file_info *fi) +{ + (void) path; + close(fi->fh); + + return 0; +} + +static int xmp_fsync(const char *path, int isdatasync, + struct fuse_file_info *fi) +{ + int res; + (void) path; + + if (isdatasync) + res = fdatasync(fi->fh); + else + res = fsync(fi->fh); + if(res == -1) + return -errno; + + return 0; +} + +#ifdef HAVE_SETXATTR +/* xattr operations are optional and can safely be left unimplemented */ +static int xmp_setxattr(const char *path, const char *name, const char *value, + size_t size, int flags) +{ + int res = lsetxattr(path, name, value, size, flags); + if(res == -1) + return -errno; + return 0; +} + +static int xmp_getxattr(const char *path, const char *name, char *value, + size_t size) +{ + int res = lgetxattr(path, name, value, size); + if(res == -1) + return -errno; + return res; +} + +static int xmp_listxattr(const char *path, char *list, size_t size) +{ + int res = llistxattr(path, list, size); + if(res == -1) + return -errno; + return res; +} + +static int xmp_removexattr(const char *path, const char *name) +{ + int res = lremovexattr(path, name); + if(res == -1) + return -errno; + return 0; +} +#endif /* HAVE_SETXATTR */ + +static struct fuse_operations xmp_oper = { + .getattr = xmp_getattr, + .readlink = xmp_readlink, + .opendir = xmp_opendir, + .readdir = xmp_readdir, + .releasedir = xmp_releasedir, + .mknod = xmp_mknod, + .mkdir = xmp_mkdir, + .symlink = xmp_symlink, + .unlink = xmp_unlink, + .rmdir = xmp_rmdir, + .rename = xmp_rename, + .link = xmp_link, + .chmod = xmp_chmod, + .chown = xmp_chown, + .truncate = xmp_truncate, + .utime = xmp_utime, + .open = xmp_open, + .read = xmp_read, + .write = xmp_write, + .statfs = xmp_statfs, + .release = xmp_release, + .fsync = xmp_fsync, +#ifdef HAVE_SETXATTR + .setxattr = xmp_setxattr, + .getxattr = xmp_getxattr, + .listxattr = xmp_listxattr, + .removexattr= xmp_removexattr, +#endif +}; + +int main(int argc, char *argv[]) +{ + return fuse_main(argc, argv, &xmp_oper); +} diff --git a/example/hello.c b/example/hello.c index 9e4099b..b71bcd6 100644 --- a/example/hello.c +++ b/example/hello.c @@ -35,14 +35,18 @@ static int hello_getattr(const char *path, struct stat *stbuf) return res; } -static int hello_getdir(const char *path, fuse_dirh_t h, fuse_dirfil_t filler) +static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler, + off_t offset, struct fuse_file_info *fi) { + (void) offset; + (void) fi; + if(strcmp(path, "/") != 0) return -ENOENT; - filler(h, ".", 0, 0); - filler(h, "..", 0, 0); - filler(h, hello_path + 1, 0, 0); + filler(buf, ".", NULL, 0); + filler(buf, "..", NULL, 0); + filler(buf, hello_path + 1, NULL, 0); return 0; } @@ -79,7 +83,7 @@ static int hello_read(const char *path, char *buf, size_t size, off_t offset, static struct fuse_operations hello_oper = { .getattr = hello_getattr, - .getdir = hello_getdir, + .readdir = hello_readdir, .open = hello_open, .read = hello_read, }; diff --git a/include/fuse.h b/include/fuse.h index 35ec49f..336e8f3 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -47,33 +47,21 @@ extern "C" { /** Handle for a FUSE filesystem */ struct fuse; -/** Handle for a getdir() operation */ -typedef struct fuse_dirhandle *fuse_dirh_t; - -/** Function to add an entry in a getdir() operation - * - * @param h the handle passed to the getdir() operation - * @param name the file name of the directory entry - * @param type the file type (0 if unknown) see - * @param ino the inode number, ignored if "use_ino" mount option is - * not specified - * @return 0 on success, -errno on error - */ -typedef int (*fuse_dirfil_t) (fuse_dirh_t h, const char *name, int type, - ino_t ino); - /** Function to add an entry in a readdir() operation * * @param buf the buffer passed to the readdir() operation * @param name the file name of the directory entry - * @param type the file type (0 if unknown) see - * @param ino the inode number, ignored if "use_ino" mount option is - * not specified - * @param off offset of the next entry - * @return 0 on success, 1 if buffer is full + * @param stat file attributes, can be NULL + * @param off offset of the next entry or zero + * @return 1 if buffer is full, zero otherwise */ -typedef int (*fuse_dirfil5_t) (void *buf, const char *name, int type, - ino_t ino, off_t off); +typedef int (*fuse_fill_dir_t) (void *buf, const char *name, + const struct stat *stat, off_t off); + +/* Used by deprecated getdir() method */ +typedef struct fuse_dirhandle *fuse_dirh_t; +typedef int (*fuse_dirfil_t) (fuse_dirh_t h, const char *name, int type, + ino_t ino); /** Information about open files */ struct fuse_file_info { @@ -99,7 +87,7 @@ struct fuse_file_info { * * All methods are optional, but some are essential for a useful * filesystem (e.g. getattr). Flush, release, fsync, opendir, - * readdir, releasedir, fsyncdir, init and destroy are special purpose + * releasedir, fsyncdir, init and destroy are special purpose * methods, without which a full featured filesystem can still be * implemented. */ @@ -122,12 +110,7 @@ struct fuse_operations { */ int (*readlink) (const char *, char *, size_t); - /** Read the contents of a directory - * - * This operation is the opendir(), readdir(), ..., closedir() - * sequence in one call. For each directory entry the filldir - * function should be called. - */ + /* Deprecated, use readdir() instead */ int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t); /** Create a file node @@ -265,10 +248,24 @@ struct fuse_operations { /** Read directory * - * This is an alternative inteface to getdir(), and if defined - * getdir() will not be called + * This supersedes the old getdir() interface. New applications + * should use this. + * + * The filesystem may choose between two modes of operation: + * + * 1) The readdir implementation ignores the offset parameter, and + * passes zero to the filler function's offset. The filler + * function will not return '1' (unless an error happens), so the + * whole directory is read in a single readdir operation. This + * works just like the old getdir() method. + * + * 2) The readdir implementation keeps track of the offsets of the + * directory entries. It uses the offset parameter and always + * passes non-zero offset to the filler function. When the buffer + * is full (or an error happens) the filler function will return + * '1'. */ - int (*readdir) (const char *, void *, fuse_dirfil5_t, off_t, + int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t, struct fuse_file_info *); /** Release directory */ 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 #include #include +#include /* 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); diff --git a/lib/fuse_kernel_compat5.h b/lib/fuse_kernel_compat5.h index 8edca59..49936dc 100644 --- a/lib/fuse_kernel_compat5.h +++ b/lib/fuse_kernel_compat5.h @@ -38,5 +38,3 @@ struct fuse_dirent_compat5 { }; #define FUSE_NAME_OFFSET_COMPAT5 ((unsigned) ((struct fuse_dirent_compat5 *) 0)->name) -#define FUSE_DIRENT_SIZE_COMPAT5(d) \ - FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_COMPAT5 + (d)->namelen)