more kernel interface changes
diff --git a/ChangeLog b/ChangeLog
index cdc2579..cd1da1d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+2004-02-20  Miklos Szeredi <mszeredi@inf.bme.hu>
+
+	* more kernel interface changes:
+
+	* added nanosecond precision to file times
+
 2004-02-19  Miklos Szeredi <mszeredi@inf.bme.hu>
 
 	* statfs library API changed to match other methods.  Since this
diff --git a/include/linux/fuse.h b/include/linux/fuse.h
index 9bca31c..10ef186 100644
--- a/include/linux/fuse.h
+++ b/include/linux/fuse.h
@@ -66,11 +66,13 @@
 	unsigned int        gid;
 	unsigned int        rdev;
 	unsigned long long  size;
-	unsigned long       _dummy;
 	unsigned long       blocks;
 	unsigned long       atime;
+	unsigned long       atimensec;
 	unsigned long       mtime;
+	unsigned long       mtimensec;
 	unsigned long       ctime;
+	unsigned long       ctimensec;
 };
 
 struct fuse_kstatfs {
@@ -87,7 +89,9 @@
 #define FATTR_UID	(1 << 1)
 #define FATTR_GID	(1 << 2)
 #define FATTR_SIZE	(1 << 3)
-#define FATTR_UTIME	(1 << 4)
+#define FATTR_ATIME	(1 << 4)
+#define FATTR_MTIME	(1 << 5)
+#define FATTR_CTIME	(1 << 6)
 
 enum fuse_opcode {
 	FUSE_LOOKUP	= 1,
diff --git a/kernel/dir.c b/kernel/dir.c
index b9c26e6..c5810e9 100644
--- a/kernel/dir.c
+++ b/kernel/dir.c
@@ -47,11 +47,11 @@
 	inode->i_blocks  = attr->blocks;
 #ifdef KERNEL_2_6
 	inode->i_atime.tv_sec   = attr->atime;
-	inode->i_atime.tv_nsec  = 0;
+	inode->i_atime.tv_nsec  = attr->atimensec;
 	inode->i_mtime.tv_sec   = attr->mtime;
-	inode->i_mtime.tv_nsec  = 0;
+	inode->i_mtime.tv_nsec  = attr->mtimensec;
 	inode->i_ctime.tv_sec   = attr->ctime;
-	inode->i_ctime.tv_nsec  = 0;
+	inode->i_ctime.tv_nsec  = attr->ctimensec;
 #else
 	inode->i_atime   = attr->atime;
 	inode->i_mtime   = attr->mtime;
@@ -261,43 +261,46 @@
 	return lookup_new_entry(dir, entry, &outarg, out.h.unique, S_IFLNK);
 }
 
-static int fuse_remove(struct inode *dir, struct dentry *entry, 
-		       enum fuse_opcode op)
+static int fuse_unlink(struct inode *dir, struct dentry *entry)
 {
 	struct fuse_conn *fc = INO_FC(dir);
 	struct fuse_in in = FUSE_IN_INIT;
 	struct fuse_out out = FUSE_OUT_INIT;
 
-	in.h.opcode = op;
+	in.h.opcode = FUSE_UNLINK;
 	in.h.ino = dir->i_ino;
 	in.numargs = 1;
 	in.args[0].size = entry->d_name.len + 1;
 	in.args[0].value = entry->d_name.name;
 	request_send(fc, &in, &out);
 
-	return out.h.error;
-}
-
-static int fuse_unlink(struct inode *dir, struct dentry *entry)
-{
-	int err = fuse_remove(dir, entry, FUSE_UNLINK);
-	if(!err) {
-		/* FIXME: the new i_nlink could be returned by the
-                   unlink operation */
-		err = fuse_do_getattr(entry->d_inode);
-		if(err == -ENOENT)
-			entry->d_inode->i_nlink = 0;
-		return 0;
+	if(!out.h.error) {
+		/* Set nlink to zero so the inode can be cleared, if
+                   the inode does have more links this will be
+                   discovered at the next lookup/getattr */
+		/* FIXME: mark inode "not uptodate" */
+		entry->d_inode->i_nlink = 0;
 	}
-	return err;
+
+	return out.h.error;
 }
 
 static int fuse_rmdir(struct inode *dir, struct dentry *entry)
 {
-	int err = fuse_remove(dir, entry, FUSE_RMDIR);
-	if(!err)
+	struct fuse_conn *fc = INO_FC(dir);
+	struct fuse_in in = FUSE_IN_INIT;
+	struct fuse_out out = FUSE_OUT_INIT;
+
+	in.h.opcode = FUSE_RMDIR;
+	in.h.ino = dir->i_ino;
+	in.numargs = 1;
+	in.args[0].size = entry->d_name.len + 1;
+	in.args[0].value = entry->d_name.name;
+	request_send(fc, &in, &out);
+	if(!out.h.error)
 		entry->d_inode->i_nlink = 0;
-	return err;
+
+	return out.h.error;
 }
 
 static int fuse_rename(struct inode *olddir, struct dentry *oldent,
@@ -594,7 +597,7 @@
 		fvalid |= FATTR_SIZE,   fattr->size = iattr->ia_size;
 	/* You can only _set_ these together (they may change by themselves) */
 	if((ivalid & (ATTR_ATIME | ATTR_MTIME)) == (ATTR_ATIME | ATTR_MTIME)) {
-		fvalid |= FATTR_UTIME;
+		fvalid |= FATTR_ATIME | FATTR_MTIME;
 #ifdef KERNEL_2_6
 		fattr->atime = iattr->ia_atime.tv_sec;
 		fattr->mtime = iattr->ia_mtime.tv_sec;
diff --git a/lib/fuse.c b/lib/fuse.c
index 079220a..2e6950d 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -315,17 +315,19 @@
 
 static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
 {
-    attr->mode    = stbuf->st_mode;
-    attr->nlink   = stbuf->st_nlink;
-    attr->uid     = stbuf->st_uid;
-    attr->gid     = stbuf->st_gid;
-    attr->rdev    = stbuf->st_rdev;
-    attr->size    = stbuf->st_size;
-    attr->blocks  = stbuf->st_blocks;
-    attr->atime   = stbuf->st_atime;
-    attr->mtime   = stbuf->st_mtime;
-    attr->ctime   = stbuf->st_ctime;
-    attr->_dummy  = 4096;
+    attr->mode      = stbuf->st_mode;
+    attr->nlink     = stbuf->st_nlink;
+    attr->uid       = stbuf->st_uid;
+    attr->gid       = stbuf->st_gid;
+    attr->rdev      = stbuf->st_rdev;
+    attr->size      = stbuf->st_size;
+    attr->blocks    = stbuf->st_blocks;
+    attr->atime     = stbuf->st_atime;
+    attr->atimensec = stbuf->st_atim.tv_nsec;
+    attr->mtime     = stbuf->st_mtime;
+    attr->mtimensec = stbuf->st_mtim.tv_nsec;
+    attr->ctime     = stbuf->st_ctime;
+    attr->ctimensec = stbuf->st_ctim.tv_nsec;
 }
 
 static int fill_dir(struct fuse_dirhandle *dh, char *name, int type)
@@ -553,7 +555,8 @@
                 res = do_chown(f, path, attr, valid);
             if(!res && (valid & FATTR_SIZE))
                 res = do_truncate(f, path, attr);
-            if(!res && (valid & FATTR_UTIME))
+            if(!res && (valid & (FATTR_ATIME | FATTR_MTIME)) == 
+               (FATTR_ATIME | FATTR_MTIME))
                 res = do_utime(f, path, attr);
             if(!res) {
                 struct stat buf;
@@ -665,7 +668,7 @@
     send_reply(f, in, res, &outarg, sizeof(outarg));
 }
 
-static void do_remove(struct fuse *f, struct fuse_in_header *in, char *name)
+static void do_unlink(struct fuse *f, struct fuse_in_header *in, char *name)
 {
     int res;
     char *path;
@@ -674,18 +677,32 @@
     path = get_path_name(f, in->ino, name);
     if(path != NULL) {
         res = -ENOSYS;
-        if(in->opcode == FUSE_UNLINK) {
-            if(f->op.unlink)
-                res = f->op.unlink(path);
-        }
-        else {
-            if(f->op.rmdir)
-                res = f->op.rmdir(path);
+        if(f->op.unlink) {
+            res = f->op.unlink(path);
+            if(res == 0)
+                remove_node(f, in->ino, name);
         }
         free(path);
     }
-    if(res == 0)
-        remove_node(f, in->ino, name);
+    send_reply(f, in, res, NULL, 0);
+}
+
+static void do_rmdir(struct fuse *f, struct fuse_in_header *in, char *name)
+{
+    int res;
+    char *path;
+
+    res = -ENOENT;
+    path = get_path_name(f, in->ino, name);
+    if(path != NULL) {
+        res = -ENOSYS;
+        if(f->op.rmdir) {
+            res = f->op.rmdir(path);
+            if(res == 0)
+                remove_node(f, in->ino, name);
+        }
+        free(path);
+    }
     send_reply(f, in, res, NULL, 0);
 }
 
@@ -990,8 +1007,11 @@
         break;
             
     case FUSE_UNLINK:
+        do_unlink(f, in, (char *) inarg);
+        break;
+
     case FUSE_RMDIR:
-        do_remove(f, in, (char *) inarg);
+        do_rmdir(f, in, (char *) inarg);
         break;
 
     case FUSE_SYMLINK: