lnbackup 2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/bin/lnbackup +187 -0
- data/bin/lnmount +16 -0
- data/bin/lnstat +4 -0
- data/bin/lnumount +4 -0
- data/bin/pc_backup +66 -0
- data/bin/sql_backup.sh +123 -0
- data/lib/lnbackup/backup.rb +1938 -0
- data/lib/lnbackup/freespace.rb +73 -0
- data/lib/lnbackup/util.rb +123 -0
- data/lib/lnbackup/version.rb +3 -0
- data/lib/lnbackup.rb +21 -0
- metadata +63 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
class FreeSpaceCalc
|
2
|
+
def initialize( path, free_files=0, free_blocks=10, log=nil )
|
3
|
+
@log = log
|
4
|
+
@stat = FileSystem.stat(path)
|
5
|
+
|
6
|
+
if (free_blocks.class == String) and (free_blocks =~ /^(\d+)%$/)
|
7
|
+
free_blocks = @stat.blocks * $1.to_i / 100
|
8
|
+
else
|
9
|
+
free_blocks = free_blocks.to_i rescue 100
|
10
|
+
end
|
11
|
+
# vzdy nechame alespon 100 volnych bloku (typicky blok=4kB)
|
12
|
+
@free_blocks = free_blocks < 100 ? 100 : free_blocks
|
13
|
+
|
14
|
+
if (free_files.class == String) and (free_files =~ /^(\d+)%$/)
|
15
|
+
free_files = @stat.files * $1.to_i / 100
|
16
|
+
else
|
17
|
+
free_files = free_files.to_i rescue 100
|
18
|
+
end
|
19
|
+
# vzdy nechame alespon 100 volnych inodu (pokud dojdou, fs se umi prekne
|
20
|
+
# podelat a ani fsck z toho nema radost)
|
21
|
+
@free_files = free_files < 100 ? 100 : free_files
|
22
|
+
end
|
23
|
+
|
24
|
+
def much_used?
|
25
|
+
@stat = FileSystem.stat(@stat.path)
|
26
|
+
if Process.euid == 0
|
27
|
+
if (@stat.blocks_free < @free_blocks) or (@stat.files_free < @free_files)
|
28
|
+
@log.debug { "FreeSpaceCalc: much used (euid=#{Process.euid}) (#{@stat.blocks_free} < #{@free_blocks}) or (#{@stat.files_free} < #{@free_files})" }
|
29
|
+
return true
|
30
|
+
end
|
31
|
+
else
|
32
|
+
if (@stat.blocks_avail < @free_blocks) or (@stat.files_avail < @free_files)
|
33
|
+
@log.debug { "FreeSpaceCalc: much used (euid=#{Process.euid}) (#{@stat.blocks_avail} < #{@free_blocks}) or (#{@stat.files_avail} < #{@free_files})" }
|
34
|
+
return true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
return false
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_free
|
41
|
+
@stat = FileSystem.stat(@stat.path)
|
42
|
+
return [ @stat.blocks_free, @stat.files_free ]
|
43
|
+
end
|
44
|
+
|
45
|
+
def can_backup?( path )
|
46
|
+
begin
|
47
|
+
@stat = FileSystem.stat(@stat.path)
|
48
|
+
if Process.euid == 0
|
49
|
+
if (@stat.blocks_free < @free_blocks) or (@stat.files_free < @free_files) or
|
50
|
+
(File.size(path) >= ( @stat.block_size * (@stat.blocks_free-@free_blocks) ))
|
51
|
+
|
52
|
+
@log.debug { "FreeSpaceCalc: (euid=#{Process.euid}) can't backup #{path} size #{File.size(path)}" }
|
53
|
+
@log.debug { "FreeSpaceCalc: blocks avail: #{@stat.blocks_free} should have: #{@free_blocks}" }
|
54
|
+
@log.debug { "FreeSpaceCalc: files avail: #{@stat.files_free} should have: #{@free_files}" }
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
else
|
58
|
+
if (@stat.blocks_avail < @free_blocks) or (@stat.files_avail < @free_files) or
|
59
|
+
(File.size(path) >= ( @stat.block_size * (@stat.blocks_avail-@free_blocks) ))
|
60
|
+
|
61
|
+
@log.debug { "FreeSpaceCalc: (euid=#{Process.euid}) can't backup #{path} size #{File.size(path)}" }
|
62
|
+
@log.debug { "FreeSpaceCalc: blocks avail: #{@stat.blocks_avail} should have: #{@free_blocks}" }
|
63
|
+
@log.debug { "FreeSpaceCalc: files avail: #{@stat.files_avail} should have: #{@free_files}" }
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
return true
|
68
|
+
rescue Errno::ENOENT # pokud soubor, ktery kontrolujeme prestal existovat,
|
69
|
+
# podminka je trivialne splnena
|
70
|
+
return true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
class File
|
2
|
+
TOO_BIG = 1024 * 1024 * 2 # 2MB
|
3
|
+
|
4
|
+
# a variant of syscopy, that propagates exception Errno::ENOSPC
|
5
|
+
# and handles some extra error states
|
6
|
+
#
|
7
|
+
def File.syscopy2 from, to
|
8
|
+
fsize = size(from)
|
9
|
+
fsize = 1024 if fsize < 512
|
10
|
+
fsize = TOO_BIG if fsize > TOO_BIG
|
11
|
+
fmode = stat(from).mode
|
12
|
+
tpath = to
|
13
|
+
not_exist = !exist?(tpath)
|
14
|
+
begin
|
15
|
+
from = open(from, "r")
|
16
|
+
from.binmode
|
17
|
+
to = open(to, "w")
|
18
|
+
to.binmode
|
19
|
+
|
20
|
+
while true
|
21
|
+
r = from.sysread(fsize)
|
22
|
+
rsize = r.size
|
23
|
+
w = 0
|
24
|
+
while w < rsize
|
25
|
+
t = to.syswrite(r[w, rsize - w])
|
26
|
+
w += t
|
27
|
+
end
|
28
|
+
end
|
29
|
+
rescue EOFError
|
30
|
+
ret = true
|
31
|
+
rescue Errno::ENOSPC
|
32
|
+
raise
|
33
|
+
rescue
|
34
|
+
ret = false
|
35
|
+
ensure
|
36
|
+
begin; to.close; rescue; end
|
37
|
+
begin; from.close; rescue; end
|
38
|
+
end
|
39
|
+
chmod(fmode, tpath) if not_exist
|
40
|
+
ret
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Hash
|
45
|
+
# "aktualizace" hashe jinou hashi: existujici klice jsou prepsany,
|
46
|
+
# neexistujici pridany a to cele rekurzivne po hashich
|
47
|
+
def append( hash )
|
48
|
+
hash.each_pair do |k,v|
|
49
|
+
if self.has_key?(k)
|
50
|
+
case v
|
51
|
+
when Hash
|
52
|
+
self[k].append(v)
|
53
|
+
else
|
54
|
+
self[k] = v
|
55
|
+
end
|
56
|
+
else
|
57
|
+
self[k] = v
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class SysCopyFailure < StandardError
|
64
|
+
end
|
65
|
+
|
66
|
+
class MakeSpaceFailure < StandardError
|
67
|
+
attr_accessor :code
|
68
|
+
def initialize(code)
|
69
|
+
@code = code
|
70
|
+
super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Date
|
75
|
+
# formatovani data do cesty pro zalohu
|
76
|
+
def backup_dir(hourly)
|
77
|
+
if hourly
|
78
|
+
"%d/%02d/%02d-%02d" % [year, month, day, Time.now.hour]
|
79
|
+
else
|
80
|
+
"%d/%02d/%02d" % [year, month, day]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
module Find
|
86
|
+
#
|
87
|
+
# reimplementace funkce "find" s osetrenim vyjimky Errno::EOVERFLOW, ktera minimalne
|
88
|
+
# na ruby1.6 nastava pri zachazeni (napr. stat) se souborem o velikosti >2GB
|
89
|
+
#
|
90
|
+
# pruchod je udelan pomoci zasobniku s hloubkou, aby bylo mozne usetrit pamet
|
91
|
+
#
|
92
|
+
def Find.find3(*path)
|
93
|
+
path.collect!{|d| [d.dup,0]}
|
94
|
+
while temp = path.shift
|
95
|
+
catch(:prune) do
|
96
|
+
(file, depth) = temp
|
97
|
+
yield [file, depth]
|
98
|
+
begin
|
99
|
+
if File.lstat(file).directory? then
|
100
|
+
d = Dir.open(file)
|
101
|
+
begin
|
102
|
+
for f in d
|
103
|
+
next if f == "." or f == ".."
|
104
|
+
if File::ALT_SEPARATOR and file =~ /^(?:[\/\\]|[A-Za-z]:[\/\\]?)$/ then
|
105
|
+
f = file + f
|
106
|
+
elsif file == "/" then
|
107
|
+
f = "/" + f
|
108
|
+
else
|
109
|
+
f = File.join(file, f)
|
110
|
+
end
|
111
|
+
path.unshift [f, depth+1]
|
112
|
+
end
|
113
|
+
ensure
|
114
|
+
d.close
|
115
|
+
end
|
116
|
+
end
|
117
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::EOVERFLOW
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
data/lib/lnbackup.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'lnbackup/backup.rb'
|
2
|
+
require 'lnbackup/freespace.rb'
|
3
|
+
require 'lnbackup/util.rb'
|
4
|
+
require 'lnbackup/version.rb'
|
5
|
+
|
6
|
+
require 'find'
|
7
|
+
require 'date'
|
8
|
+
#require 'ftools'
|
9
|
+
require 'fileutils'
|
10
|
+
begin
|
11
|
+
require 'filesystem'
|
12
|
+
rescue LoadError
|
13
|
+
require 'sys/filesystem'
|
14
|
+
include Sys
|
15
|
+
FileSystem = Filesystem
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
require 'logger'
|
20
|
+
require 'time'
|
21
|
+
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: lnbackup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '2.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Martin Povolny
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-08 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! "\nLnbackup is a hardlink backup system for hard drives. \n\nLnbackup
|
15
|
+
operates in a way similar to the '--link-dest' switch in rsync.\n\nIt creates incremental
|
16
|
+
backups using hardlinks so that each backup seems like a full backup. Additionaly
|
17
|
+
it can make (using hardlinks) bootable mirror from the latest backup.\n\nObviously
|
18
|
+
the target filesystem of lnbackup needs to support hardlinks.\n\nIt's run on ~200
|
19
|
+
servers for several years and it is considered stable.\n\nRead the man page for
|
20
|
+
more information."
|
21
|
+
email: martin.povolny@gmail.com
|
22
|
+
executables: []
|
23
|
+
extensions: []
|
24
|
+
extra_rdoc_files: []
|
25
|
+
files:
|
26
|
+
- lib/lnbackup.rb
|
27
|
+
- lib/lnbackup/version.rb
|
28
|
+
- lib/lnbackup/freespace.rb
|
29
|
+
- lib/lnbackup/backup.rb
|
30
|
+
- lib/lnbackup/util.rb
|
31
|
+
- bin/lnbackup
|
32
|
+
- bin/lnumount
|
33
|
+
- bin/lnmount
|
34
|
+
- bin/lnstat
|
35
|
+
- bin/pc_backup
|
36
|
+
- bin/sql_backup.sh
|
37
|
+
homepage: https://github.com/martinpovolny/lnbackup
|
38
|
+
licenses: []
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options:
|
41
|
+
- --title
|
42
|
+
- Lnbackup -- hardlink backup system for hard drives.
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
none: false
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '0'
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
53
|
+
requirements:
|
54
|
+
- - ! '>='
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
requirements: []
|
58
|
+
rubyforge_project:
|
59
|
+
rubygems_version: 1.8.24
|
60
|
+
signing_key:
|
61
|
+
specification_version: 3
|
62
|
+
summary: Hardlink backup system for hard drives.
|
63
|
+
test_files: []
|