Package: fusedav
Version: 0.2-3
Severity: normal
Tags: upstream patch

This is a two-part fix

Subject: [PATCH 12/13] implement dav_flush to avoid race from dav_release

dav_release unlinks a file from the cache before syncing,
allowing a concurrent thread to open a new cached entry
against stale file data.  So ensure we implement dav_flush
as libfuse2 will always flush before release.

Subject: [PATCH 13/13] fix release() with multiple open() to the same path

dav_release() should not unlink a file from the cache until its
ref count drops to zero.  The ref count may be non-zero after
release because because dav_open() may be called multiple times
without a dav_release() in between, so relying on a boolean dead
flag is problematic.

Instead of relying on a boolean dead flag, rely on the ref count
of the file and only unlink it when the counter drops to zero.



Btw, all my patches are available in my git repository at
 git://bogomips.org/fusedav.git

I have made the "bugfix-only" branch for things I consider bugfixes
(including Sebastian's existing patches in Debian).  I also have "home"
for whatever I'm running at home (which may have more experimental
changes).
>From ad701acac54f8dcc60b697332c1a3ca1971cbc63 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalper...@yhbt.net>
Date: Thu, 1 Nov 2012 23:24:36 +0000
Subject: [PATCH 12/13] implement dav_flush to avoid race from dav_release

dav_release unlinks a file from the cache before syncing,
allowing a concurrent thread to open a new cached entry
against stale file data.  So ensure we implement dav_flush
as libfuse2 will always flush before release.
---
 src/fusedav.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/src/fusedav.c b/src/fusedav.c
index 6a92799..69fed1b 100644
--- a/src/fusedav.c
+++ b/src/fusedav.c
@@ -549,7 +549,7 @@ finish:
     return r;
 }
 
