*
* 7.20
* - add FUSE_AUTO_INVAL_DATA
+ *
+ * 7.21
+ * - add FUSE_READDIRPLUS
*/
#ifndef _LINUX_FUSE_H
#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
#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
FUSE_NOTIFY_REPLY = 41,
FUSE_BATCH_FORGET = 42,
FUSE_FALLOCATE = 43,
+ FUSE_READDIRPLUS = 44,
/* CUSE specific operations */
CUSE_INIT = 4096,
#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;
*/
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);
};
/**
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
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)
{
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;
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;
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",
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;
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) {
[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" },
};
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;
{ "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),
" -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"
);
}