Class: ZKSync::CryptoFile

Inherits:
Object
  • Object
show all
Defined in:
lib/zksync/crypto_file.rb

Direct Known Subclasses

CryptoFileIndex

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(archive_path, archive) ⇒ CryptoFile

Returns a new instance of CryptoFile.



22
23
24
25
# File 'lib/zksync/crypto_file.rb', line 22

def initialize(archive_path, archive)
  @archive = archive
  @archive_path = archive_path
end

Instance Attribute Details

#archiveObject (readonly)

Returns the value of attribute archive.



20
21
22
# File 'lib/zksync/crypto_file.rb', line 20

def archive
  @archive
end

#archive_pathObject (readonly)

Returns the value of attribute archive_path.



20
21
22
# File 'lib/zksync/crypto_file.rb', line 20

def archive_path
  @archive_path
end

Instance Method Details

#__read_page_keep_padding(page_num) ⇒ Object



71
72
73
74
75
# 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_keyObject



47
48
49
# File 'lib/zksync/crypto_file.rb', line 47

def file_key
  @file_key ||= archive.keystore.file_key(archive_path)
end

#inodeObject



43
44
45
# File 'lib/zksync/crypto_file.rb', line 43

def inode
  @inode ||= archive.file_index.inode_for_path(archive_path)
end

#make_parent_directories(path) ⇒ Object



83
84
85
# File 'lib/zksync/crypto_file.rb', line 83

def make_parent_directories(path)
  FileUtils.mkdir_p(File.dirname(path))
end

#page_countObject



39
40
41
# File 'lib/zksync/crypto_file.rb', line 39

def page_count
  (size.to_f/page_size).ceil
end

#page_matches?(pagefile_path, key) ⇒ Boolean

Returns:

  • (Boolean)


87
88
89
90
# 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) ⇒ Object



51
52
53
54
# 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_sizeObject



27
28
29
# File 'lib/zksync/crypto_file.rb', line 27

def page_physical_size
  65536
end

#page_sizeObject



31
32
33
# File 'lib/zksync/crypto_file.rb', line 31

def page_size
  page_physical_size - Key.write_size - 1
end

#readObject



65
66
67
68
69
# 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) ⇒ Object



77
78
79
80
81
# 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

#sizeObject



35
36
37
# File 'lib/zksync/crypto_file.rb', line 35

def size
  inode.size
end

#trim_to_size(size) ⇒ Object

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



128
129
130
131
132
133
134
135
136
137
# 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?Boolean

Returns:

  • (Boolean)


56
57
58
59
60
61
62
63
# 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) ⇒ Object



92
93
94
95
96
97
98
99
100
101
102
103
104
105
# 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) ⇒ Object



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# 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