close after delete support
diff --git a/lib/fuse.c b/lib/fuse.c
index 0009456..639b713 100644
--- a/lib/fuse.c
+++ b/lib/fuse.c
@@ -136,7 +136,7 @@
     return (hash + parent) % f->name_table_size;
 }
 
-static struct node *lookup_node(struct fuse *f, fino_t parent,
+static struct node *__lookup_node(struct fuse *f, fino_t parent,
                                 const char *name)
 {
     size_t hash = name_hash(f, parent, name);
@@ -149,6 +149,22 @@
     return NULL;
 }
 
+static struct node *lookup_node(struct fuse *f, fino_t parent,
+                                const char *name)
+{
+    struct node *node;
+
+    pthread_mutex_lock(&f->lock);
+    node = __lookup_node(f, parent, name);
+    pthread_mutex_unlock(&f->lock);
+    if (node != NULL)
+        return node;
+    
+    fprintf(stderr, "fuse internal error: node %lu/%s not found\n", parent,
+            name);
+    abort();
+}
+
 static void hash_name(struct fuse *f, struct node *node, fino_t parent,
                       const char *name)
 {
@@ -191,7 +207,7 @@
         rdev = attr->rdev;
 
     pthread_mutex_lock(&f->lock);
-    node = lookup_node(f, parent, name);
+    node = __lookup_node(f, parent, name);
     if (node != NULL) {
         if (node->mode == mode && node->rdev == rdev)
             goto out;
@@ -202,6 +218,8 @@
     node = (struct node *) calloc(1, sizeof(struct node));
     node->mode = mode;
     node->rdev = rdev;
+    node->open_count = 0;
+    node->is_hidden = 0;
     node->ino = next_ino(f);
     node->generation = f->generation;
     hash_ino(f, node);
@@ -289,7 +307,7 @@
     struct node *node;
 
     pthread_mutex_lock(&f->lock);
-    node = lookup_node(f, dir, name);
+    node = __lookup_node(f, dir, name);
     if (node == NULL) {
         fprintf(stderr, "fuse internal error: unable to remove node %lu/%s\n",
                 dir, name);
@@ -299,27 +317,39 @@
     pthread_mutex_unlock(&f->lock);
 }
 
-static void rename_node(struct fuse *f, fino_t olddir, const char *oldname,
-                        fino_t newdir, const char *newname)
+static int rename_node(struct fuse *f, fino_t olddir, const char *oldname,
+                        fino_t newdir, const char *newname, int hide)
 {
     struct node *node;
     struct node *newnode;
+    int err = 0;
     
     pthread_mutex_lock(&f->lock);
-    node  = lookup_node(f, olddir, oldname);
-    newnode  = lookup_node(f, newdir, newname);
+    node  = __lookup_node(f, olddir, oldname);
+    newnode  = __lookup_node(f, newdir, newname);
     if (node == NULL) {
         fprintf(stderr, "fuse internal error: unable to rename node %lu/%s\n",
                 olddir, oldname);
         abort();
     }
 
-    if (newnode != NULL)
+    if (newnode != NULL) {
+        if (hide) {
+            fprintf(stderr, "fuse: hidden file got created during hiding\n");
+            err = -1;
+            goto out;
+        }
         unhash_name(f, newnode);
+    }
         
     unhash_name(f, node);
     hash_name(f, node, newdir, newname);
+    if (hide)
+        node->is_hidden = 1;
+
+ out:
     pthread_mutex_unlock(&f->lock);
+    return err;
 }
 
 static void convert_stat(struct stat *stbuf, struct fuse_attr *attr)
@@ -417,6 +447,79 @@
     return res;
 }
 
+static int is_open(struct fuse *f, fino_t dir, const char *name)
+{
+    struct node *node;
+    int isopen = 0;
+    pthread_mutex_lock(&f->lock);
+    node = __lookup_node(f, dir, name);
+    if (node && node->open_count > 0)
+        isopen = 1;
+    pthread_mutex_unlock(&f->lock);
+    return isopen;
+}
+
+static char *hidden_name(struct fuse *f, fino_t dir, const char *oldname,
+                        char *newname, size_t bufsize)
+{
+    struct stat buf;
+    struct node *node;
+    struct node *newnode;
+    char *newpath;
+    int res;
+    int failctr = 10;
+
+    if (!f->op.getattr)
+        return NULL;
+
+    do {
+        node = lookup_node(f, dir, oldname);
+        pthread_mutex_lock(&f->lock);
+        do {
+            f->hidectr ++;
+            snprintf(newname, bufsize, ".fuse_hidden%08x%08x",
+                     (unsigned int) node->ino, f->hidectr);
+            newnode = __lookup_node(f, dir, newname);
+        } while(newnode);
+        pthread_mutex_unlock(&f->lock);
+        
+        newpath = get_path_name(f, dir, newname);
+        if (!newpath)
+            break;
+        
+        res = f->op.getattr(newpath, &buf);
+        if (res != 0)
+            break;
+        free(newpath);
+        newpath = NULL;
+    } while(--failctr);
+
+    return newpath;
+}
+
+static int hide_node(struct fuse *f, const char *oldpath, fino_t dir,
+                     const char *oldname)
+{
+    char newname[64];
+    char *newpath;
+    int err = -1;
+
+    if (!f->op.rename || !f->op.unlink)
+        return -EBUSY;
+
+    newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
+    if (newpath) {
+        err = f->op.rename(oldpath, newpath);
+        if (!err)
+            err = rename_node(f, dir, oldname, dir, newname, 1);
+        free(newpath);
+    }
+    if (err)
+        return -EBUSY;
+    
+    return 0;
+}
+
 static int lookup_path(struct fuse *f, fino_t ino, int version,  char *name,
                        const char *path, struct fuse_entry_out *arg)
 {
@@ -706,9 +809,13 @@
     if (path != NULL) {
         res = -ENOSYS;
         if (f->op.unlink) {
-            res = f->op.unlink(path);
-            if (res == 0)
-                remove_node(f, in->ino, name);
+            if (is_open(f, in->ino, name))
+                res = hide_node(f, path, in->ino, name);
+            else {
+                res = f->op.unlink(path);
+                if (res == 0)
+                    remove_node(f, in->ino, name);
+            }
         }
         free(path);
     }
@@ -780,10 +887,16 @@
         newpath = get_path_name(f, newdir, newname);
         if (newpath != NULL) {
             res = -ENOSYS;
-            if (f->op.rename)
-                res = f->op.rename(oldpath, newpath);
-            if (res == 0)
-                rename_node(f, olddir, oldname, newdir, newname);
+            if (f->op.rename) {
+                res = 0;
+                if (is_open(f, newdir, newname))
+                    res = hide_node(f, newpath, newdir, newname);
+                if (res == 0) {
+                    res = f->op.rename(oldpath, newpath);
+                    if (res == 0)
+                        rename_node(f, olddir, oldname, newdir, newname, 0);
+                }
+            }
             free(newpath);
         }
         free(oldpath);
@@ -841,11 +954,19 @@
             res = f->op.open(path, arg->flags);
     }
     res2 = send_reply(f, in, res, NULL, 0);
-    if (path != NULL) {
-        /* The open syscall was interrupted, so it must be cancelled */
-        if (res == 0 && res2 == -ENOENT && f->op.release)
-            f->op.release(path, arg->flags);
-        free(path);
+    if(res == 0) {
+        if(res2 == -ENOENT) {
+            /* The open syscall was interrupted, so it must be cancelled */
+            if(f->op.release)
+                f->op.release(path, arg->flags);
+        } else {
+            struct node *node;
+            
+            pthread_mutex_lock(&f->lock);
+            node = get_node(f, in->ino);
+            ++node->open_count;
+            pthread_mutex_unlock(&f->lock);
+        }
     }
 }
 
@@ -868,14 +989,24 @@
 static void do_release(struct fuse *f, struct fuse_in_header *in,
                        struct fuse_open_in *arg)
 {
-    if (f->op.release) {
-        char *path;
+    struct node *node;
+    char *path;
 
-        path = get_path(f, in->ino);
-        if (path != NULL) {
+    pthread_mutex_lock(&f->lock);
+    node = get_node(f, in->ino);
+    --node->open_count;
+    pthread_mutex_unlock(&f->lock);
+
+    path = get_path(f, in->ino);
+    if (path != NULL) {
+        if (f->op.release)
             f->op.release(path, arg->flags);
-            free(path);
-        }
+
+        if(node->is_hidden && node->open_count == 0)
+            /* can now clean up this hidden file */
+            f->op.unlink(path);
+        
+        free(path);
     }
 }
 
@@ -1454,7 +1585,19 @@
     size_t i;
     for (i = 0; i < f->ino_table_size; i++) {
         struct node *node;
+
+        for (node = f->ino_table[i]; node != NULL; node = node->ino_next) {
+            if (node->is_hidden) {
+                char *path = get_path(f, node->ino);
+                if (path)
+                    f->op.unlink(path);
+            }
+        }
+    }
+    for (i = 0; i < f->ino_table_size; i++) {
+        struct node *node;
         struct node *next;
+
         for (node = f->ino_table[i]; node != NULL; node = next) {
             next = node->ino_next;
             free_node(node);