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

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.create_for(file_information) ⇒ Object



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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/nearline/archived_file.rb', line 15

def self.create_for(file_information)
  # The path doesn't actually exist and fails a File.lstat
  return nil if file_information.path_hash.nil?
  
  # We need to create a record for a link, directory or file
  archived_file = ArchivedFile.new(
    file_information.archived_file_parameters
  )
  
  begin
    # Find a new link
    if(file_information.ftype == "link")
      archived_file.ftype_data = file_information.link_target
      archived_file.save!
      file_information.manifest.archived_files << archived_file
      return archived_file          
    end

    # Find a new directory
    if (file_information.ftype=="directory")
      archived_file.save!
      file_information.manifest.archived_files << archived_file
      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 = archived_file.persist(file_information.manifest)
    unless archived_file.nil? || archived_file.frozen?
      archived_file.save!
      file_information.manifest.archived_files << archived_file
    end
    return archived_file
    # TODO: block devices, ...?

  rescue
    msg = "#{file_information.file_path} failed to persist"
    puts msg
    file_information.manifest.add_log(msg)
    return nil
  end
end

Instance Method Details

#before_destroyObject



110
111
112
# File 'lib/nearline/archived_file.rb', line 110

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

#clean_up_duplicate_contentObject

In the special case of an identical sequence existing, we can safely delete all related sequences and then destroy the file content object without the (far slower) orphan checking process



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

def clean_up_duplicate_content
  Sequence.delete_all "file_content_id = #{self.file_content.id}"
  self.file_content.destroy
end

#guarantee_pathObject



85
86
87
88
89
90
# File 'lib/nearline/archived_file.rb', line 85

def guarantee_path
  target_path = File.dirname(option_override(:path))
  if (!File.exist? target_path)
    FileUtils.mkdir_p target_path
  end        
end

#option_override(key) ⇒ Object



92
93
94
95
96
97
# File 'lib/nearline/archived_file.rb', line 92

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

#orphan_checkObject



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

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.



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/nearline/archived_file.rb', line 127

def persist(manifest)
  seq = nil
  begin
    seq = read_file
  rescue
    error = "Got error '#{$!}' on path: #{self.path}"
    manifest.add_log error
    self.orphan_check
    return nil
  end
         
  size_check(seq.file_size, manifest)
  
  # Do we have a unique sequence?
  key = seq.fingerprint
  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_fileObject



150
151
152
153
154
155
156
157
158
# File 'lib/nearline/archived_file.rb', line 150

def read_file
  File.open(self.path, "rb") do |io|
    seq = FileSequencer.new(io, self.file_content)
    while (!io.eof)
      seq.persist_segment
    end
    return seq
  end
end

#replace_content(key) ⇒ Object



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

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

#restore(*args) ⇒ Object



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/nearline/archived_file.rb', line 59

def restore(*args)
  @options = args.extract_options!

  if (self.ftype == "link")
    guarantee_path
    File.symlink(self.ftype_data,
      option_override(:path))
    # do not restore_metadata on a link
    return
  end

  if (self.ftype == "directory")
    FileUtils.mkdir_p option_override(:path)
    
    return
  end
  
  # ftype == "file"
  guarantee_path
  f = File.open(option_override(:path), "wb")
  self.file_content.restore_to(f)
  f.close
  
  return
end

#restore_metadataObject



99
100
101
102
103
104
105
106
107
108
# File 'lib/nearline/archived_file.rb', line 99

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



160
161
162
163
164
165
166
167
# File 'lib/nearline/archived_file.rb', line 160

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}"
    self.file_content.file_size = file_size
  end        
end

#unique_sequence_processed?(key, manifest) ⇒ Boolean

Returns:

  • (Boolean)


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

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

#verify_content(manifest) ⇒ Object



169
170
171
172
173
174
175
# File 'lib/nearline/archived_file.rb', line 169

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