-static int dav_fsync(const char *path, __unused int isdatasync, __unused 
struct fuse_file_info *info) {
+static int flush_internal(const char *fn, const char *path, __unused struct 
fuse_file_info *fi) {
     void *f = NULL;
     int r = 0;
     ne_session *session;
@@ -558,7 +558,7 @@ static int dav_fsync(const char *path, __unused int 
isdatasync, __unused struct
 
     path = path_cvt(path);
     if (debug)
-        fprintf(stderr, "fsync(%s)\n", path);
+        fprintf(stderr, "%s(%s)\n", fn, path);
 
     if (!(session = session_get(1))) {
         r = -EIO;
@@ -566,7 +566,7 @@ static int dav_fsync(const char *path, __unused int 
isdatasync, __unused struct
     }
 
     if (!(f = file_cache_get(path))) {
-        fprintf(stderr, "fsync() called for closed file\n");
+        fprintf(stderr, "%s() called for closed file\n", fn);
         r = -EFAULT;
         goto finish;
     }
@@ -590,6 +590,14 @@ finish:
     return r;
 }
 
+static int dav_flush(const char *path, struct fuse_file_info *fi) {
+       return flush_internal("flush", path, fi);
+}
+
+static int dav_fsync(const char *path, __unused int isdatasync, struct 
fuse_file_info *fi) {
+       return flush_internal("fsync", path, fi);
+}
+
 static int dav_mknod(const char *path, mode_t mode, __unused dev_t rdev) {
     char tempfile[PATH_MAX];
     int fd;
@@ -1167,6 +1175,7 @@ static struct fuse_operations dav_oper = {
     .chmod       = dav_chmod,
     .truncate   = dav_truncate,
     .utime       = dav_utime,
+    .flush       = dav_flush,
     .open       = dav_open,
     .read       = dav_read,
     .write      = dav_write,
-- 
1.8.0.3.gdd57fab.dirty

>From 6fb7e3cf48c1cd4d07c4807e2ff3bd0fd6f14fb9 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalper...@yhbt.net>
Date: Fri, 2 Nov 2012 23:56:24 +0000
Subject: [PATCH 13/13] fix release() with multiple open() to the same path

dav_release() should not unlink a file from the cache until its
ref count drops to zero.  The ref count may be non-zero after
release because because dav_open() may be called multiple times
without a dav_release() in between, so relying on a boolean dead
flag is problematic.

Instead of relying on a boolean dead flag, rely on the ref count
of the file and only unlink it when the counter drops to zero.
---
 src/filecache.c | 44 ++++++++++++++++++--------------------------
 src/filecache.h |  2 --
 src/fusedav.c   | 13 +++++--------
 3 files changed, 23 insertions(+), 36 deletions(-)

diff --git a/src/filecache.c b/src/filecache.c
index f291e15..eded0aa 100644
--- a/src/filecache.c
+++ b/src/filecache.c
@@ -57,7 +57,7 @@ struct file_info {
 
     int modified;
 
-    int ref, dead;
+    int ref;
 
     pthread_mutex_t mutex;
 
@@ -68,6 +68,7 @@ struct file_info {
 static struct file_info *files = NULL;
 static pthread_mutex_t files_mutex = PTHREAD_MUTEX_INITIALIZER;
 
+static void file_cache_unlink(struct file_info *fi);
 static int file_cache_sync_unlocked(struct file_info *fi);
 
 static void* file_cache_get_unlocked(const char *path) {
@@ -76,7 +77,7 @@ static void* file_cache_get_unlocked(const char *path) {
     for (f = files; f; f = f->next) {
         
         pthread_mutex_lock(&f->mutex);
-        if (!f->dead && f->filename && !strcmp(path, f->filename)) {
+        if (f->ref && f->filename && !strcmp(path, f->filename)) {
             f->ref++;
             r = f;
         }
@@ -100,7 +101,7 @@ void* file_cache_get(const char *path) {
 }
 
 static void file_cache_free_unlocked(struct file_info *fi) {
-    assert(fi && fi->dead && fi->ref == 0);
+    assert(fi && fi->ref == 0);
 
     free(fi->filename);
 
@@ -113,6 +114,7 @@ static void file_cache_free_unlocked(struct file_info *fi) {
 
 void file_cache_unref(void *f) {
     struct file_info *fi = f;
+    int unlinked = 0;
     assert(fi);
 
     pthread_mutex_lock(&fi->mutex);
@@ -120,22 +122,29 @@ void file_cache_unref(void *f) {
     assert(fi->ref >= 1);
     fi->ref--;
 
-    if (!fi->ref && fi->dead) {
-        file_cache_sync_unlocked(fi);
-        file_cache_free_unlocked(fi);
+    if (fi->ref == 0) {
+        if (fi->writable)
+            file_cache_sync_unlocked(fi);
+        file_cache_unlink(fi);
+        unlinked = 1;
     }
 
     pthread_mutex_unlock(&fi->mutex);
+
+    if (unlinked)
+        file_cache_free_unlocked(fi);
 }
 
 static void file_cache_unlink(struct file_info *fi) {
     struct file_info *s, *prev;
+    int found = 0;
     assert(fi);
 
     pthread_mutex_lock(&files_mutex);
     
     for (s = files, prev = NULL; s; s = s->next) {
         if (s == fi) {
+            found = 1;
             if (prev)
                 prev->next = s->next;
             else
@@ -148,20 +157,9 @@ static void file_cache_unlink(struct file_info *fi) {
     }
     
     pthread_mutex_unlock(&files_mutex);
-}
-
-int file_cache_close(void *f) {
-    struct file_info *fi = f;
-    int r = 0;
-    assert(fi);
 
-    file_cache_unlink(f);
-
-    pthread_mutex_lock(&fi->mutex);
-    fi->dead = 1;
-    pthread_mutex_unlock(&fi->mutex);
-
-    return r;
+    if (!found)
+        fprintf(stderr, "file_cache_unlink(%s) failed", fi->filename);
 }
 
 static void file_cache_update_flags_unlocked(struct file_info *fi, int flags)
@@ -241,7 +239,6 @@ fail:
 
     if (fi) {
         pthread_mutex_unlock(&fi->mutex);
-        fi->dead = 1;
         file_cache_free_unlocked(fi);
     }
         
@@ -415,7 +412,7 @@ int file_cache_sync(void *f) {
     assert(fi);
 
     pthread_mutex_lock(&fi->mutex);
-    r = file_cache_sync_unlocked(fi);
+    r = fi->writable ? file_cache_sync_unlocked(fi) : 0;
     pthread_mutex_unlock(&fi->mutex);
     
     return r;
@@ -429,12 +426,7 @@ int file_cache_close_all(void) {
     while (files) {
         struct file_info *fi = files;
         
-        pthread_mutex_lock(&fi->mutex);
-        fi->ref++;
         pthread_mutex_unlock(&fi->mutex);
-
-        pthread_mutex_unlock(&files_mutex);
-        file_cache_close(fi);
         file_cache_unref(fi);
         pthread_mutex_lock(&files_mutex);
     }
diff --git a/src/filecache.h b/src/filecache.h
index c3845ca..7e217e2 100644
--- a/src/filecache.h
+++ b/src/filecache.h
@@ -29,8 +29,6 @@ void* file_cache_open(const char *path, int flags);
 void* file_cache_get(const char *path);
 void file_cache_unref(void *f);
 
-int file_cache_close(void *f);
-
 int file_cache_read(void *f, char *buf, size_t size, off_t offset);
 int file_cache_write(void *f, const char *buf, size_t size, off_t offset);
 int file_cache_truncate(void *f, off_t s);
diff --git a/src/fusedav.c b/src/fusedav.c
index 69fed1b..be6d264 100644
--- a/src/fusedav.c
+++ b/src/fusedav.c
@@ -537,14 +537,13 @@ static int dav_release(const char *path, __unused struct 
fuse_file_info *info) {
         goto finish;
     }
 
-    if (file_cache_close(f) < 0) {
-        r = -errno;
-        goto finish;
-    }
+    /* reverse the ref increment from file_cache_get() */
+    file_cache_unref(f);
+
+    /* reverse the ref increment from dav_open() */
+    file_cache_unref(f);
 
 finish:
-    if (f)
-        file_cache_unref(f);
     
     return r;
 }
@@ -648,8 +647,6 @@ static int dav_open(const char *path, struct fuse_file_info 
*info) {
     if (!(f = file_cache_open(path, info->flags)))
         return -errno;
 
-    file_cache_unref(f);
-
     return 0;
 }
 
-- 
1.8.0.3.gdd57fab.dirty

Reply via email to