Skip to content

Instantly share code, notes, and snippets.

@JustinAiken
Created October 3, 2016 00:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save JustinAiken/3e8121a41a3a5cd4a4fdd5a29b825513 to your computer and use it in GitHub Desktop.
Save JustinAiken/3e8121a41a3a5cd4a4fdd5a29b825513 to your computer and use it in GitHub Desktop.
Crashplan, wut?
class BackedUpFile
PARSING_REGEX = /(?<status>[IW]) (?<date>\d\d\/\d\d\/\d\d) (?<time>\d\d:\d\d[AP]M) (?<some_num>\d+) (?<hash>\w+) (?<other_num>\d+) (?<filename>.*) \((?<size>\d+)\) \[(?<flags>[\d\,]+)\]/
def self.from(line)
return unless match_data = PARSING_REGEX.match(line)
result = match_data.names.inject({}) do |memo, name|
if name == 'flags'
memo[:flags] = match_data[name.to_sym].split(',')
else
memo[name.to_sym] = match_data[name.to_sym]
end
memo
end
date = result.delete :date
time = result.delete :time
result[:datetime] = DateTime.strptime "#{date} #{time}", "%m/%d/%y %I:%M%p"
self.new result
rescue => e
puts e.inspect
puts "SKIP: #{line}"
end
attr_reader :params
delegate *%i{status datetime some_num hash other_num filename size flags}, to: :params
def initialize(params)
@params = OpenStruct.new(params)
end
def real_path
# TODO: Top-level config constant or somethin:
filename.gsub("/data", "/Volumes")
end
def missing?
!File.exist? real_path
end
def size_mismatch?
size.to_i != local_size
end
def local_size
File.size real_path
end
end
class BackedUpFiles
attr_accessor :backed_up_files, :log_files, :missing_files, :unsized_files
delegate :count, to: :backed_up_files
def initialize(log_files)
@backed_up_files = {}
@log_files = log_files
@missing_files = []
@unsized_files = []
end
SKIP_REGEX = /\/\.\_|appdata|data\/apps|sparsebundle|DS_store/i
def load_logs!
log_files.each do |file|
File.open(file).each_line { |line| handle line }
end
end
def check!
backed_up_files.each do |_, bf|
if bf.missing?
puts "MISSING: #{bf.inspect}"
missing_files << bf.filename
elsif bf.size_mismatch?
puts "SIZE: Local(#{bf.local_size}) / #{bf.inspect}"
unsized_files << bf.filename
else
next
end
end
end
def report!
File.open 'missing.log', 'a' do |f|
missing_files.each { |mf| f.write mf; f.write "\n" }
end
File.open 'unsized_files.log', 'a' do |f|
unsized_files.each { |uf| f.write uf; f.write "\n" }
end
end
private
def handle(line)
return if SKIP_REGEX =~ line
return unless file = BackedUpFile.from(line)
if existing = backed_up_files[file.filename]
return if existing.datetime > file.datetime
end
backed_up_files[file.filename] = file
end
end
require 'set'
require 'ostruct'
require 'active_support'
require 'active_support/all'
require 'date'
require 'time'
require 'pry'
require_relative 'backed_up_file'
require_relative 'backed_up_files'
LOG_FILES = %w{backup_files.log.1 backup_files.log.0}
backed_up_files = BackedUpFiles.new LOG_FILES
puts "Loading files..."
backed_up_files.load_logs!
puts "Loading done - #{backed_up_files.count} files backed up on CrashPlan according to log."
backed_up_files.check!
puts "Check done, writing files"
backed_up_files.report!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment