fix
diff --git a/ChangeLog b/ChangeLog
index cc49014..637ca23 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+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
diff --git a/kernel/dev.c b/kernel/dev.c
index d5fb2b6..4f1e8c0 100644
--- a/kernel/dev.c
+++ b/kernel/dev.c
@@ -188,6 +188,37 @@
 	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
@@ -212,21 +243,9 @@
 		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);
@@ -399,7 +418,7 @@
 	/* 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;
@@ -407,8 +426,12 @@
 	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);
 }
 
diff --git a/kernel/dir.c b/kernel/dir.c
index 991edcf..3258d42 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -202,7 +202,7 @@
 	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);
@@ -274,7 +274,7 @@
 		goto out;
 
 	err = -ENAMETOOLONG;
-	if (entry->d_name.len > FUSE_NAME_MAX)
+	if (entry->d_name.len > fc->name_max)
 		goto out;
 
 	err = -EINTR;
@@ -455,7 +455,7 @@
 	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);
@@ -561,7 +561,8 @@
 			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
@@ -798,11 +799,13 @@
 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;
@@ -1125,7 +1128,7 @@
 	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)
diff --git a/kernel/file.c b/kernel/file.c
index a4cc04d..2365d2e 100644
--- a/kernel/file.c
+++ b/kernel/file.c
@@ -516,7 +516,12 @@
 	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;
 
diff --git a/kernel/fuse_i.h b/kernel/fuse_i.h
index 9ae1eec..7f26b65 100644
--- a/kernel/fuse_i.h
+++ b/kernel/fuse_i.h
@@ -236,7 +236,8 @@
 	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 */
@@ -284,6 +285,15 @@
 	/** 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;
 
diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h
index e43153e..26fff13 100644
--- a/kernel/fuse_kernel.h
+++ b/kernel/fuse_kernel.h
@@ -49,7 +49,7 @@
 #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
@@ -143,13 +143,6 @@
 	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
@@ -283,11 +276,20 @@
 	__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 {
 	__u32	len;
 	__u32	opcode;
diff --git a/kernel/inode.c b/kernel/inode.c
index 1d56566..8c7c7d6 100644
--- a/kernel/inode.c
+++ b/kernel/inode.c
@@ -647,7 +647,6 @@
 	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);
diff --git a/lib/fuse_kern_chan.c b/lib/fuse_kern_chan.c
index d2999c5..fbe3943 100644
--- a/lib/fuse_kern_chan.c
+++ b/lib/fuse_kern_chan.c
@@ -64,6 +64,8 @@
     close(fuse_chan_fd(ch));
 }
 
+#define MIN_BUFSIZE 0x21000
+
 struct fuse_chan *fuse_kern_chan_new(int fd)
 {
     struct fuse_chan_ops op = {
@@ -71,5 +73,7 @@
         .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);
 }
diff --git a/lib/fuse_loop_mt.c b/lib/fuse_loop_mt.c
index 37f0922..3566c60 100644
--- a/lib/fuse_loop_mt.c
+++ b/lib/fuse_loop_mt.c
@@ -155,7 +155,7 @@
     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;
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 3cc49f0..0787368 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -19,6 +19,17 @@
 
 #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;
@@ -683,10 +694,11 @@
         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);
@@ -699,16 +711,37 @@
     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)
@@ -759,7 +792,7 @@
         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: