fix
diff --git a/ChangeLog b/ChangeLog
index faf3559..c44e8ff 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2005-08-25  Miklos Szeredi <miklos@szeredi.hu>
+
+	* lib: add userspace side of ftruncate() method for experimentation
+
 2005-08-23  Miklos Szeredi <miklos@szeredi.hu>
 
 	* lib: add userspace side of create() method for experimentation
diff --git a/example/fusexmp_fh.c b/example/fusexmp_fh.c
index a1d2283..17bd77e 100644
--- a/example/fusexmp_fh.c
+++ b/example/fusexmp_fh.c
@@ -193,6 +193,20 @@
     return 0;
 }
 
+static int xmp_ftruncate(const char *path, off_t size,
+                         struct fuse_file_info *fi)
+{
+    int res;
+
+    (void) path;
+
+    res = ftruncate(fi->fh, size);
+    if(res == -1)
+        return -errno;
+
+    return 0;
+}
+
 static int xmp_utime(const char *path, struct utimbuf *buf)
 {
     int res;
@@ -353,6 +367,7 @@
     .chmod	= xmp_chmod,
     .chown	= xmp_chown,
     .truncate	= xmp_truncate,
+    .ftruncate	= xmp_ftruncate,
     .utime	= xmp_utime,
     .open	= xmp_open,
     .read	= xmp_read,
diff --git a/example/hello_ll.c b/example/hello_ll.c
index f5a2baf..882f2cd 100644
--- a/example/hello_ll.c
+++ b/example/hello_ll.c
@@ -40,10 +40,13 @@
     return 0;
 }
 
-static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino)
+static void hello_ll_getattr(fuse_req_t req, fuse_ino_t ino,
+                             struct fuse_file_info *fi)
 {
     struct stat stbuf;
 
+    (void) fi;
+
     memset(&stbuf, 0, sizeof(stbuf));
     if (hello_stat(ino, &stbuf) == -1)
         fuse_reply_err(req, ENOENT);
diff --git a/include/fuse.h b/include/fuse.h
index 6251158..39982dc 100644
--- a/include/fuse.h
+++ b/include/fuse.h
@@ -313,6 +313,8 @@
     int (*access) (const char *, int);
 
     int (*create) (const char *, mode_t, struct fuse_file_info *);
+
+    int (*ftruncate) (const char *, off_t, struct fuse_file_info *);
 };
 
 /** Extra context that may be needed by some filesystems
diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h
index a8e3e3e..a1c475e 100644
--- a/include/fuse_lowlevel.h
+++ b/include/fuse_lowlevel.h
@@ -78,9 +78,9 @@
 
     void (*lookup) (fuse_req_t req, fuse_ino_t parent, const char *name);
     void (*forget) (fuse_req_t req, fuse_ino_t ino, unsigned long nlookup);
-    void (*getattr)(fuse_req_t req, fuse_ino_t ino);
+    void (*getattr)(fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi);
     void (*setattr)(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
-                     int to_set);
+                    int to_set, struct fuse_file_info *fi);
     void (*access) (fuse_req_t req, fuse_ino_t ino, int mask);
     void (*readlink)(fuse_req_t req, fuse_ino_t ino);
     void (*mknod)  (fuse_req_t req, fuse_ino_t parent, const char *name,
diff --git a/kernel/fuse_kernel.h b/kernel/fuse_kernel.h
index aa6b298..e1d8a0c 100644
--- a/kernel/fuse_kernel.h
+++ b/kernel/fuse_kernel.h
@@ -107,7 +107,8 @@
 	FUSE_SETLK         = 32,
 	FUSE_SETLKW        = 33,
 	FUSE_ACCESS        = 34,
-	FUSE_CREATE        = 35
+	FUSE_CREATE        = 35,
+	FUSE_FSETATTR      = 36
 };
 
 /* Conservative buffer size for the client */
@@ -163,6 +164,11 @@
 	struct fuse_attr attr;
 };
 
+struct fuse_fsetattr_in {
+	__u64	fh;
+	struct fuse_setattr_in setattr;
+};
+
 struct fuse_open_in {
 	__u32	flags;
 	__u32	mode;
diff --git a/lib/fuse.c b/lib/fuse.c
index 237dee1..7f8ed3d 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -612,13 +612,16 @@
     fuse_reply_none(req);
 }
 
-static void fuse_getattr(fuse_req_t req, fuse_ino_t ino)
+static void fuse_getattr(fuse_req_t req, fuse_ino_t ino,
+                         struct fuse_file_info *fi)
 {
     struct fuse *f = req_fuse_prepare(req);
     struct stat buf;
     char *path;
     int err;
 
+    (void) fi;
+
     err = -ENOENT;
     pthread_rwlock_rdlock(&f->tree_lock);
     path = get_path(f, ino);
@@ -661,12 +664,15 @@
     return err;
 }
 
-static int do_truncate(struct fuse *f, const char *path, struct stat *attr)
+static int do_truncate(struct fuse *f, const char *path, struct stat *attr,
+                       struct fuse_file_info *fi)
 {
     int err;
 
     err = -ENOSYS;
-    if (f->op.truncate)
+    if (fi && f->op.ftruncate)
+        err = f->op.ftruncate(path, attr->st_size, fi);
+    else if (f->op.truncate)
         err = f->op.truncate(path, attr->st_size);
 
     return err;
@@ -686,7 +692,7 @@
 }
 
 static void fuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr,
-                         int valid)
+                         int valid, struct fuse_file_info *fi)
 {
     struct fuse *f = req_fuse_prepare(req);
     struct stat buf;
@@ -705,7 +711,7 @@
             if (!err && (valid & (FUSE_SET_ATTR_UID | FUSE_SET_ATTR_GID)))
                 err = do_chown(f, path, attr, valid);
             if (!err && (valid & FUSE_SET_ATTR_SIZE))
-                err = do_truncate(f, path, attr);
+                err = do_truncate(f, path, attr, fi);
             if (!err && (valid & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) == (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME))
                 err = do_utime(f, path, attr);
             if (!err)
diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c
index 893f723..3bbc296 100644
--- a/lib/fuse_lowlevel.c
+++ b/lib/fuse_lowlevel.c
@@ -74,6 +74,7 @@
     case FUSE_SETLKW:		return "SETLKW";
     case FUSE_ACCESS:		return "ACCESS";
     case FUSE_CREATE:		return "CREATE";
+    case FUSE_FSETATTR:		return "FSETATTR";
     default: 			return "???";
     }
 }
@@ -394,23 +395,33 @@
 static void do_getattr(fuse_req_t req, fuse_ino_t nodeid)
 {
     if (req->f->op.getattr)
-        req->f->op.getattr(req, nodeid);
+        req->f->op.getattr(req, nodeid, NULL);
     else
         fuse_reply_err(req, ENOSYS);
 }
 
 static void do_setattr(fuse_req_t req, fuse_ino_t nodeid,
-                       struct fuse_setattr_in *arg)
+                       struct fuse_setattr_in *arg, struct fuse_file_info *fi)
 {
     if (req->f->op.setattr) {
         struct stat stbuf;
         memset(&stbuf, 0, sizeof(stbuf));
         convert_attr(&arg->attr, &stbuf);
-        req->f->op.setattr(req, nodeid, &stbuf, arg->valid);
+        req->f->op.setattr(req, nodeid, &stbuf, arg->valid, fi);
     } else
         fuse_reply_err(req, ENOSYS);
 }
 
+static void do_fsetattr(fuse_req_t req, fuse_ino_t nodeid,
+                       struct fuse_fsetattr_in *arg)
+{
+    struct fuse_file_info fi;
+
+    memset(&fi, 0, sizeof(fi));
+    fi.fh = arg->fh;
+    do_setattr(req, nodeid, &arg->setattr, &fi);
+}
+
 static void do_access(fuse_req_t req, fuse_ino_t nodeid,
                       struct fuse_access_in *arg)
 {
@@ -813,7 +824,11 @@
         break;
 
     case FUSE_SETATTR:
-        do_setattr(req, in->nodeid, (struct fuse_setattr_in *) inarg);
+        do_setattr(req, in->nodeid, (struct fuse_setattr_in *) inarg, NULL);
+        break;
+
+    case FUSE_FSETATTR:
+        do_fsetattr(req, in->nodeid, (struct fuse_fsetattr_in *) inarg);
         break;
 
     case FUSE_READLINK:
diff --git a/test/test.c b/test/test.c
index 0f409ad..3fba745 100644
--- a/test/test.c
+++ b/test/test.c
@@ -364,7 +364,6 @@
     return 0;
 }
 
-
 int test_truncate(int len)
 {
     const char *data = testdata;
@@ -412,6 +411,73 @@
     return 0;
 }
 
