file.rb 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. class Store::Provider::File
  3. # write file to fs
  4. def self.add(data, sha)
  5. location = get_location(sha)
  6. # write file to file system
  7. if !File.exist?(location)
  8. Rails.logger.debug { "storge write '#{location}' (600)" }
  9. File.binwrite(location, data)
  10. end
  11. File.chmod(0o600, location)
  12. validate_file(sha)
  13. rescue # .validate_file will raise an error if contents do not match SHA
  14. delete(sha)
  15. fail_count ||= 0
  16. fail_count.zero? ? (fail_count += 1) && retry : raise
  17. end
  18. # read file from fs
  19. def self.get(sha)
  20. location = get_location(sha)
  21. Rails.logger.debug { "read from fs #{location}" }
  22. content = File.binread(location)
  23. local_sha = Store::File.checksum(content)
  24. # check sha
  25. raise "File corrupted: path #{location} does not match SHA digest (#{local_sha})" if local_sha != sha
  26. content
  27. end
  28. class << self
  29. alias validate_file get
  30. end
  31. # unlink file from fs
  32. def self.delete(sha)
  33. location = get_location(sha)
  34. if File.exist?(location)
  35. Rails.logger.info "storage remove '#{location}'"
  36. File.delete(location)
  37. end
  38. # remove empty ancestor directories
  39. storage_fs_path = Rails.root.join('storage/fs')
  40. location.parent.ascend do |path|
  41. break if !Dir.empty?(path)
  42. break if path == storage_fs_path
  43. Dir.rmdir(path)
  44. end
  45. end
  46. # generate file location
  47. def self.get_location(sha)
  48. parts = sha.scan(%r{^(.{4})(.{4})(.{5})(.{5})(.{7})(.{7})(.*)}).first
  49. Rails.root.join('storage/fs', *parts).tap { |path| FileUtils.mkdir_p(path.parent) }
  50. end
  51. end