class ZKSync::CryptoFile

Attributes

archive[R]
archive_path[R]

Public Class Methods

new(archive_path, archive) click to toggle source
# File lib/zksync/crypto_file.rb, line 22
def initialize(archive_path, archive)
  @archive = archive
  @archive_path = archive_path
end

Public Instance Methods

__read_page_keep_padding(page_num) click to toggle source
# File lib/zksync/crypto_file.rb, line 71
def __read_page_keep_padding(page_num)
  page_encrypted = IO.read(page_path(page_num), page_physical_size)
  page_key = Key.new(file_key.decrypt(page_encrypted[0...Key.write_size]), type: :literal)
  page_key.decrypt(page_encrypted[Key.write_size..-1])
end
file_key() click to toggle source
# File lib/zksync/crypto_file.rb, line 47
def file_key
  @file_key ||= archive.keystore.file_key(archive_path)
end
inode() click to toggle source
# File lib/zksync/crypto_file.rb, line 43
def inode
  @inode ||= archive.file_index.inode_for_path(archive_path)
end
make_parent_directories(path) click to toggle source
# File lib/zksync/crypto_file.rb, line 83
def make_parent_directories(path)
  FileUtils.mkdir_p(File.dirname(path))
end
page_count() click to toggle source
# File lib/zksync/crypto_file.rb, line 39
def page_count
  (size.to_f/page_size).ceil
end
page_matches?(pagefile_path, key) click to toggle source
# File lib/zksync/crypto_file.rb, line 87
def page_matches?(pagefile_path, key)
  return false unless File.exists?(pagefile_path)
  IO.read(pagefile_path, Key.size) == key.to_b
end
page_path(page_num) click to toggle source
# File lib/zksync/crypto_file.rb, line 51
def page_path(page_num)
  index_hash = Digest::SHA256.hexdigest(file_key.to_s + ":" + archive_path + ":#{page_num}-")
  File.join(archive.root, "#{index_hash[0..1]}/#{index_hash[2..3]}/#{index_hash[4..-1]}")
end
page_physical_size() click to toggle source
# File lib/zksync/crypto_file.rb, line 27
def page_physical_size
  65536
end
page_size() click to toggle source
# File lib/zksync/crypto_file.rb, line 31
def page_size
  page_physical_size - Key.write_size - 1
end
read() { |read_page(n)| ... } click to toggle source
# File lib/zksync/crypto_file.rb, line 65
def read
  page_count.times do |n|
    yield read_page(n)
  end
end
read_page(page_num) click to toggle source
# File lib/zksync/crypto_file.rb, line 77
def read_page(page_num)
  plaintext = __read_page_keep_padding(page_num)
  plaintext = plaintext[0...(size % page_size)] if page_num == page_count-1
  plaintext
end
size() click to toggle source
# File lib/zksync/crypto_file.rb, line 35
def size
  inode.size
end
trim_to_size(size) click to toggle source

call me before updating inode size ensure excess pages are pruned if a file shrinks

# File lib/zksync/crypto_file.rb, line 128
def trim_to_size(size)
  old_page_count = (inode.size/page_size).ceil
  new_page_count = (size/page_size).ceil
  return if new_page_count >= old_page_count

  (new_page_count .. old_page_count).to_a.each do |page_num|
    pagefile = page_path(page_num)
    File.unlink(pagefile) if File.exists? pagefile
  end
end
valid?() click to toggle source
# File lib/zksync/crypto_file.rb, line 56
def valid?
  return true if inode.ftype == "directory"
  raise "No inode" unless inode

  digest = Digest::SHA256.new
  read { |page| digest << page }
  digest.hexdigest == inode.sha256
end
write(in_stream, mtime) click to toggle source
# File lib/zksync/crypto_file.rb, line 92
def write(in_stream, mtime)
  page_num = 0
  bytes = 0

  while page_plaintext = in_stream.read(page_size) do
    write_page(page_num, page_plaintext)
    bytes += page_plaintext.length
    page_num += 1
  end

  trim_to_size(bytes)
  inode.size = bytes
  inode.mtime = mtime
end
write_page(page_num, page_plaintext) click to toggle source
# File lib/zksync/crypto_file.rb, line 107
def write_page(page_num, page_plaintext)
  page_hash = Digest::SHA256.hexdigest(page_plaintext)
  pagefile_path = page_path(page_num)
  page_key = archive.keystore.page_key(archive_path, page_num, page_hash)
  return if page_matches?(pagefile_path, page_key)
  
  encrypted_key = file_key.encrypt(page_key.to_b)
  padding = "\0" * (page_size - page_plaintext.length)
  encrypted_data = page_key.encrypt(page_plaintext + padding)

  make_parent_directories(pagefile_path)
  File.write(pagefile_path, encrypted_key + encrypted_data)

  ts_digest = "TIMESTAMP:" + page_hash
  16.times { ts_digest = Digest::SHA256.digest(ts_digest) }
  ts = ts_digest[0..3].unpack("L").first & 0x7FFFFFFF
  File.utime(Time.at(ts), Time.at(ts), pagefile_path)
end