Class: Nearline::Models::ArchivedFile

Inherits:
ActiveRecord::Base
  • Object
show all
Defined in:
lib/nearline/archived_file.rb

Overview

Represents file metadata and possible related FileContent for a single file on a single system

Defined Under Namespace

Classes: FileInformation

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.create_for(file_path, manifest) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/nearline/archived_file.rb', line 12

def self.create_for(file_path, manifest)        
  file_information = FileInformation.new(file_path, manifest)

  # The path doesn't actually exist and fails a File.stat
  return nil if file_information.path_hash.nil?

  # If we find an exising entry, use it
  hit = self.find_by_path_hash(file_information.path_hash)
  return hit unless hit.nil?
  
  # We need to create a record for either a directory or file
  archived_file = ArchivedFile.new(
    file_information.archived_file_parameters
  )
  
  # Find a new directory
  if (file_information.is_directory)
    archived_file.save!
    return archived_file
  end
  
  # Find a new file that needs persisted
  archived_file.file_content.file_size = 
    [file_information.stat.size].pack('Q').unpack('L').first # HACK for Windows
  archived_file.persist(manifest)
  archived_file.save!
  archived_file
  
  # TODO: Symbolic links, block devices, ...?
end

Instance Method Details

#before_destroyObject



133
134
135
# File 'lib/nearline/archived_file.rb', line 133

def before_destroy
  self.file_content.orphan_check if !self.file_content.nil?
end

#clean_up_duplicate_contentObject



213
214
215
216
# File 'lib/nearline/archived_file.rb', line 213

def clean_up_duplicate_content
  Sequence.delete_all("file_content_id=#{self.file_content.id}")
  self.file_content.orphan_check
end

#option_override(key) ⇒ Object



115
116
117
118
119
120
# File 'lib/nearline/archived_file.rb', line 115

def option_override(key)
  if (@options.has_key?(key))
    return @options[key]
  end
  return self.send(key.to_s)
end

#orphan_checkObject



137
138
139
140
141
# File 'lib/nearline/archived_file.rb', line 137

def orphan_check
  if self.manifests.size == 1
    self.destroy
  end
end

#persist(manifest) ⇒ Object

Actually persist the file to the repository It has already been determined that a new ArchivedFile record is necessary and the file requires persisting

But, the content may be identical to something else, and we won’t know that until we complete the process and have to clean up our mess.



150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/nearline/archived_file.rb', line 150

def persist(manifest)
  whole_file_hash = Digest::SHA1.new
  file_size = 0
  begin
    file_size = read_file_counting_bytes(whole_file_hash)
  rescue
    manifest.add_log "Got error '#{$!}' on path: #{self.path}"
    self.orphan_check
    return nil
  end
  
  size_check(file_size, manifest)
  
  # Do we have a unique sequence?
  key = whole_file_hash.hexdigest
  return self if unique_sequence_processed?(key, manifest)
          
  # Handle the case where the sequence is not unique...
  clean_up_duplicate_content
  replace_content(key)
  self
end

#read_file_counting_bytes(whole_file_hash) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/nearline/archived_file.rb', line 173

def read_file_counting_bytes(whole_file_hash)
  sequencer = FileSequencer.new(self.file_content)
  file_size = 0
  buffer = ""
  File.open(self.path, "rb") do |io|
    while (!io.eof) do
      io.read(Block::MAX_SIZE, buffer)
      file_size += buffer.size
      whole_file_hash.update(buffer)
      sequencer.preserve_content(buffer)
    end
  end
  return file_size
end

#replace_content(key) ⇒ Object



218
219
220
221
# File 'lib/nearline/archived_file.rb', line 218

def replace_content(key)
  self.file_content = FileContent.find_by_fingerprint(key)
  self.save!                
end

#restore(*args) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/nearline/archived_file.rb', line 97

def restore(*args)
  @options = args.extract_options!
  if (self.is_directory)
    FileUtils.mkdir_p option_override(:path)
    
    return
  end
  target_path = File.dirname(option_override(:path))
  if (!File.exist? target_path)
    FileUtils.mkdir_p target_path
  end
  f = File.open(option_override(:path), "wb")
  self.file_content.restore_to(f)
  f.close
  
  return
end

#restore_metadataObject



122
123
124
125
126
127
128
129
130
131
# File 'lib/nearline/archived_file.rb', line 122

def 
  path = option_override(:path)
  mtime = option_override(:mtime)
  uid = option_override(:uid)
  gid = option_override(:gid)
  mode = option_override(:mode)
  File.utime(0,Time.at(mtime),path)
  File.chown(uid, gid, path)
  File.chmod(mode, path)
end

#size_check(file_size, manifest) ⇒ Object



188
189
190
191
192
193
194
# File 'lib/nearline/archived_file.rb', line 188

def size_check(file_size, manifest)
  if file_size != self.file_content.file_size
    manifest.add_log "recorded file length #{file_size} " +
      "does not match #{self.file_content.file_size} " +
      "reported by the file system on path: #{self.path}"
  end        
end

#unique_sequence_processed?(key, manifest) ⇒ Boolean

Returns:

  • (Boolean)


202
203
204
205
206
207
208
209
210
211
# File 'lib/nearline/archived_file.rb', line 202

def unique_sequence_processed?(key,manifest)
  if self.file_content.unique_fingerprint?(key)
    self.file_content.fingerprint = key
    self.file_content.save!
    self.save!
    verify_content(manifest)
    return true
  end
  false
end

#verify_content(manifest) ⇒ Object



196
197
198
199
200
# File 'lib/nearline/archived_file.rb', line 196

def verify_content(manifest)
  unless (self.file_content.verified?)
    manifest.add_log "failed verification on path: #{self.path}"
  end        
end