close after delete support
authorMiklos Szeredi <miklos@szeredi.hu>
Thu, 24 Jun 2004 21:00:00 +0000 (21:00 +0000)
committerMiklos Szeredi <miklos@szeredi.hu>
Thu, 24 Jun 2004 21:00:00 +0000 (21:00 +0000)
ChangeLog
lib/fuse.c
lib/fuse_i.h
lib/fuse_mt.c
lib/helper.c

index 502efd8ba1fe42cd4f89dd65098bd11ea19d0869..27e92266215d54591ffdece89b43959dda6bbcb0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2004-06-24  Miklos Szeredi <mszeredi@inf.bme.hu>
+
+       * Add "close after delete" support to libfuse (patch by Valient
+       Gough)
+
+       * Cancel all worker threads before exit in multithreaded mode
+
 2004-06-23  Miklos Szeredi <mszeredi@inf.bme.hu>
 
        * Fix locking bugs
index 00094562c6812facb97a96a71ffdff030667a3d8..639b713890b632f64517b6f945969094eca4bb3c 100644 (file)
@@ -136,7 +136,7 @@ static unsigned int name_hash(struct fuse *f, fino_t parent, const char *name)
     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 @@ static struct node *lookup_node(struct fuse *f, fino_t parent,
     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 @@ static struct node *find_node(struct fuse *f, fino_t parent, char *name,
         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 @@ static struct node *find_node(struct fuse *f, fino_t parent, char *name,
     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 @@ static void remove_node(struct fuse *f, fino_t dir, const char *name)
     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 @@ static void remove_node(struct fuse *f, fino_t dir, const char *name)
     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 @@ static int send_reply(struct fuse *f, struct fuse_in_header *in, int error,
     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 @@ static void do_unlink(struct fuse *f, struct fuse_in_header *in, char *name)
     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 @@ static void do_rename(struct fuse *f, struct fuse_in_header *in,
         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 @@ static void do_open(struct fuse *f, struct fuse_in_header *in,
             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_flush(struct fuse *f, struct fuse_in_header *in)
 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;
+
+    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) {
+    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);
     }
 }
 
@@ -1452,9 +1583,21 @@ struct fuse *fuse_new(int fd, int flags, const struct fuse_operations *op)
 void fuse_destroy(struct fuse *f)
 {
     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);
index 3938f29aa0d8653668c061c827122512881fb1d5..4d387e2de20641a9044fde7a6a306438da8452ee 100644 (file)
@@ -22,6 +22,8 @@ struct node {
     int mode;
     int rdev;
     int version;
+    int open_count;
+    int is_hidden;
 };
 
 struct fuse {
@@ -34,6 +36,7 @@ struct fuse {
     size_t ino_table_size;
     fino_t ctr;
     unsigned int generation;
+    unsigned int hidectr;
     pthread_mutex_t lock;
     int numworker;
     int numavail;
index aa9dc7e8b7a657e051f5bb5a714f9a9d2e0bd44b..afc70d424712dc71659b23cd15f2f2be404b1fc1 100644 (file)
 
 struct fuse_worker {
     struct fuse *f;
+    pthread_t threads[FUSE_MAX_WORKERS];
     void *data;
     fuse_processor_t proc;
 };
 
-static void start_thread(struct fuse_worker *w);
+static void start_thread(struct fuse_worker *w, pthread_t *thread_id);
 
 static void *do_work(void *data)
 {
     struct fuse_worker *w = (struct fuse_worker *) data;
     struct fuse *f = w->f;
 
+    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
+    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
+
     while (1) {
         struct fuse_cmd *cmd;
 
@@ -43,10 +47,14 @@ static void *do_work(void *data)
 
         if (f->numavail == 0 && f->numworker < FUSE_MAX_WORKERS) {
             pthread_mutex_lock(&f->lock);
-            f->numavail ++;
-            f->numworker ++;
-            pthread_mutex_unlock(&f->lock);
-            start_thread(w);
+            if (f->numworker < FUSE_MAX_WORKERS) {
+                pthread_t *thread_id = &w->threads[f->numworker];
+                f->numavail ++;
+                f->numworker ++;
+                pthread_mutex_unlock(&f->lock);
+                start_thread(w, thread_id);
+            } else
+                pthread_mutex_unlock(&f->lock);
         }
 
         w->proc(w->f, cmd, w->data);
@@ -55,9 +63,8 @@ static void *do_work(void *data)
     return NULL;
 }
 
-static void start_thread(struct fuse_worker *w)
+static void start_thread(struct fuse_worker *w, pthread_t *thread_id)
 {
-    pthread_t thrid;
     sigset_t oldset;
     sigset_t newset;
     int res;
@@ -65,13 +72,14 @@ static void start_thread(struct fuse_worker *w)
     /* Disallow signal reception in worker threads */
     sigfillset(&newset);
     pthread_sigmask(SIG_SETMASK, &newset, &oldset);
-    res = pthread_create(&thrid, NULL, do_work, w);
+    res = pthread_create(thread_id, NULL, do_work, w);
     pthread_sigmask(SIG_SETMASK, &oldset, NULL);
     if (res != 0) {
         fprintf(stderr, "Error creating thread: %s\n", strerror(res));
         exit(1);
     }
-    pthread_detach(thrid);
+    
+    pthread_detach(*thread_id);
 }
 
 static struct fuse_context *mt_getcontext(struct fuse *f)
@@ -96,8 +104,10 @@ void __fuse_loop_mt(struct fuse *f, fuse_processor_t proc, void *data)
 {
     struct fuse_worker *w;
     int res;
+    int i;
 
     w = malloc(sizeof(struct fuse_worker));    
+    memset(w, 0, sizeof(struct fuse_worker));
     w->f = f;
     w->data = data;
     w->proc = proc;
@@ -110,6 +120,11 @@ void __fuse_loop_mt(struct fuse *f, fuse_processor_t proc, void *data)
     }
     f->getcontext = mt_getcontext;
     do_work(w);
+
+    pthread_mutex_lock(&f->lock);
+    for (i = 1; i < f->numworker; i++)
+        pthread_cancel(w->threads[i]);
+    pthread_mutex_unlock(&f->lock);
 }
 
 void fuse_loop_mt(struct fuse *f)
index a5e48540a717afca9cc33ab6b573fa787ef6ce34..cd3cfeab5e7b89f52b37d5dace43aaa553421130 100644 (file)
@@ -98,6 +98,8 @@ static int fuse_start(int fuse_fd, int flags, int multithreaded,
         fuse_loop_mt(fuse);
     else
         fuse_loop(fuse);
+    
+    fuse_destroy(fuse);
 
     return 0;
 }