From 8e4d71adc9ebcfa48d7fcff388cc68661c03eaa2 Mon Sep 17 00:00:00 2001 From: Go MAEDA Date: Mon, 19 Jul 2021 13:27:59 +0000 Subject: Ensure unique attachment filenames (#35539). MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Patch by Jens Krämer. git-svn-id: http://svn.redmine.org/redmine/trunk@21071 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/models/attachment.rb | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) (limited to 'app/models') diff --git a/app/models/attachment.rb b/app/models/attachment.rb index c3c3fc8b3..c65c56722 100644 --- a/app/models/attachment.rb +++ b/app/models/attachment.rb @@ -138,14 +138,10 @@ class Attachment < ActiveRecord::Base def files_to_final_location if @temp_file self.disk_directory = target_directory - self.disk_filename = Attachment.disk_filename(filename, disk_directory) - logger.info("Saving attachment '#{self.diskfile}' (#{@temp_file.size} bytes)") if logger - path = File.dirname(diskfile) - unless File.directory?(path) - FileUtils.mkdir_p(path) - end sha = Digest::SHA256.new - File.open(diskfile, "wb") do |f| + Attachment.create_diskfile(filename, disk_directory) do |f| + self.disk_filename = File.basename f.path + logger.info("Saving attachment '#{self.diskfile}' (#{@temp_file.size} bytes)") if logger if @temp_file.respond_to?(:read) buffer = "" while (buffer = @temp_file.read(8192)) @@ -557,9 +553,8 @@ class Attachment < ActiveRecord::Base # Singleton class method is public class << self - # Returns an ASCII or hashed filename that do not - # exists yet in the given subdirectory - def disk_filename(filename, directory=nil) + # Claims a unique ASCII or hashed filename, yields the open file handle + def create_diskfile(filename, directory=nil, &block) timestamp = DateTime.now.strftime("%y%m%d%H%M%S") ascii = '' if %r{^[a-zA-Z0-9_\.\-]*$}.match?(filename) && filename.length <= 50 @@ -569,11 +564,21 @@ class Attachment < ActiveRecord::Base # keep the extension if any ascii << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$} end - while File.exist?(File.join(storage_path, directory.to_s, - "#{timestamp}_#{ascii}")) + + path = File.join storage_path, directory.to_s + FileUtils.mkdir_p(path) unless File.directory?(path) + begin + name = "#{timestamp}_#{ascii}" + File.open( + File.join(path, name), + flags: File::CREAT | File::EXCL | File::BINARY | File::WRONLY, + binmode: true, + &block + ) + rescue Errno::EEXIST timestamp.succ! + retry end - "#{timestamp}_#{ascii}" end end end -- cgit v1.2.3