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

[PATCH] fix truncate (it was completely broken)

dav_truncate(): add braces to prevent early exit,
previously all calls to dav_truncate() were no-ops

file_cache_truncate(): set the fi->modified flag to ensure
file_cache_sync_unlocked() picks up the change.
Additionally, we need to update the fi->present offset
as we no longer have the truncated data cached.

load_up_to_unlocked(): limit retrieval to fi->length.  If we
truncate a file locally, we don't want to reload the data on the
server, instead we want to leave holes in the file if we write
beyond the truncation point.

I used the following Ruby test script to test this behavior.

---------------------------- 8< ----------------------------
require 'test/unit'
class TestTruncate < Test::Unit::TestCase

  # Ideally this directory should be mounted with the auto_cache
  # FUSE mount option
  TEST_DIR = ENV["TEST_DIR"] or abort "TEST_DIR env needs to be set"

  def test_truncate
    Dir.chdir(TEST_DIR) do
      File.open("truncate", "w") do |f|
        f.write("HELLO WORLD")
      end
      assert system("sync") # try to work around lack of auto_cache

      File.open("truncate", "r+") do |f|
        f.sync = true
        f.truncate(0)
        f.seek(5)
        f.write "!"
      end
      assert system("sync") # try to work around lack of auto_cache
      assert_equal "\0\0\0\0\0!", File.read("truncate")
    end
  end
end
---------------------------- 8< ----------------------------
Usage: TEST_DIR=/path/to/fusedav/mount ruby /path/to/this_script.rb
>From d4b6e7b53634ac2577e64325ffadba2530bb98b8 Mon Sep 17 00:00:00 2001
From: Eric Wong <normalper...@yhbt.net>
Date: Thu, 25 Oct 2012 19:10:42 +0000
Subject: [PATCH 10/13] fix truncate (it was completely broken)

dav_truncate(): add braces to prevent early exit,
previously all calls to dav_truncate() were no-ops

file_cache_truncate(): set the fi->modified flag to ensure
file_cache_sync_unlocked() picks up the change.
Additionally, we need to update the fi->present offset
as we no longer have the truncated data cached.

load_up_to_unlocked(): limit retrieval to fi->length.  If we
truncate a file locally, we don't want to reload the data on the
server, instead we want to leave holes in the file if we write
beyond the truncation point.

I used the following Ruby test script to test this behavior.

---------------------------- 8< ----------------------------
require 'test/unit'
class TestTruncate < Test::Unit::TestCase

  # Ideally this directory should be mounted with the auto_cache
  # FUSE mount option
  TEST_DIR = ENV["TEST_DIR"] or abort "TEST_DIR env needs to be set"

  def test_truncate
    Dir.chdir(TEST_DIR) do
      File.open("truncate", "w") do |f|
        f.write("HELLO WORLD")
      end
      assert system("sync") # try to work around lack of auto_cache

      File.open("truncate", "r+") do |f|
        f.sync = true
        f.truncate(0)
        f.seek(5)
        f.write "!"
      end
      assert system("sync") # try to work around lack of auto_cache
      assert_equal "\0\0\0\0\0!", File.read("truncate")
    end
  end
end
---------------------------- 8< ----------------------------
Usage: TEST_DIR=/path/to/fusedav/mount ruby /path/to/this_script.rb
---
 src/filecache.c | 6 ++++++
 src/fusedav.c   | 3 ++-
 2 files changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/filecache.c b/src/filecache.c
index 08071fe..f9b4075 100644
--- a/src/filecache.c
+++ b/src/filecache.c
@@ -265,6 +265,9 @@ static int load_up_to_unlocked(struct file_info *fi, off_t 
l) {
         return -1;
     }
 
+    if (l > fi->length)
+        l = fi->length;
+
     if (l > fi->server_length)
         l = fi->server_length;
     
@@ -353,6 +356,9 @@ int file_cache_truncate(void *f, off_t s) {
 
     fi->length = s;
     r = ftruncate(fi->fd, fi->length);
+    fi->modified = 1;
+    if (fi->present > s)
+        fi->present = s;
 
     pthread_mutex_unlock(&fi->mutex);
 
diff --git a/src/fusedav.c b/src/fusedav.c
index 4c95ea3..6a92799 100644
--- a/src/fusedav.c
+++ b/src/fusedav.c
@@ -710,9 +710,10 @@ static int dav_truncate(const char *path, off_t size) {
     if (debug)
         fprintf(stderr, "truncate(%s, %lu)\n", path, (unsigned long) size);
 
-    if (!(session = session_get(1)))
+    if (!(session = session_get(1))) {
         r = -EIO;
         goto finish;
+    }
     
     if (!(f = file_cache_get(path))) {
         fprintf(stderr, "truncate() called for closed file\n");
-- 
1.8.0.3.gdd57fab.dirty

Reply via email to