mirror of
https://github.com/libfuse/libfuse.git
synced 2024-11-23 04:04:31 +08:00
libfuse: add readdirplus support in fuse_lowlevel_ops
This patch implements readdirplus support in FUSE usersapce. It adds a new fuse lowlevel operations fuse_lowleve_ops::readdir_plus, corespoding mount options and helper functions to maintain buffer. [From: Eric Wong <normalperson@yhbt.net>] This makes our terminology consistent with NFS and our kernel module, as well as reducing user/developer confusion in the command-line. Note: I'm keeping "fuse_add_direntry_plus" since that is less standardized in its use than "readdirplus" for now. Signed-off-by: Feng Shuo <steve.shuo.feng@gmail.com>
This commit is contained in:
parent
914871b20a
commit
f448ac69d1
@ -1,3 +1,8 @@
|
||||
2013-02-07 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* libfuse: add readdirplus support in fuse_lowlevel_ops. Patch by
|
||||
Feng Shuo
|
||||
|
||||
2013-02-06 Miklos Szeredi <miklos@szeredi.hu>
|
||||
|
||||
* libfuse: set close-on-exec flag on pipe file descriptors. Patch
|
||||
|
@ -112,6 +112,7 @@ struct fuse_file_info {
|
||||
#define FUSE_CAP_FLOCK_LOCKS (1 << 10)
|
||||
#define FUSE_CAP_IOCTL_DIR (1 << 11)
|
||||
#define FUSE_CAP_AUTO_INVAL_DATA (1 << 12)
|
||||
#define FUSE_CAP_READDIRPLUS (1 << 13)
|
||||
|
||||
/**
|
||||
* Ioctl flags
|
||||
|
@ -86,6 +86,9 @@
|
||||
*
|
||||
* 7.20
|
||||
* - add FUSE_AUTO_INVAL_DATA
|
||||
*
|
||||
* 7.21
|
||||
* - add FUSE_READDIRPLUS
|
||||
*/
|
||||
|
||||
#ifndef _LINUX_FUSE_H
|
||||
@ -126,7 +129,7 @@
|
||||
#define FUSE_KERNEL_VERSION 7
|
||||
|
||||
/** Minor version number of this interface */
|
||||
#define FUSE_KERNEL_MINOR_VERSION 20
|
||||
#define FUSE_KERNEL_MINOR_VERSION 21
|
||||
|
||||
/** The node ID of the root inode */
|
||||
#define FUSE_ROOT_ID 1
|
||||
@ -228,6 +231,7 @@ struct fuse_file_lock {
|
||||
#define FUSE_FLOCK_LOCKS (1 << 10)
|
||||
#define FUSE_HAS_IOCTL_DIR (1 << 11)
|
||||
#define FUSE_AUTO_INVAL_DATA (1 << 12)
|
||||
#define FUSE_DO_READDIRPLUS (1 << 13)
|
||||
|
||||
/**
|
||||
* CUSE INIT request/reply flags
|
||||
@ -334,6 +338,7 @@ enum fuse_opcode {
|
||||
FUSE_NOTIFY_REPLY = 41,
|
||||
FUSE_BATCH_FORGET = 42,
|
||||
FUSE_FALLOCATE = 43,
|
||||
FUSE_READDIRPLUS = 44,
|
||||
|
||||
/* CUSE specific operations */
|
||||
CUSE_INIT = 4096,
|
||||
@ -665,6 +670,16 @@ struct fuse_dirent {
|
||||
#define FUSE_DIRENT_SIZE(d) \
|
||||
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
|
||||
|
||||
struct fuse_direntplus {
|
||||
struct fuse_entry_out entry_out;
|
||||
struct fuse_dirent dirent;
|
||||
};
|
||||
|
||||
#define FUSE_NAME_OFFSET_DIRENTPLUS \
|
||||
offsetof(struct fuse_direntplus, dirent.name)
|
||||
#define FUSE_DIRENTPLUS_SIZE(d) \
|
||||
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS + (d)->dirent.namelen)
|
||||
|
||||
struct fuse_notify_inval_inode_out {
|
||||
__u64 ino;
|
||||
__s64 off;
|
||||
|
@ -1016,6 +1016,32 @@ struct fuse_lowlevel_ops {
|
||||
*/
|
||||
void (*fallocate) (fuse_req_t req, fuse_ino_t ino, int mode,
|
||||
off_t offset, off_t length, struct fuse_file_info *fi);
|
||||
|
||||
/**
|
||||
* Read directory with attributes
|
||||
*
|
||||
* Send a buffer filled using fuse_add_direntry_plus(), with size not
|
||||
* exceeding the requested size. Send an empty buffer on end of
|
||||
* stream.
|
||||
*
|
||||
* fi->fh will contain the value set by the opendir method, or
|
||||
* will be undefined if the opendir method didn't set any value.
|
||||
*
|
||||
* Introduced in version 3.0
|
||||
*
|
||||
* Valid replies:
|
||||
* fuse_reply_buf
|
||||
* fuse_reply_data
|
||||
* fuse_reply_err
|
||||
*
|
||||
* @param req request handle
|
||||
* @param ino the inode number
|
||||
* @param size maximum number of bytes to send
|
||||
* @param off offset to continue reading the directory stream
|
||||
* @param fi file information
|
||||
*/
|
||||
void (*readdirplus) (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
|
||||
struct fuse_file_info *fi);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1251,6 +1277,34 @@ size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize,
|
||||
const char *name, const struct stat *stbuf,
|
||||
off_t off);
|
||||
|
||||
/**
|
||||
* Add a directory entry to the buffer with the attributes
|
||||
*
|
||||
* Buffer needs to be large enough to hold the entry. If it's not,
|
||||
* then the entry is not filled in but the size of the entry is still
|
||||
* returned. The caller can check this by comparing the bufsize
|
||||
* parameter with the returned entry size. If the entry size is
|
||||
* larger than the buffer size, the operation failed.
|
||||
*
|
||||
* From the 'stbuf' argument the st_ino field and bits 12-15 of the
|
||||
* st_mode field are used. The other fields are ignored.
|
||||
*
|
||||
* Note: offsets do not necessarily represent physical offsets, and
|
||||
* could be any marker, that enables the implementation to find a
|
||||
* specific point in the directory stream.
|
||||
*
|
||||
* @param req request handle
|
||||
* @param buf the point where the new entry will be added to the buffer
|
||||
* @param bufsize remaining size of the buffer
|
||||
* @param name the name of the entry
|
||||
* @param e the directory entry
|
||||
* @param off the offset of the next entry
|
||||
* @return the space needed for the entry
|
||||
*/
|
||||
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
|
||||
const char *name,
|
||||
const struct fuse_entry_param *e, off_t off);
|
||||
|
||||
/**
|
||||
* Reply to ask for data fetch and output buffer preparation. ioctl
|
||||
* will be retried with the specified input data fetched and output
|
||||
|
@ -73,6 +73,7 @@ struct fuse_ll {
|
||||
int no_splice_read;
|
||||
int auto_inval_data;
|
||||
int no_auto_inval_data;
|
||||
int no_readdirplus;
|
||||
struct fuse_lowlevel_ops op;
|
||||
int got_init;
|
||||
struct cuse_data *cuse_data;
|
||||
|
@ -339,6 +339,25 @@ static void fill_entry(struct fuse_entry_out *arg,
|
||||
convert_stat(&e->attr, &arg->attr);
|
||||
}
|
||||
|
||||
size_t fuse_add_direntry_plus(fuse_req_t req, char *buf, size_t bufsize,
|
||||
const char *name,
|
||||
const struct fuse_entry_param *e, off_t off)
|
||||
{
|
||||
struct fuse_entry_out *argp;
|
||||
size_t entsize;
|
||||
|
||||
(void) req;
|
||||
entsize = FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET_DIRENTPLUS +
|
||||
fuse_dirent_size(strlen(name)));
|
||||
if (entsize <= bufsize && buf){
|
||||
argp = (struct fuse_entry_out *)buf;
|
||||
memset(argp, 0, sizeof(*argp));
|
||||
fill_entry(argp, e);
|
||||
fuse_add_dirent(buf + sizeof(*argp), name, &(e->attr), off);
|
||||
}
|
||||
return entsize;
|
||||
}
|
||||
|
||||
static void fill_open(struct fuse_open_out *arg,
|
||||
const struct fuse_file_info *f)
|
||||
{
|
||||
@ -1406,6 +1425,21 @@ static void do_readdir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
|
||||
static void do_readdirplus(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
||||
{
|
||||
struct fuse_read_in *arg = (struct fuse_read_in *) inarg;
|
||||
struct fuse_file_info fi;
|
||||
|
||||
memset(&fi, 0, sizeof(fi));
|
||||
fi.fh = arg->fh;
|
||||
fi.fh_old = fi.fh;
|
||||
|
||||
if (req->f->op.readdirplus)
|
||||
req->f->op.readdirplus(req, nodeid, arg->size, arg->offset, &fi);
|
||||
else
|
||||
fuse_reply_err(req, ENOSYS);
|
||||
}
|
||||
|
||||
static void do_releasedir(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
||||
{
|
||||
struct fuse_release_in *arg = (struct fuse_release_in *) inarg;
|
||||
@ -1806,6 +1840,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
||||
f->conn.capable |= FUSE_CAP_FLOCK_LOCKS;
|
||||
if (arg->flags & FUSE_AUTO_INVAL_DATA)
|
||||
f->conn.capable |= FUSE_CAP_AUTO_INVAL_DATA;
|
||||
if (arg->flags & FUSE_DO_READDIRPLUS)
|
||||
f->conn.capable |= FUSE_CAP_READDIRPLUS;
|
||||
} else {
|
||||
f->conn.async_read = 0;
|
||||
f->conn.max_readahead = 0;
|
||||
@ -1838,6 +1874,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
||||
f->conn.want |= FUSE_CAP_BIG_WRITES;
|
||||
if (f->auto_inval_data)
|
||||
f->conn.want |= FUSE_CAP_AUTO_INVAL_DATA;
|
||||
if (f->op.readdirplus && !f->no_readdirplus)
|
||||
f->conn.want |= FUSE_CAP_READDIRPLUS;
|
||||
|
||||
if (bufsize < FUSE_MIN_READ_BUFFER) {
|
||||
fprintf(stderr, "fuse: warning: buffer size too small: %zu\n",
|
||||
@ -1861,6 +1899,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
||||
f->conn.want &= ~FUSE_CAP_SPLICE_MOVE;
|
||||
if (f->no_auto_inval_data)
|
||||
f->conn.want &= ~FUSE_CAP_AUTO_INVAL_DATA;
|
||||
if (f->no_readdirplus)
|
||||
f->conn.want &= ~FUSE_CAP_READDIRPLUS;
|
||||
|
||||
if (f->conn.async_read || (f->conn.want & FUSE_CAP_ASYNC_READ))
|
||||
outarg.flags |= FUSE_ASYNC_READ;
|
||||
@ -1878,6 +1918,8 @@ static void do_init(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
|
||||
outarg.flags |= FUSE_FLOCK_LOCKS;
|
||||
if (f->conn.want & FUSE_CAP_AUTO_INVAL_DATA)
|
||||
outarg.flags |= FUSE_AUTO_INVAL_DATA;
|
||||
if (f->conn.want & FUSE_CAP_READDIRPLUS)
|
||||
outarg.flags |= FUSE_DO_READDIRPLUS;
|
||||
outarg.max_readahead = f->conn.max_readahead;
|
||||
outarg.max_write = f->conn.max_write;
|
||||
if (f->conn.proto_minor >= 13) {
|
||||
@ -2294,6 +2336,7 @@ static struct {
|
||||
[FUSE_DESTROY] = { do_destroy, "DESTROY" },
|
||||
[FUSE_NOTIFY_REPLY] = { (void *) 1, "NOTIFY_REPLY" },
|
||||
[FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
|
||||
[FUSE_READDIRPLUS] = { do_readdirplus, "READDIRPLUS"},
|
||||
[CUSE_INIT] = { cuse_lowlevel_init, "CUSE_INIT" },
|
||||
};
|
||||
|
||||
@ -2402,7 +2445,8 @@ static void fuse_ll_process_buf(void *data, const struct fuse_buf *buf,
|
||||
in->opcode != FUSE_WRITE && in->opcode != FUSE_FSYNC &&
|
||||
in->opcode != FUSE_RELEASE && in->opcode != FUSE_READDIR &&
|
||||
in->opcode != FUSE_FSYNCDIR && in->opcode != FUSE_RELEASEDIR &&
|
||||
in->opcode != FUSE_NOTIFY_REPLY)
|
||||
in->opcode != FUSE_NOTIFY_REPLY &&
|
||||
in->opcode != FUSE_READDIRPLUS)
|
||||
goto reply_err;
|
||||
|
||||
err = ENOSYS;
|
||||
@ -2501,6 +2545,7 @@ static const struct fuse_opt fuse_ll_opts[] = {
|
||||
{ "no_splice_read", offsetof(struct fuse_ll, no_splice_read), 1},
|
||||
{ "auto_inval_data", offsetof(struct fuse_ll, auto_inval_data), 1},
|
||||
{ "no_auto_inval_data", offsetof(struct fuse_ll, no_auto_inval_data), 1},
|
||||
{ "no_readdirplus", offsetof(struct fuse_ll, no_readdirplus), 1},
|
||||
FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD),
|
||||
FUSE_OPT_KEY("-h", KEY_HELP),
|
||||
FUSE_OPT_KEY("--help", KEY_HELP),
|
||||
@ -2533,6 +2578,7 @@ static void fuse_ll_help(void)
|
||||
" -o [no_]splice_move move data while splicing to the fuse device\n"
|
||||
" -o [no_]splice_read use splice to read from the fuse device\n"
|
||||
" -o [no_]auto_inval_data use automatic kernel cache invalidation logic\n"
|
||||
" -o [no_]readdirplus use readdirplus if possible.\n"
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ FUSE_3.0 {
|
||||
fuse_reply_statfs;
|
||||
fuse_set_signal_handlers;
|
||||
fuse_add_direntry;
|
||||
fuse_add_direntry_plus;
|
||||
fuse_chan_new;
|
||||
fuse_chan_recv;
|
||||
fuse_daemonize;
|
||||
|
@ -41,6 +41,7 @@ static struct {
|
||||
[FUSE_INTERRUPT] = { "INTERRUPT" },
|
||||
[FUSE_BMAP] = { "BMAP" },
|
||||
[FUSE_DESTROY] = { "DESTROY" },
|
||||
[FUSE_READDIRPLUS] = { "READDIRPLUS" },
|
||||
};
|
||||
|
||||
#define FUSE_MAXOP (sizeof(fuse_ll_ops) / sizeof(fuse_ll_ops[0]))
|
||||
|
Loading…
Reference in New Issue
Block a user