7 class Keep < Sinatra::Base
9 mime_type :binary, 'application/octet-stream'
11 set :port, (ENV['PORT'] || '25107').to_i
12 set :bind, (ENV['IP'] || '0.0.0.0')
15 def verify_hash(data, hash)
17 Digest::MD5.hexdigest(data) == hash && hash
18 elsif hash.length == 40
19 Digest::SHA1.hexdigest(data) == hash && hash
26 if ENV['DEBUG'] and ENV['DEBUG'].match /^-?\d+/
33 def self.debuglog(loglevel, msg)
34 if debuglevel >= loglevel
35 $stderr.puts "[keepd/#{$$} #{Time.now}] #{msg}"
39 self.class.debuglog *args
47 return @@keepdirs if defined? @@keepdirs
48 # Configure backing store directories
50 rootdir = (ENV['KEEP_ROOT'] || '/').sub /\/$/, ''
51 `mount`.split("\n").each do |mountline|
52 dev, on_txt, mountpoint, type_txt, fstype, opts = mountline.split
53 if on_txt == 'on' and type_txt == 'type'
54 debuglog 2, "dir #{mountpoint} is mounted"
55 if mountpoint[0..(rootdir.length)] == rootdir + '/'
56 debuglog 2, "dir #{mountpoint} is in #{rootdir}/"
57 keepdir = "#{mountpoint.sub /\/$/, ''}/keep"
58 if File.exists? "#{keepdir}/."
59 keepdirs << { :root => "#{keepdir}", :readonly => false }
60 if opts.gsub(/[\(\)]/, '').split(',').index('ro')
61 keepdirs[-1][:readonly] = true
63 debuglog 0, "keepdir #{keepdirs[-1].inspect}"
72 def find_backfile(hash, opts)
74 keepdirs.each do |keepdir|
75 backfile = "#{keepdir[:root]}/#{subdir}/#{hash}"
76 if File.exists? backfile
78 File.open("#{keepdir[:root]}/lock", "a+") do |f|
79 if f.flock File::LOCK_EX
80 data = File.read backfile
83 if data and (!opts[:verify_hash] or verify_hash data, hash)
84 return [backfile, data]
91 get '/:locator' do |locator|
92 regs = locator.match /^([0-9a-f]{32,})/
95 backfile, data = find_backfile hash, :verify_hash => false
108 put '/:locator' do |locator|
109 data = request.body.read
110 hash = verify_hash(data, locator)
113 body "Checksum mismatch"
116 backfile, havedata = find_backfile hash, :verify_hash => true
123 keepdirs.each do |keepdir|
124 next if keepdir[:readonly]
125 backdir = "#{keepdir[:root]}/#{subdir}"
126 if !File.exists? backdir
132 backfile = "#{keepdir[:root]}/#{subdir}/#{hash}"
133 File.open("#{keepdir[:root]}/lock", "a+") do |lf|
134 if lf.flock File::LOCK_EX
135 File.open(backfile + ".tmp", "a+") do |wf|
136 if wf.flock File::LOCK_EX
137 wf.seek 0, File::SEEK_SET
139 wrote = wf.write data
141 if wrote == data.length
142 File.rename backfile+".tmp", backfile
145 File.unlink backfile+".tmp"
151 if wrote == data.length
163 if ENV['SSL_CERT'] and ENV['SSL_KEY']
165 :cert_chain_file => ENV['SSL_CERT'],
166 :private_key_file => ENV['SSL_KEY'],
167 :verify_peer => false
170 server.ssl_options = ssl_options