+2005-12-06 Miklos Szeredi <miklos@szeredi.hu>
+
+ * Use bigger request buffer size. write() did not work on archs
+ with > 4k page size, Bug report by Mark Haney
+
+ * ABI version 7.5:
+
+ * Extend INIT reply with data size limits
+
2005-12-02 Miklos Szeredi <miklos@szeredi.hu>
* Fix memory leak in fuse_read_cmd()/fuse_process_cmd(). Bug
spin_unlock(&fuse_lock);
}
+static void process_init_reply(struct fuse_conn *fc, struct fuse_req *req)
+{
+ int i;
+ struct fuse_init_out *arg = &req->misc.init_out;
+
+ if (arg->major != FUSE_KERNEL_VERSION)
+ fc->conn_error = 1;
+ else {
+ fc->minor = arg->minor;
+ if (fc->minor >= 5) {
+ fc->name_max = arg->name_max;
+ fc->symlink_max = arg->symlink_max;
+ fc->xattr_size_max = arg->xattr_size_max;
+ fc->max_write = arg->max_write;
+ } else {
+ /* Old fix values */
+ fc->name_max = 1024;
+ fc->symlink_max = 4096;
+ fc->xattr_size_max = 4096;
+ fc->max_write = 4096;
+ }
+ }
+
+ /* After INIT reply is received other requests can go
+ out. So do (FUSE_MAX_OUTSTANDING - 1) number of
+ up()s on outstanding_sem. The last up() is done in
+ fuse_putback_request() */
+ for (i = 1; i < FUSE_MAX_OUTSTANDING; i++)
+ up(&fc->outstanding_sem);
+}
+
/*
* This function is called when a request is finished. Either a reply
* has arrived or it was interrupted (and not yet sent) or some error
up_read(&fc->sbput_sem);
}
wake_up(&req->waitq);
- if (req->in.h.opcode == FUSE_INIT) {
- int i;
-
- if (req->misc.init_in_out.major != FUSE_KERNEL_VERSION)
- fc->conn_error = 1;
-
- fc->minor = req->misc.init_in_out.minor;
-
- /* After INIT reply is received other requests can go
- out. So do (FUSE_MAX_OUTSTANDING - 1) number of
- up()s on outstanding_sem. The last up() is done in
- fuse_putback_request() */
- for (i = 1; i < FUSE_MAX_OUTSTANDING; i++)
- up(&fc->outstanding_sem);
- } else if (req->in.h.opcode == FUSE_RELEASE && req->inode == NULL) {
+ if (req->in.h.opcode == FUSE_INIT)
+ process_init_reply(fc, req);
+ else if (req->in.h.opcode == FUSE_RELEASE && req->inode == NULL) {
/* Special case for failed iget in CREATE */
u64 nodeid = req->in.h.nodeid;
__fuse_get_request(req);
/* This is called from fuse_read_super() so there's guaranteed
to be a request available */
struct fuse_req *req = do_get_request(fc);
- struct fuse_init_in_out *arg = &req->misc.init_in_out;
+ struct fuse_init_in *arg = &req->misc.init_in;
arg->major = FUSE_KERNEL_VERSION;
arg->minor = FUSE_KERNEL_MINOR_VERSION;
req->in.h.opcode = FUSE_INIT;
req->in.args[0].size = sizeof(*arg);
req->in.args[0].value = arg;
req->out.numargs = 1;
- req->out.args[0].size = sizeof(*arg);
- req->out.args[0].value = arg;
+ /* Variable length arguement used for backward compatibility
+ with interface version < 7.5. Rest of init_out is zeroed
+ by do_get_request(), so a short reply is not a problem */
+ req->out.argvar = 1;
+ req->out.args[0].size = sizeof(struct fuse_init_out);
+ req->out.args[0].value = &req->misc.init_out;
request_send_background(fc, req);
}
struct dentry *newent;
#endif
- if (entry->d_name.len > FUSE_NAME_MAX)
+ if (entry->d_name.len > fc->name_max)
return ERR_PTR(-ENAMETOOLONG);
req = fuse_get_request(fc);
goto out;
err = -ENAMETOOLONG;
- if (entry->d_name.len > FUSE_NAME_MAX)
+ if (entry->d_name.len > fc->name_max)
goto out;
err = -EINTR;
unsigned len = strlen(link) + 1;
struct fuse_req *req;
- if (len > FUSE_SYMLINK_MAX)
+ if (len > fc->symlink_max)
return -ENAMETOOLONG;
req = fuse_get_request(fc);
fuse_invalidate_attr(newdir);
/* newent will end up negative */
- fuse_invalidate_entry_cache(newent);
+ if (newent->d_inode)
+ fuse_invalidate_entry_cache(newent);
} else if (err == -EINTR) {
/* If request was interrupted, DEITY only knows if the
rename actually took place. If the invalidation
static int parse_dirfile(char *buf, size_t nbytes, struct file *file,
void *dstbuf, filldir_t filldir)
{
+ struct fuse_conn *fc = get_fuse_conn(file->f_dentry->d_inode);
+
while (nbytes >= FUSE_NAME_OFFSET) {
struct fuse_dirent *dirent = (struct fuse_dirent *) buf;
size_t reclen = FUSE_DIRENT_SIZE(dirent);
int over;
- if (!dirent->namelen || dirent->namelen > FUSE_NAME_MAX)
+ if (!dirent->namelen || dirent->namelen > fc->name_max)
return -EIO;
if (reclen > nbytes)
break;
struct fuse_setxattr_in inarg;
int err;
- if (size > FUSE_XATTR_SIZE_MAX)
+ if (size > fc->xattr_size_max)
return -E2BIG;
if (fc->no_setxattr)
struct inode *inode = page->mapping->host;
struct fuse_conn *fc = get_fuse_conn(inode);
loff_t pos = page_offset(page) + offset;
- struct fuse_req *req = fuse_get_request(fc);
+ struct fuse_req *req;
+
+ if (count > fc->max_write)
+ return -EIO;
+
+ req = fuse_get_request(fc);
if (!req)
return -EINTR;
union {
struct fuse_forget_in forget_in;
struct fuse_release_in release_in;
- struct fuse_init_in_out init_in_out;
+ struct fuse_init_in init_in;
+ struct fuse_init_out init_out;
} misc;
/** page vector */
/** Maximum write size */
unsigned max_write;
+ /** Maximum path segment length */
+ unsigned name_max;
+
+ /** Maximum symbolic link size */
+ unsigned symlink_max;
+
+ /** Maximum size of xattr data */
+ unsigned xattr_size_max;
+
/** Readers of the connection are waiting on this */
wait_queue_head_t waitq;
#define FUSE_KERNEL_VERSION 7
/** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 4
+#define FUSE_KERNEL_MINOR_VERSION 5
/** The node ID of the root inode */
#define FUSE_ROOT_ID 1
FUSE_CREATE = 35
};
-/* Conservative buffer size for the client */
-#define FUSE_MAX_IN 8192
-
-#define FUSE_NAME_MAX 1024
-#define FUSE_SYMLINK_MAX 4096
-#define FUSE_XATTR_SIZE_MAX 4096
-
struct fuse_entry_out {
__u64 nodeid; /* Inode ID */
__u64 generation; /* Inode generation: nodeid:gen must
__u32 padding;
};
-struct fuse_init_in_out {
+struct fuse_init_in {
+ __u32 major;
+ __u32 minor;
+};
+
+struct fuse_init_out {
__u32 major;
__u32 minor;
+ __u32 name_max;
+ __u32 symlink_max;
+ __u32 xattr_size_max;
+ __u32 max_write;
};
struct fuse_in_header {
if (fc->max_read / PAGE_CACHE_SIZE < fc->bdi.ra_pages)
fc->bdi.ra_pages = fc->max_read / PAGE_CACHE_SIZE;
#endif
- fc->max_write = FUSE_MAX_IN / 2;
err = -ENOMEM;
root = get_root_inode(sb, d.rootmode);
close(fuse_chan_fd(ch));
}
+#define MIN_BUFSIZE 0x21000
+
struct fuse_chan *fuse_kern_chan_new(int fd)
{
struct fuse_chan_ops op = {
.send = fuse_kern_chan_send,
.destroy = fuse_kern_chan_destroy,
};
- return fuse_chan_new(&op, fd, FUSE_MAX_IN, NULL);
+ size_t bufsize = getpagesize() + 0x1000;
+ bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE : bufsize;
+ return fuse_chan_new(&op, fd, bufsize, NULL);
}
memset(w, 0, sizeof(struct fuse_worker));
w->se = se;
w->prevch = fuse_session_next_chan(se, NULL);
- w->ch = fuse_chan_new(&cop, -1, 0, w);
+ w->ch = fuse_chan_new(&cop, -1, fuse_chan_bufsize(w->prevch), w);
if (w->ch == NULL) {
free(w);
return -1;
#define PARAM(inarg) (((char *)(inarg)) + sizeof(*(inarg)))
+/* PATH_MAX is 4k on Linux, but I don't dare to define it to PATH_MAX,
+ because it may be much larger on other systems */
+#define MIN_SYMLINK 0x1000
+
+/* Generous 4k overhead for headers, includes room for xattr name
+ (XATTR_NAME_MAX = 255) */
+#define HEADER_OVERHEAD 0x1000
+
+/* 8k, the same as the old FUSE_MAX_IN constant */
+#define MIN_BUFFER_SIZE (MIN_SYMLINK + HEADER_OVERHEAD)
+
struct fuse_ll {
unsigned int debug : 1;
unsigned int allow_root : 1;
fuse_reply_err(req, ENOSYS);
}
-static void do_init(fuse_req_t req, struct fuse_init_in_out *arg)
+static void do_init(fuse_req_t req, struct fuse_init_in *arg)
{
- struct fuse_init_in_out outarg;
+ struct fuse_init_out outarg;
struct fuse_ll *f = req->f;
+ size_t bufsize = fuse_chan_bufsize(req->ch);
if (f->debug) {
printf("INIT: %u.%u\n", arg->major, arg->minor);
f->major = FUSE_KERNEL_VERSION;
f->minor = arg->minor;
+ if (bufsize < MIN_BUFFER_SIZE) {
+ fprintf(stderr, "fuse: warning: buffer size too small: %i\n", bufsize);
+ bufsize = MIN_BUFFER_SIZE;
+ }
+
+ bufsize -= HEADER_OVERHEAD;
+
memset(&outarg, 0, sizeof(outarg));
outarg.major = f->major;
outarg.minor = FUSE_KERNEL_MINOR_VERSION;
+ /* The calculated limits may be oversized, but because of the
+ limits in VFS names and symlinks are never larger than PATH_MAX - 1
+ and xattr values never larger than XATTR_SIZE_MAX */
+
+ /* Max two names per request */
+ outarg.symlink_max = outarg.name_max = bufsize / 2;
+ /* But if buffer is small, give more room to link name */
+ if (outarg.symlink_max < MIN_SYMLINK) {
+ outarg.symlink_max = MIN_SYMLINK;
+ /* Borrow from header overhead for the SYMLINK operation */
+ outarg.name_max = HEADER_OVERHEAD / 4;
+ }
+ outarg.xattr_size_max = outarg.max_write = bufsize;
+
if (f->debug) {
printf(" INIT: %u.%u\n", outarg.major, outarg.minor);
fflush(stdout);
}
- send_reply_ok(req, &outarg, sizeof(outarg));
+ send_reply_ok(req, &outarg, arg->minor < 5 ? 8 : sizeof(outarg));
}
void *fuse_req_userdata(fuse_req_t req)
fuse_reply_err(req, EACCES);
} else switch (in->opcode) {
case FUSE_INIT:
- do_init(req, (struct fuse_init_in_out *) inarg);
+ do_init(req, (struct fuse_init_in *) inarg);
break;
case FUSE_LOOKUP: