Moved from md5 to sha256 for file identification.

This commit is contained in:
Martin Edenhofer 2014-05-03 16:52:37 +02:00
parent 1528e6dc2e
commit 84c23e2311
6 changed files with 98 additions and 74 deletions

View file

@ -1,8 +1,5 @@
# Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/ # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
require 'digest/md5'
class Store < ApplicationModel class Store < ApplicationModel
require 'store/object' require 'store/object'
require 'store/file' require 'store/file'

View file

@ -6,8 +6,9 @@ class Store::File < ApplicationModel
# add new file # add new file
def self.add(data) def self.add(data)
md5 = Digest::MD5.hexdigest( data ) sha = Digest::SHA256.hexdigest( data )
file = Store::File.where( :md5 => md5 ).first
file = Store::File.where( :sha => sha ).first
if file == nil if file == nil
# load backend based on config # load backend based on config
@ -16,10 +17,10 @@ class Store::File < ApplicationModel
raise "Missing storage_provider setting option" raise "Missing storage_provider setting option"
end end
adapter = self.load_adapter( "Store::Provider::#{ adapter_name }" ) adapter = self.load_adapter( "Store::Provider::#{ adapter_name }" )
adapter.add( data, md5 ) adapter.add( data, sha )
file = Store::File.create( file = Store::File.create(
:provider => adapter_name, :provider => adapter_name,
:md5 => md5, :sha => sha,
) )
end end
file file
@ -27,24 +28,22 @@ class Store::File < ApplicationModel
# read content # read content
def content def content
puts "get #{self.id} #{self.provider}"
adapter = self.class.load_adapter("Store::Provider::#{ self.provider }") adapter = self.class.load_adapter("Store::Provider::#{ self.provider }")
adapter.get( self.md5 ) adapter.get( self.sha )
end end
# check data and sha, in case fix it
# check data and md5, in case fix it def self.verify(fix_it = nil)
def self.check_md5(fix_it = nil)
success = true success = true
Store::File.all.each {|item| Store::File.all.each {|item|
content = item.content content = item.content
md5 = Digest::MD5.hexdigest( content ) sha = Digest::SHA256.hexdigest( content )
puts "CHECK: Store::File.find(#{item.id}) " puts "CHECK: Store::File.find(#{item.id}) "
if md5 != item.md5 if sha != item.sha
success = false success = false
puts "DIFF: md5 diff of Store::File.find(#{item.id}) " puts "DIFF: sha diff of Store::File.find(#{item.id}) "
if fix_it if fix_it
item.update_attribute( :md5, md5 ) item.update_attribute( :sha, sha )
end end
end end
} }
@ -61,22 +60,23 @@ class Store::File < ApplicationModel
content = item.content content = item.content
# add to new provider # add to new provider
adapter_target.add( content, item.md5 ) adapter_target.add( content, item.sha )
# update meta data # update meta data
item.update_attribute( :provider, target ) item.update_attribute( :provider, target )
# remove from old provider # remove from old provider
adapter_source.delete( item.md5 ) adapter_source.delete( item.sha )
puts "NOTICE: Moved file #{item.md5} from #{source} to #{target}" puts "NOTICE: Moved file #{item.sha} from #{source} to #{target}"
} }
true
end end
private private
def destroy_provider def destroy_provider
adapter = self.class.load_adapter("Store::Provider::#{ self.provider }") adapter = self.class.load_adapter("Store::Provider::#{ self.provider }")
adapter.delete( md5 ) adapter.delete( self.sha )
end end
end end

View file

@ -3,22 +3,22 @@
class Store::Provider::DB < ApplicationModel class Store::Provider::DB < ApplicationModel
self.table_name = 'store_provider_dbs' self.table_name = 'store_provider_dbs'
def self.add(data, md5) def self.add(data, sha)
Store::Provider::DB.create( Store::Provider::DB.create(
:data => data, :data => data,
:md5 => md5, :sha => sha,
) )
true true
end end
def self.get(md5) def self.get(sha)
file = Store::Provider::DB.where( :md5 => md5 ).first file = Store::Provider::DB.where( :sha => sha ).first
return if !file return if !file
file.data file.data
end end
def self.delete(md5) def self.delete(sha)
Store::Provider::DB.where( :md5 => md5 ).destroy_all Store::Provider::DB.where( :sha => sha ).destroy_all
true true
end end

View file

@ -2,29 +2,29 @@
class Store::Provider::File class Store::Provider::File
def self.add(data, md5) def self.add(data, sha)
write_to_fs(data, md5) write_to_fs(data, sha)
true true
end end
def self.get(md5) def self.get(sha)
read_from_fs(md5) read_from_fs(sha)
end end
def self.delete(md5) def self.delete(sha)
unlink_from_fs(md5) unlink_from_fs(sha)
end end
private private
# generate file location # generate file location
def self.get_locaton(md5) def self.get_locaton(sha)
# generate directory # generate directory
base = Rails.root.to_s + '/storage/fs/' base = Rails.root.to_s + '/storage/fs/'
parts = md5.scan(/.{1,3}/) parts = sha.scan(/.{1,3}/)
path = parts[ 1 .. 7 ].join('/') + '/' path = parts[ 1 .. 10 ].join('/') + '/'
file = parts[ 8 .. parts.count ].join('') file = parts[ 11 .. parts.count ].join('')
location = "#{base}/#{path}" location = "#{base}/#{path}"
# create directory if not exists # create directory if not exists
@ -35,47 +35,47 @@ class Store::Provider::File
end end
# unlink file from fs # unlink file from fs
def self.unlink_from_fs(md5) def self.unlink_from_fs(sha)
if File.exist?( get_locaton(md5) ) if File.exist?( get_locaton(sha) )
puts "NOTICE: storge remove '#{ get_locaton(md5) }'" puts "NOTICE: storge remove '#{ get_locaton(sha) }'"
File.delete( get_locaton(md5) ) File.delete( get_locaton(sha) )
end end
end end
# read file from fs # read file from fs
def self.read_from_fs(md5) def self.read_from_fs(sha)
puts "read from fs #{ get_locaton(md5) }" puts "read from fs #{ get_locaton(sha) }"
if !File.exist?( get_locaton(md5) ) if !File.exist?( get_locaton(sha) )
raise "ERROR: No such file #{ get_locaton(md5) }" raise "ERROR: No such file #{ get_locaton(sha) }"
end end
data = File.open( get_locaton(md5), 'rb' ) data = File.open( get_locaton(sha), 'rb' )
content = data.read content = data.read
# check md5 # check sha
local_md5 = Digest::MD5.hexdigest( content ) local_sha = Digest::SHA256.hexdigest( content )
if local_md5 != md5 if local_sha != sha
raise "ERROR: Corrupt file in fs #{ get_locaton(md5) }, md5 should be #{md5} but is #{local_md5}" raise "ERROR: Corrupt file in fs #{ get_locaton(sha) }, sha should be #{sha} but is #{local_sha}"
end end
content content
end end
# write file to fs # write file to fs
def self.write_to_fs(data,md5) def self.write_to_fs(data,sha)
# install file # install file
permission = '600' permission = '600'
if !File.exist?( get_locaton(md5) ) if !File.exist?( get_locaton(sha) )
puts "NOTICE: storge write '#{ get_locaton(md5) }' (#{permission})" puts "NOTICE: storge write '#{ get_locaton(sha) }' (#{permission})"
file = File.new( get_locaton(md5), 'wb' ) file = File.new( get_locaton(sha), 'wb' )
file.write( data ) file.write( data )
file.close file.close
end end
File.chmod( permission.to_i(8), get_locaton(md5) ) File.chmod( permission.to_i(8), get_locaton(sha) )
# check md5 # check sha
local_md5 = Digest::MD5.hexdigest( read_from_fs(md5) ) local_sha = Digest::SHA256.hexdigest( read_from_fs(sha) )
if md5 != local_md5 if sha != local_sha
raise "ERROR: Corrupt file in fs #{ get_locaton(md5) }, md5 should be #{md5} but is #{local_md5}" raise "ERROR: Corrupt file in fs #{ get_locaton(sha) }, sha should be #{sha} but is #{local_sha}"
end end
true true

View file

@ -0,0 +1,27 @@
class UpdateStorage3 < ActiveRecord::Migration
def up
add_column :store_files, :sha, :string, :limit => 128, :null => true
add_index :store_files, [:sha], :unique => true
add_column :store_provider_dbs, :sha, :string, :limit => 128, :null => true
add_index :store_provider_dbs, [:sha], :unique => true
Store::File.all.each {|file|
next if file.sha
sha = Digest::SHA256.hexdigest( file.content )
file.update_attribute( :sha, sha )
}
Store::Provider::DB.all.each {|file|
next if file.sha
sha = Digest::SHA256.hexdigest( file.content )
file.update_attribute( :sha, sha )
}
remove_column :store_files, :md5
remove_column :store_provider_dbs, :md5
end
def down
end
end

View file

@ -27,7 +27,7 @@ class StoreTest < ActiveSupport::TestCase
] ]
files.each { |file| files.each { |file|
md5 = Digest::MD5.hexdigest( file[:data] ) sha = Digest::SHA256.hexdigest( file[:data] )
# add attachments # add attachments
store = Store.add( store = Store.add(
@ -47,9 +47,9 @@ class StoreTest < ActiveSupport::TestCase
) )
assert attachments assert attachments
# md5 check # sha check
md5_new = Digest::MD5.hexdigest( attachments[0].content ) sha_new = Digest::SHA256.hexdigest( attachments[0].content )
assert_equal( md5, md5_new, "check file #{ file[:filename] }") assert_equal( sha, sha_new, "check file #{ file[:filename] }")
# filename check # filename check
assert_equal( file[:filename], attachments[0].filename ) assert_equal( file[:filename], attachments[0].filename )
@ -58,13 +58,13 @@ class StoreTest < ActiveSupport::TestCase
assert_equal( 'DB', attachments[0].provider ) assert_equal( 'DB', attachments[0].provider )
} }
success = Store::File.check_md5 success = Store::File.verify
assert success, "check_md5 ok" assert success, "verify ok"
Store::File.move( 'DB', 'File' ) Store::File.move( 'DB', 'File' )
files.each { |file| files.each { |file|
md5 = Digest::MD5.hexdigest( file[:data] ) sha = Digest::SHA256.hexdigest( file[:data] )
# get list of attachments # get list of attachments
attachments = Store.list( attachments = Store.list(
@ -73,9 +73,9 @@ class StoreTest < ActiveSupport::TestCase
) )
assert attachments assert attachments
# md5 check # sha check
md5_new = Digest::MD5.hexdigest( attachments[0].content ) sha_new = Digest::SHA256.hexdigest( attachments[0].content )
assert_equal( md5, md5_new, "check file #{ file[:filename] }") assert_equal( sha, sha_new, "check file #{ file[:filename] }")
# filename check # filename check
assert_equal( file[:filename], attachments[0].filename ) assert_equal( file[:filename], attachments[0].filename )
@ -84,13 +84,13 @@ class StoreTest < ActiveSupport::TestCase
assert_equal( 'File', attachments[0].provider ) assert_equal( 'File', attachments[0].provider )
} }
success = Store::File.check_md5 success = Store::File.verify
assert success, "check_md5 ok" assert success, "verify ok"
Store::File.move( 'File', 'DB' ) Store::File.move( 'File', 'DB' )
files.each { |file| files.each { |file|
md5 = Digest::MD5.hexdigest( file[:data] ) sha = Digest::SHA256.hexdigest( file[:data] )
# get list of attachments # get list of attachments
attachments = Store.list( attachments = Store.list(
@ -99,9 +99,9 @@ class StoreTest < ActiveSupport::TestCase
) )
assert attachments assert attachments
# md5 check # sha check
md5_new = Digest::MD5.hexdigest( attachments[0].content ) sha_new = Digest::SHA256.hexdigest( attachments[0].content )
assert_equal( md5, md5_new, "check file #{ file[:filename] }") assert_equal( sha, sha_new, "check file #{ file[:filename] }")
# filename check # filename check
assert_equal( file[:filename], attachments[0].filename ) assert_equal( file[:filename], attachments[0].filename )