mirror of
https://github.com/openharmony/third_party_libfuse.git
synced 2026-07-18 18:44:26 -04:00
libfuse: implement readdirplus for high-level API
Reuse the old "readdir" callback, but add a flags argument, that has FUSE_READDIR_PLUS in case this is a "plus" version. Filesystems can safely ignore this flag, but if they want they can add optimizations based on it: i.e. only retrieve the full attributes in PLUS mode. The filler function is also given a flags argument and the filesystem can set FUSE_FILL_DIR_PLUS if all the attributes in "stat" are valid.
This commit is contained in:
committed by
Miklos Szeredi
parent
0096c126aa
commit
6bf2e6f07c
@@ -1,3 +1,14 @@
|
||||
2014-03-05 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* libfuse: implement readdirplus for high-level API. Reuse the
|
||||
old "readdir" callback, but add a flags argument, that has
|
||||
FUSE_READDIR_PLUS in case this is a "plus" version. Filesystems
|
||||
can safely ignore this flag, but if they want they can add
|
||||
optimizations based on it: i.e. only retrieve the full attributes
|
||||
in PLUS mode. The filler function is also given a flags argument
|
||||
and the filesystem can set FUSE_FILL_DIR_PLUS if all the
|
||||
attributes in "stat" are valid. Original patch by Eric Wong
|
||||
|
||||
2014-02-21 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* libfuse: added fuse_lo-plus.c to the examples
|
||||
|
||||
+1
-1
@@ -54,7 +54,7 @@ if test "$enable_mtab" = "no"; then
|
||||
fi
|
||||
|
||||
AC_CHECK_FUNCS([fork setxattr fdatasync splice vmsplice utimensat pipe2])
|
||||
AC_CHECK_FUNCS([posix_fallocate])
|
||||
AC_CHECK_FUNCS([posix_fallocate fstatat])
|
||||
AC_CHECK_MEMBERS([struct stat.st_atim])
|
||||
AC_CHECK_MEMBERS([struct stat.st_atimespec])
|
||||
|
||||
|
||||
+6
-4
@@ -169,17 +169,19 @@ static int fioc_truncate(const char *path, off_t size)
|
||||
}
|
||||
|
||||
static int fioc_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
off_t offset, struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
(void) fi;
|
||||
(void) offset;
|
||||
(void) flags;
|
||||
|
||||
if (fioc_file_type(path) != FIOC_ROOT)
|
||||
return -ENOENT;
|
||||
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, FIOC_NAME, NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
filler(buf, FIOC_NAME, NULL, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+4
-2
@@ -87,20 +87,22 @@ static int fsel_getattr(const char *path, struct stat *stbuf)
|
||||
}
|
||||
|
||||
static int fsel_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
off_t offset, struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
char name[2] = { };
|
||||
int i;
|
||||
|
||||
(void) offset;
|
||||
(void) fi;
|
||||
(void) flags;
|
||||
|
||||
if (strcmp(path, "/") != 0)
|
||||
return -ENOENT;
|
||||
|
||||
for (i = 0; i < FSEL_FILES; i++) {
|
||||
name[0] = fsel_hex_map[i];
|
||||
filler(buf, name, NULL, 0);
|
||||
filler(buf, name, NULL, 0, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
+4
-2
@@ -81,13 +81,15 @@ static int xmp_readlink(const char *path, char *buf, size_t size)
|
||||
|
||||
|
||||
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
off_t offset, struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
DIR *dp;
|
||||
struct dirent *de;
|
||||
|
||||
(void) offset;
|
||||
(void) fi;
|
||||
(void) flags;
|
||||
|
||||
dp = opendir(path);
|
||||
if (dp == NULL)
|
||||
@@ -98,7 +100,7 @@ static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
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))
|
||||
if (filler(buf, de->d_name, &st, 0, 0))
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
+18
-5
@@ -128,7 +128,8 @@ static inline struct xmp_dirp *get_dirp(struct fuse_file_info *fi)
|
||||
}
|
||||
|
||||
static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
off_t offset, struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
struct xmp_dirp *d = get_dirp(fi);
|
||||
|
||||
@@ -141,18 +142,30 @@ static int xmp_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
while (1) {
|
||||
struct stat st;
|
||||
off_t nextoff;
|
||||
enum fuse_fill_dir_flags fill_flags = 0;
|
||||
|
||||
if (!d->entry) {
|
||||
d->entry = readdir(d->dp);
|
||||
if (!d->entry)
|
||||
break;
|
||||
}
|
||||
#ifdef HAVE_FSTATAT
|
||||
if (flags & FUSE_READDIR_PLUS) {
|
||||
int res;
|
||||
|
||||
memset(&st, 0, sizeof(st));
|
||||
st.st_ino = d->entry->d_ino;
|
||||
st.st_mode = d->entry->d_type << 12;
|
||||
res = fstatat(dirfd(d->dp), d->entry->d_name, &st,
|
||||
AT_SYMLINK_NOFOLLOW);
|
||||
if (res != -1)
|
||||
fill_flags |= FUSE_FILL_DIR_PLUS;
|
||||
}
|
||||
#endif
|
||||
if (!(fill_flags & FUSE_FILL_DIR_PLUS)) {
|
||||
memset(&st, 0, sizeof(st));
|
||||
st.st_ino = d->entry->d_ino;
|
||||
st.st_mode = d->entry->d_type << 12;
|
||||
}
|
||||
nextoff = telldir(d->dp);
|
||||
if (filler(buf, d->entry->d_name, &st, nextoff))
|
||||
if (filler(buf, d->entry->d_name, &st, nextoff, fill_flags))
|
||||
break;
|
||||
|
||||
d->entry = NULL;
|
||||
|
||||
+6
-4
@@ -65,17 +65,19 @@ static int hello_getattr(const char *path, struct stat *stbuf)
|
||||
}
|
||||
|
||||
static int hello_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
off_t offset, struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
(void) offset;
|
||||
(void) fi;
|
||||
(void) flags;
|
||||
|
||||
if (strcmp(path, "/") != 0)
|
||||
return -ENOENT;
|
||||
|
||||
filler(buf, ".", NULL, 0);
|
||||
filler(buf, "..", NULL, 0);
|
||||
filler(buf, hello_path + 1, NULL, 0);
|
||||
filler(buf, ".", NULL, 0, 0);
|
||||
filler(buf, "..", NULL, 0, 0);
|
||||
filler(buf, hello_path + 1, NULL, 0, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+34
-3
@@ -36,16 +36,46 @@ extern "C" {
|
||||
/** Handle for a FUSE filesystem */
|
||||
struct fuse;
|
||||
|
||||
/**
|
||||
* Readdir flags, passed to ->readdir()
|
||||
*/
|
||||
enum fuse_readdir_flags {
|
||||
/**
|
||||
* "Plus" mode.
|
||||
*
|
||||
* The kernel wants to prefill the inode cache during readdir. The
|
||||
* filesystem may honour this by filling in the attributes and setting
|
||||
* FUSE_FILL_DIR_FLAGS for the filler function. The filesystem may also
|
||||
* just ignore this flag completely.
|
||||
*/
|
||||
FUSE_READDIR_PLUS = (1 << 0),
|
||||
};
|
||||
|
||||
enum fuse_fill_dir_flags {
|
||||
/**
|
||||
* "Plus" mode: all file attributes are valid
|
||||
*
|
||||
* The attributes are used by the kernel to prefill the inode cache
|
||||
* during a readdir.
|
||||
*
|
||||
* It is okay to set FUSE_FILL_DIR_PLUS if FUSE_READDIR_PLUS is not set
|
||||
* and vice versa.
|
||||
*/
|
||||
FUSE_FILL_DIR_PLUS = (1 << 1),
|
||||
};
|
||||
|
||||
/** 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 stat file attributes, can be NULL
|
||||
* @param off offset of the next entry or zero
|
||||
* @param flags fill flags
|
||||
* @return 1 if buffer is full, zero otherwise
|
||||
*/
|
||||
typedef int (*fuse_fill_dir_t) (void *buf, const char *name,
|
||||
const struct stat *stbuf, off_t off);
|
||||
const struct stat *stbuf, off_t off,
|
||||
enum fuse_fill_dir_flags flags);
|
||||
|
||||
/**
|
||||
* The file system operations:
|
||||
@@ -289,9 +319,10 @@ struct fuse_operations {
|
||||
* '1'.
|
||||
*
|
||||
* Introduced in version 2.3
|
||||
* The "flags" argument added in version 3.0
|
||||
*/
|
||||
int (*readdir) (const char *, void *, fuse_fill_dir_t, off_t,
|
||||
struct fuse_file_info *);
|
||||
struct fuse_file_info *, enum fuse_readdir_flags);
|
||||
|
||||
/** Release directory
|
||||
*
|
||||
@@ -806,7 +837,7 @@ int fuse_fs_opendir(struct fuse_fs *fs, const char *path,
|
||||
struct fuse_file_info *fi);
|
||||
int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
|
||||
fuse_fill_dir_t filler, off_t off,
|
||||
struct fuse_file_info *fi);
|
||||
struct fuse_file_info *fi, enum fuse_readdir_flags flags);
|
||||
int fuse_fs_fsyncdir(struct fuse_fs *fs, const char *path, int datasync,
|
||||
struct fuse_file_info *fi);
|
||||
int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
|
||||
|
||||
+255
-51
@@ -188,11 +188,19 @@ struct node_lru {
|
||||
struct timespec forget_time;
|
||||
};
|
||||
|
||||
struct fuse_direntry {
|
||||
struct stat stat;
|
||||
char *name;
|
||||
struct fuse_direntry *next;
|
||||
};
|
||||
|
||||
struct fuse_dh {
|
||||
pthread_mutex_t lock;
|
||||
struct fuse *fuse;
|
||||
fuse_req_t req;
|
||||
char *contents;
|
||||
struct fuse_direntry *first;
|
||||
struct fuse_direntry **last;
|
||||
int allocated;
|
||||
unsigned len;
|
||||
unsigned size;
|
||||
@@ -1887,16 +1895,19 @@ int fuse_fs_releasedir(struct fuse_fs *fs, const char *path,
|
||||
|
||||
int fuse_fs_readdir(struct fuse_fs *fs, const char *path, void *buf,
|
||||
fuse_fill_dir_t filler, off_t off,
|
||||
struct fuse_file_info *fi)
|
||||
struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
fuse_get_context()->private_data = fs->user_data;
|
||||
if (fs->op.readdir) {
|
||||
if (fs->debug)
|
||||
fprintf(stderr, "readdir[%llu] from %llu\n",
|
||||
if (fs->debug) {
|
||||
fprintf(stderr, "readdir%s[%llu] from %llu\n",
|
||||
(flags & FUSE_READDIR_PLUS) ? "plus" : "",
|
||||
(unsigned long long) fi->fh,
|
||||
(unsigned long long) off);
|
||||
}
|
||||
|
||||
return fs->op.readdir(path, buf, filler, off, fi);
|
||||
return fs->op.readdir(path, buf, filler, off, fi, flags);
|
||||
} else {
|
||||
return -ENOSYS;
|
||||
}
|
||||
@@ -2329,6 +2340,28 @@ static void update_stat(struct node *node, const struct stat *stbuf)
|
||||
curr_time(&node->stat_updated);
|
||||
}
|
||||
|
||||
static int do_lookup(struct fuse *f, fuse_ino_t nodeid, const char *name,
|
||||
struct fuse_entry_param *e)
|
||||
{
|
||||
struct node *node;
|
||||
|
||||
node = find_node(f, nodeid, name);
|
||||
if (node == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
e->ino = node->nodeid;
|
||||
e->generation = node->generation;
|
||||
e->entry_timeout = f->conf.entry_timeout;
|
||||
e->attr_timeout = f->conf.attr_timeout;
|
||||
if (f->conf.auto_cache) {
|
||||
pthread_mutex_lock(&f->lock);
|
||||
update_stat(node, &e->attr);
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
}
|
||||
set_stat(f, e->ino, &e->attr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
|
||||
const char *name, const char *path,
|
||||
struct fuse_entry_param *e, struct fuse_file_info *fi)
|
||||
@@ -2341,25 +2374,10 @@ static int lookup_path(struct fuse *f, fuse_ino_t nodeid,
|
||||
else
|
||||
res = fuse_fs_getattr(f->fs, path, &e->attr);
|
||||
if (res == 0) {
|
||||
struct node *node;
|
||||
|
||||
node = find_node(f, nodeid, name);
|
||||
if (node == NULL)
|
||||
res = -ENOMEM;
|
||||
else {
|
||||
e->ino = node->nodeid;
|
||||
e->generation = node->generation;
|
||||
e->entry_timeout = f->conf.entry_timeout;
|
||||
e->attr_timeout = f->conf.attr_timeout;
|
||||
if (f->conf.auto_cache) {
|
||||
pthread_mutex_lock(&f->lock);
|
||||
update_stat(node, &e->attr);
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
}
|
||||
set_stat(f, e->ino, &e->attr);
|
||||
if (f->conf.debug)
|
||||
fprintf(stderr, " NODEID: %llu\n",
|
||||
(unsigned long long) e->ino);
|
||||
res = do_lookup(f, nodeid, name, e);
|
||||
if (res == 0 && f->conf.debug) {
|
||||
fprintf(stderr, " NODEID: %llu\n",
|
||||
(unsigned long long) e->ino);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
@@ -3199,6 +3217,7 @@ static void fuse_lib_opendir(fuse_req_t req, fuse_ino_t ino,
|
||||
memset(dh, 0, sizeof(struct fuse_dh));
|
||||
dh->fuse = f;
|
||||
dh->contents = NULL;
|
||||
dh->first = NULL;
|
||||
dh->len = 0;
|
||||
dh->filled = 0;
|
||||
dh->nodeid = ino;
|
||||
@@ -3257,12 +3276,56 @@ static int extend_contents(struct fuse_dh *dh, unsigned minsize)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fuse_add_direntry_to_dh(struct fuse_dh *dh, const char *name,
|
||||
struct stat *st)
|
||||
{
|
||||
struct fuse_direntry *de;
|
||||
|
||||
de = malloc(sizeof(struct fuse_direntry));
|
||||
if (!de) {
|
||||
dh->error = -ENOMEM;
|
||||
return -1;
|
||||
}
|
||||
de->name = strdup(name);
|
||||
if (!de->name) {
|
||||
dh->error = -ENOMEM;
|
||||
free(de);
|
||||
return -1;
|
||||
}
|
||||
de->stat = *st;
|
||||
de->next = NULL;
|
||||
|
||||
*dh->last = de;
|
||||
dh->last = &de->next;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static fuse_ino_t lookup_nodeid(struct fuse *f, fuse_ino_t parent,
|
||||
const char *name)
|
||||
{
|
||||
struct node *node;
|
||||
fuse_ino_t res = FUSE_UNKNOWN_INO;
|
||||
|
||||
pthread_mutex_lock(&f->lock);
|
||||
node = lookup_node(f, parent, name);
|
||||
if (node)
|
||||
res = node->nodeid;
|
||||
pthread_mutex_unlock(&f->lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int fill_dir(void *dh_, const char *name, const struct stat *statp,
|
||||
off_t off)
|
||||
off_t off, enum fuse_fill_dir_flags flags)
|
||||
{
|
||||
struct fuse_dh *dh = (struct fuse_dh *) dh_;
|
||||
struct stat stbuf;
|
||||
size_t newlen;
|
||||
|
||||
if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
|
||||
dh->error = -EIO;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (statp)
|
||||
stbuf = *statp;
|
||||
@@ -3274,16 +3337,19 @@ static int fill_dir(void *dh_, const char *name, const struct stat *statp,
|
||||
if (!dh->fuse->conf.use_ino) {
|
||||
stbuf.st_ino = FUSE_UNKNOWN_INO;
|
||||
if (dh->fuse->conf.readdir_ino) {
|
||||
struct node *node;
|
||||
pthread_mutex_lock(&dh->fuse->lock);
|
||||
node = lookup_node(dh->fuse, dh->nodeid, name);
|
||||
if (node)
|
||||
stbuf.st_ino = (ino_t) node->nodeid;
|
||||
pthread_mutex_unlock(&dh->fuse->lock);
|
||||
stbuf.st_ino = (ino_t)
|
||||
lookup_nodeid(dh->fuse, dh->nodeid, name);
|
||||
}
|
||||
}
|
||||
|
||||
if (off) {
|
||||
size_t newlen;
|
||||
|
||||
if (dh->first) {
|
||||
dh->error = -EIO;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (extend_contents(dh, dh->needlen) == -1)
|
||||
return 1;
|
||||
|
||||
@@ -3294,22 +3360,95 @@ static int fill_dir(void *dh_, const char *name, const struct stat *statp,
|
||||
&stbuf, off);
|
||||
if (newlen > dh->needlen)
|
||||
return 1;
|
||||
|
||||
dh->len = newlen;
|
||||
} else {
|
||||
newlen = dh->len +
|
||||
fuse_add_direntry(dh->req, NULL, 0, name, NULL, 0);
|
||||
if (extend_contents(dh, newlen) == -1)
|
||||
if (!dh->filled) {
|
||||
dh->error = -EIO;
|
||||
return 1;
|
||||
}
|
||||
if (fuse_add_direntry_to_dh(dh, name, &stbuf) == -1)
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fill_dir_plus(void *dh_, const char *name, const struct stat *statp,
|
||||
off_t off, enum fuse_fill_dir_flags flags)
|
||||
{
|
||||
struct fuse_dh *dh = (struct fuse_dh *) dh_;
|
||||
struct fuse_entry_param e = {
|
||||
/* ino=0 tells the kernel to ignore readdirplus stat info */
|
||||
.ino = 0,
|
||||
};
|
||||
struct fuse *f = dh->fuse;
|
||||
int res;
|
||||
|
||||
if ((flags & ~FUSE_FILL_DIR_PLUS) != 0) {
|
||||
dh->error = -EIO;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (off && statp && (flags & FUSE_FILL_DIR_PLUS)) {
|
||||
e.attr = *statp;
|
||||
|
||||
res = do_lookup(f, dh->nodeid, name, &e);
|
||||
if (res) {
|
||||
dh->error = res;
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
e.attr.st_ino = FUSE_UNKNOWN_INO;
|
||||
if (!f->conf.use_ino && f->conf.readdir_ino) {
|
||||
e.attr.st_ino = (ino_t)
|
||||
lookup_nodeid(f, dh->nodeid, name);
|
||||
}
|
||||
}
|
||||
|
||||
if (off) {
|
||||
size_t newlen;
|
||||
|
||||
if (dh->first) {
|
||||
dh->error = -EIO;
|
||||
return 1;
|
||||
}
|
||||
if (extend_contents(dh, dh->needlen) == -1)
|
||||
return 1;
|
||||
|
||||
fuse_add_direntry(dh->req, dh->contents + dh->len,
|
||||
dh->size - dh->len, name, &stbuf, newlen);
|
||||
dh->filled = 0;
|
||||
newlen = dh->len +
|
||||
fuse_add_direntry_plus(dh->req, dh->contents + dh->len,
|
||||
dh->needlen - dh->len, name,
|
||||
&e, off);
|
||||
if (newlen > dh->needlen)
|
||||
return 1;
|
||||
dh->len = newlen;
|
||||
} else {
|
||||
if (!dh->filled) {
|
||||
dh->error = -EIO;
|
||||
return 1;
|
||||
}
|
||||
if (fuse_add_direntry_to_dh(dh, name, &e.attr) == -1)
|
||||
return 1;
|
||||
}
|
||||
dh->len = newlen;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void free_direntries(struct fuse_direntry *de)
|
||||
{
|
||||
while (de) {
|
||||
struct fuse_direntry *next = de->next;
|
||||
free(de->name);
|
||||
free(de);
|
||||
de = next;
|
||||
}
|
||||
}
|
||||
|
||||
static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
|
||||
size_t size, off_t off, struct fuse_dh *dh,
|
||||
struct fuse_file_info *fi)
|
||||
struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
char *path;
|
||||
int err;
|
||||
@@ -3320,14 +3459,21 @@ static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
|
||||
err = get_path(f, ino, &path);
|
||||
if (!err) {
|
||||
struct fuse_intr_data d;
|
||||
fuse_fill_dir_t filler = fill_dir;
|
||||
|
||||
if (flags & FUSE_READDIR_PLUS)
|
||||
filler = fill_dir_plus;
|
||||
|
||||
free_direntries(dh->first);
|
||||
dh->first = NULL;
|
||||
dh->last = &dh->first;
|
||||
dh->len = 0;
|
||||
dh->error = 0;
|
||||
dh->needlen = size;
|
||||
dh->filled = 1;
|
||||
dh->req = req;
|
||||
fuse_prepare_interrupt(f, req, &d);
|
||||
err = fuse_fs_readdir(f->fs, path, dh, fill_dir, off, fi);
|
||||
err = fuse_fs_readdir(f->fs, path, dh, filler, off, fi, flags);
|
||||
fuse_finish_interrupt(f, req, &d);
|
||||
dh->req = NULL;
|
||||
if (!err)
|
||||
@@ -3339,12 +3485,58 @@ static int readdir_fill(struct fuse *f, fuse_req_t req, fuse_ino_t ino,
|
||||
return err;
|
||||
}
|
||||
|
||||
static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
|
||||
off_t off, struct fuse_file_info *llfi)
|
||||
static int readdir_fill_from_list(fuse_req_t req, struct fuse_dh *dh,
|
||||
off_t off, enum fuse_readdir_flags flags)
|
||||
{
|
||||
off_t pos;
|
||||
struct fuse_direntry *de = dh->first;
|
||||
|
||||
dh->len = 0;
|
||||
|
||||
if (extend_contents(dh, dh->needlen) == -1)
|
||||
return dh->error;
|
||||
|
||||
for (pos = 0; pos < off; pos++) {
|
||||
if (!de)
|
||||
break;
|
||||
|
||||
de = de->next;
|
||||
}
|
||||
while (de) {
|
||||
char *p = dh->contents + dh->len;
|
||||
unsigned rem = dh->needlen - dh->len;
|
||||
unsigned thislen;
|
||||
unsigned newlen;
|
||||
pos++;
|
||||
|
||||
if (flags & FUSE_READDIR_PLUS) {
|
||||
struct fuse_entry_param e = {
|
||||
.ino = 0,
|
||||
.attr = de->stat,
|
||||
};
|
||||
thislen = fuse_add_direntry_plus(req, p, rem,
|
||||
de->name, &e, pos);
|
||||
} else {
|
||||
thislen = fuse_add_direntry(req, p, rem,
|
||||
de->name, &de->stat, pos);
|
||||
}
|
||||
newlen = dh->len + thislen;
|
||||
if (newlen > dh->needlen)
|
||||
break;
|
||||
dh->len = newlen;
|
||||
de = de->next;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void fuse_readdir_common(fuse_req_t req, fuse_ino_t ino, size_t size,
|
||||
off_t off, struct fuse_file_info *llfi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
struct fuse *f = req_fuse_prepare(req);
|
||||
struct fuse_file_info fi;
|
||||
struct fuse_dh *dh = get_dirhandle(llfi, &fi);
|
||||
int err;
|
||||
|
||||
pthread_mutex_lock(&dh->lock);
|
||||
/* According to SUS, directory contents need to be refreshed on
|
||||
@@ -3353,27 +3545,37 @@ static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
|
||||
dh->filled = 0;
|
||||
|
||||
if (!dh->filled) {
|
||||
int err = readdir_fill(f, req, ino, size, off, dh, &fi);
|
||||
err = readdir_fill(f, req, ino, size, off, dh, &fi, flags);
|
||||
if (err) {
|
||||
reply_err(req, err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (dh->filled) {
|
||||
if (off < dh->len) {
|
||||
if (off + size > dh->len)
|
||||
size = dh->len - off;
|
||||
} else
|
||||
size = 0;
|
||||
} else {
|
||||
size = dh->len;
|
||||
off = 0;
|
||||
dh->needlen = size;
|
||||
err = readdir_fill_from_list(req, dh, off, flags);
|
||||
if (err) {
|
||||
reply_err(req, err);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
fuse_reply_buf(req, dh->contents + off, size);
|
||||
fuse_reply_buf(req, dh->contents, dh->len);
|
||||
out:
|
||||
pthread_mutex_unlock(&dh->lock);
|
||||
}
|
||||
|
||||
static void fuse_lib_readdir(fuse_req_t req, fuse_ino_t ino, size_t size,
|
||||
off_t off, struct fuse_file_info *llfi)
|
||||
{
|
||||
fuse_readdir_common(req, ino, size, off, llfi, 0);
|
||||
}
|
||||
|
||||
static void fuse_lib_readdirplus(fuse_req_t req, fuse_ino_t ino, size_t size,
|
||||
off_t off, struct fuse_file_info *llfi)
|
||||
{
|
||||
fuse_readdir_common(req, ino, size, off, llfi, FUSE_READDIR_PLUS);
|
||||
}
|
||||
|
||||
static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino,
|
||||
struct fuse_file_info *llfi)
|
||||
{
|
||||
@@ -3393,6 +3595,7 @@ static void fuse_lib_releasedir(fuse_req_t req, fuse_ino_t ino,
|
||||
pthread_mutex_lock(&dh->lock);
|
||||
pthread_mutex_unlock(&dh->lock);
|
||||
pthread_mutex_destroy(&dh->lock);
|
||||
free_direntries(dh->first);
|
||||
free(dh->contents);
|
||||
free(dh);
|
||||
reply_err(req, 0);
|
||||
@@ -4023,6 +4226,7 @@ static struct fuse_lowlevel_ops fuse_path_ops = {
|
||||
.fsync = fuse_lib_fsync,
|
||||
.opendir = fuse_lib_opendir,
|
||||
.readdir = fuse_lib_readdir,
|
||||
.readdirplus = fuse_lib_readdirplus,
|
||||
.releasedir = fuse_lib_releasedir,
|
||||
.fsyncdir = fuse_lib_fsyncdir,
|
||||
.statfs = fuse_lib_statfs,
|
||||
|
||||
+6
-4
@@ -172,20 +172,22 @@ static int iconv_opendir(const char *path, struct fuse_file_info *fi)
|
||||
}
|
||||
|
||||
static int iconv_dir_fill(void *buf, const char *name,
|
||||
const struct stat *stbuf, off_t off)
|
||||
const struct stat *stbuf, off_t off,
|
||||
enum fuse_fill_dir_flags flags)
|
||||
{
|
||||
struct iconv_dh *dh = buf;
|
||||
char *newname;
|
||||
int res = 0;
|
||||
if (iconv_convpath(dh->ic, name, &newname, 1) == 0) {
|
||||
res = dh->prev_filler(dh->prev_buf, newname, stbuf, off);
|
||||
res = dh->prev_filler(dh->prev_buf, newname, stbuf, off, flags);
|
||||
free(newname);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
off_t offset, struct fuse_file_info *fi)
|
||||
off_t offset, struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
struct iconv *ic = iconv_get();
|
||||
char *newpath;
|
||||
@@ -196,7 +198,7 @@ static int iconv_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
|
||||
dh.prev_buf = buf;
|
||||
dh.prev_filler = filler;
|
||||
err = fuse_fs_readdir(ic->next, newpath, &dh, iconv_dir_fill,
|
||||
offset, fi);
|
||||
offset, fi, flags);
|
||||
free(newpath);
|
||||
}
|
||||
return err;
|
||||
|
||||
@@ -181,14 +181,15 @@ static int subdir_opendir(const char *path, struct fuse_file_info *fi)
|
||||
|
||||
static int subdir_readdir(const char *path, void *buf,
|
||||
fuse_fill_dir_t filler, off_t offset,
|
||||
struct fuse_file_info *fi)
|
||||
struct fuse_file_info *fi,
|
||||
enum fuse_readdir_flags flags)
|
||||
{
|
||||
struct subdir *d = subdir_get();
|
||||
char *newpath;
|
||||
int err = subdir_addpath(d, path, &newpath);
|
||||
if (!err) {
|
||||
err = fuse_fs_readdir(d->next, newpath, buf, filler, offset,
|
||||
fi);
|
||||
fi, flags);
|
||||
free(newpath);
|
||||
}
|
||||
return err;
|
||||
|
||||
Reference in New Issue
Block a user