use File::EXCL|File::CREAT instead of locking when adding a new file to the depot
authorLunar <lunar@anargeek.net>
Mon, 5 Mar 2012 16:31:12 +0000 (17:31 +0100)
committerLunar <lunar@anargeek.net>
Fri, 23 Mar 2012 17:11:28 +0000 (18:11 +0100)
lib/coquelicot/depot.rb
lib/coquelicot/stored_file.rb
spec/coquelicot/stored_file_spec.rb

index 649e7cb..334b88d 100644 (file)
@@ -27,12 +27,19 @@ module Coquelicot
 
     def add_file(pass, options, &block)
       dst = nil
-      lockfile.lock do
-        dst = gen_random_file_name
-        File.open(full_path(dst), 'w').close
-      end
+
       begin
-        StoredFile.create(full_path(dst), pass, options, &block)
+        # Ensure that the generated name is not already used
+        loop do
+          dst = gen_random_file_name
+          begin
+            StoredFile.create(full_path(dst), pass, options, &block)
+            break
+          rescue Errno::EEXIST => e
+            raise unless e.message =~ /(?:^|\s)#{Regexp.escape(full_path(dst))}(?:\s|$)/
+            next # let's try again
+          end
+        end
       rescue
         File.unlink full_path(dst)
         raise
index 41092a6..ed5fc5a 100644 (file)
@@ -49,7 +49,7 @@ module Coquelicot
                      "Salt" => Base64.encode64(salt).strip,
                      "Expire-at" => meta.delete('Expire-at'),
                    }
-      File.open(dest, 'w') do |dest|
+      File.open(dest, File::WRONLY|File::EXCL|File::CREAT) do |dest|
         dest.write(YAML.dump(clear_meta) + YAML_START)
 
         cipher = get_cipher(pass, salt, :encrypt)
index 2047153..68be702 100644 (file)
@@ -32,7 +32,7 @@ module Coquelicot
       end
 
       def create_stored_file(extra_meta = {})
-        @stored_file_path = File.expand_path('stored_file', @tmpdir)
+        @stored_file_path ||= File.expand_path('stored_file', @tmpdir)
         @pass = 'secret'
         @src = __FILE__
         @src_length = File.stat(@src).size
@@ -149,6 +149,15 @@ module Coquelicot
 
     describe '.create' do
       include_context 'create new StoredFile'
+      context 'when the destination file already exists' do
+        it 'should raise an error' do
+          @stored_file_path = File.expand_path('stored_file', @tmpdir)
+          FileUtils.touch @stored_file_path
+          expect {
+            create_stored_file
+          }.to raise_error(Errno::EEXIST)
+        end
+      end
       context 'in clear metadata' do
         let(:test_salt) { "\0" * StoredFile::SALT_LEN }
         let(:expire_at) { Time.now + 60 }