implement garbage collection
authorLunar <lunar@anargeek.net>
Mon, 2 Aug 2010 10:47:30 +0000 (12:47 +0200)
committerLunar <lunar@anargeek.net>
Mon, 2 Aug 2010 10:47:30 +0000 (12:47 +0200)
coquelicot.rb
test_coquelicot.rb

index 860f958..02a4f58 100644 (file)
@@ -21,6 +21,8 @@ set :lockfile_options, { :timeout => 60,
 class BadKey < StandardError; end
 
 class StoredFile
+  BUFFER_LEN = 4096
+
   attr_reader :meta, :expire_at
 
   def self.open(path, pass = nil)
@@ -64,7 +66,6 @@ private
   YAML_START = "--- \n"
   CIPHER = 'AES-256-CBC'
   SALT_LEN = 8
-  BUFFER_LEN = 4096
   COQUELICOT_VERSION = "1.0"
 
   def self.get_cipher(pass, salt, method)
@@ -172,6 +173,12 @@ class Depot
     return !name.nil?
   end
 
+  def gc!
+    files.each do |name|
+      remove_file(name) if Time.now > StoredFile::open(full_path(name)).expire_at
+    end
+  end
+
 private
 
   def lockfile
@@ -190,12 +197,12 @@ private
     end
   end
 
-  def remove_link(src)
+  def remove_from_links(&block)
     lockfile.lock do
       links = []
       File.open(links_path, 'r+') do |f|
         f.readlines.each do |l|
-          links << l unless l.start_with? "#{src} "
+          links << l unless yield l
         end
         f.rewind
         f.truncate(0)
@@ -204,6 +211,10 @@ private
     end
   end
 
+  def remove_link(src)
+    remove_from_links { |l| l.start_with? "#{src} " }
+  end
+
   def read_link(src)
     dst = nil
     lockfile.lock do
@@ -220,6 +231,29 @@ private
     dst
   end
 
+  def remove_file(name)
+    # zero the content before unlinking
+    File.open(full_path(name), 'r+') do |f|
+      f.seek 0, IO::SEEK_END
+      length = f.tell
+      f.rewind
+      while length > 0 do
+        write_len = [StoredFile::BUFFER_LEN, length].min
+        length -= f.write("\0" * write_len)
+      end
+    end
+    File.unlink full_path(name)
+    remove_from_links { |l| l.end_with? " #{name}" }
+  end
+
+  def files
+    lockfile.lock do
+      File.open(links_path) do |f|
+        f.readlines.collect { |l| l.split[1] }
+      end
+    end
+  end
+
   def gen_random_file_name
     begin
       name = gen_random_base32(@filename_length)
index dcc673f..bf77c44 100644 (file)
@@ -182,7 +182,20 @@ describe 'Coquelicot' do
     end
   end
 
-  it "should cleanup expired files"
+  it "should cleanup expired files" do
+    post '/upload', 'file' => Rack::Test::UploadedFile.new(__FILE__, 'text/x-script.ruby'),
+                    'expire' => 60,  # 1 hour
+                    'upload_password' => UPLOAD_PASSWORD
+    last_response.redirect?.should be_true
+    follow_redirect!
+    last_response.should be_ok
+    Dir.glob("#{Depot.instance.path}/*").should have(1).items
+    # let's be tomorrow
+    Timecop.travel(Date.today + 1) do
+      Depot.instance.gc!
+      Dir.glob("#{Depot.instance.path}/*").should have(0).items
+    end
+  end
 
   it "should map extra base32 characters to filenames"
 end