+int test_ftruncate(int len, int mode)
+{
+    const char *data = testdata;
+    int datalen = testdatalen;
+    int res;
+    int fd;
+
+    start_test("ftruncate(%u) mode: 0%03o", len, mode);
+    res = create_file(testfile, data, datalen);
+    if (res == -1)
+        return -1;
+
+    fd = open(testfile, O_WRONLY);
+    if (fd == -1) {
+        PERROR("open");
+        return -1;
+    }
+
+    res = fchmod(fd, mode);
+    if (res == -1) {
+        PERROR("fchmod");
+        close(fd);
+        return -1;
+    }
+    res = check_mode(testfile, mode);
+    if (res == -1) {
+        close(fd);
+        return -1;
+    }
+    res = ftruncate(fd, len);
+    if (res == -1) {
+        PERROR("ftruncate");
+        close(fd);
+        return -1;
+    }
+    close(fd);
+    res = check_size(testfile, len);
+    if (res == -1)
+        return -1;
+
+    if (len > 0) {
+        if (len <= datalen) {
+            res = check_data(testfile, data, 0, len);
+            if (res == -1)
+                return -1;
+        } else {
+            res = check_data(testfile, data, 0, datalen);
+            if (res == -1)
+                return -1;
+            res = check_data(testfile, zerodata, datalen, len - datalen);
+            if (res == -1)
+                return -1;
+        }
+    }
+    res = unlink(testfile);
+    if (res == -1) {
+        PERROR("unlink");
+        return -1;
+    }
+    res = check_nonexist(testfile2);
+    if (res == -1)
+        return -1;
+
+    success();
+    return 0;
+}
+
 static int test_create(void)
 {
     const char *data = testdata;
@@ -612,6 +678,49 @@
     return 0;
 }
 
+#define test_open_acc(flags, mode, err)  do_test_open_acc(flags, #flags, mode, err)
+
+static int do_test_open_acc(int flags, const char *flags_str, int mode, int err)
+{
+    const char *data = testdata;
+    int datalen = testdatalen;
+    int res;
+    int fd;
+
+    start_test("open_acc(%s) mode: 0%03o error: '%s'", flags_str, mode,
+               strerror(err));
+    unlink(testfile);
+    res = create_file(testfile, data, datalen);
+    if (res == -1)
+        return -1;
+
+    res = chmod(testfile, mode);
+    if (res == -1) {
+        PERROR("chmod");
+        return -1;
+    }
+    
+    res = check_mode(testfile, mode);
+    if (res == -1)
+        return -1;
+
+    fd = open(testfile, flags);
+    if (fd == -1) {
+        if (err != errno) {
+            PERROR("open");
+            return -1;
+        }
+    } else {
+        if (err) {
+            ERROR("open should have failed");
+            close(fd);
+            return -1;
+        }
+        close(fd);
+    }
+    success();
+    return 0;
+}
 
 static int test_symlink(void)
 {
@@ -849,6 +958,13 @@
     err += test_truncate(testdatalen / 2);
     err += test_truncate(testdatalen);
     err += test_truncate(testdatalen + 100);
+    err += test_ftruncate(0, 0600);
+    err += test_ftruncate(testdatalen / 2, 0600);
+    err += test_ftruncate(testdatalen, 0600);
+    err += test_ftruncate(testdatalen + 100, 0600);
+    err += test_ftruncate(0, 0400);
+    err += test_ftruncate(0, 0200);
+    err += test_ftruncate(0, 0000);
     err += test_open(0, O_RDONLY, 0);
     err += test_open(1, O_RDONLY, 0);
     err += test_open(1, O_RDWR, 0);
@@ -872,6 +988,19 @@
     err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0600);
     err += test_open(0, O_RDWR | O_CREAT | O_EXCL, 0000);
     err += test_open(1, O_RDWR | O_CREAT | O_EXCL, 0000);
+    err += test_open_acc(O_RDONLY, 0600, 0);
+    err += test_open_acc(O_WRONLY, 0600, 0);
+    err += test_open_acc(O_RDWR,   0600, 0);
+    err += test_open_acc(O_RDONLY, 0400, 0);
+    err += test_open_acc(O_RDONLY | O_TRUNC, 0400, EACCES);
+    err += test_open_acc(O_WRONLY, 0400, EACCES);
+    err += test_open_acc(O_RDWR,   0400, EACCES);
+    err += test_open_acc(O_RDONLY, 0200, EACCES);
+    err += test_open_acc(O_WRONLY, 0200, 0);
+    err += test_open_acc(O_RDWR,   0200, EACCES);
+    err += test_open_acc(O_RDONLY, 0000, EACCES);
+    err += test_open_acc(O_WRONLY, 0000, EACCES);
+    err += test_open_acc(O_RDWR,   0000, EACCES);
     
     unlink(testfile);
     unlink(testfile2);