fackup 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/fackup ADDED
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fackup/cli'
4
+
5
+ if Process.uid != 0
6
+ $stderr.puts "You should run the program as root,"
7
+ $stderr.puts "files without right permissions will be skipped"
8
+ end
9
+
10
+ FackUp::CLI.start(ARGV)
data/lib/fackup/cli.rb ADDED
@@ -0,0 +1,84 @@
1
+ #--
2
+ # Copyleft shura. [ shura1991@gmail.com ]
3
+ #
4
+ # This file is part of fackup.
5
+ #
6
+ # fackup is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # fackup is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with fackup. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'thor'
21
+ require 'fackup'
22
+
23
+ module FackUp
24
+ class CLI < Thor
25
+ class_option :help, type: :boolean, desc: 'Show help usage'
26
+
27
+ desc 'version', 'Show current version'
28
+ map '-v' => :version, '--version' => :version
29
+ def version
30
+ puts "fackup v#{FackUp::VERSION}"
31
+ end
32
+
33
+ desc 'add FILE... [OPTIONS]', 'add file to backup'
34
+ method_option :recursive, aliases: '-r', type: :boolean, default: false,
35
+ desc: 'Add files in directories recursively'
36
+ def add (*files)
37
+ files.each {|file|
38
+ if File.directory?(file)
39
+ if options[:recursive]
40
+ add(Dir["#{file}/*"])
41
+ else
42
+ say '* ', :red
43
+ say "#{file} is a directory."
44
+ end
45
+ elsif File.file?(file)
46
+ unless DB << file
47
+ say '* ', :red
48
+ say "Can't add #{file}, unknown error."
49
+ end
50
+ else
51
+ say '* ', :red
52
+ say "Can't add #{file}, unknown format."
53
+ end
54
+ }
55
+ end
56
+
57
+ desc 'backup [FILE]', 'backup files'
58
+ def backup (file=File.join(Dir.home, 'fackup_backup.img'))
59
+ FackUp.backup(file)
60
+ end
61
+
62
+ desc 'restore IMAGE [OPTIONS]', 'restore image file'
63
+ method_option :force, aliases: '-f', type: :boolean, default: false,
64
+ desc: 'Replace files without controls'
65
+ def restore (image)
66
+ FackUp.send((options[:force] ? :restore_f : :restore), image)
67
+ end
68
+
69
+ desc 'list [IMAGE]', 'list backup files, if given, list file in image files'
70
+ def list (image=nil)
71
+ if image and File.esists?(image)
72
+ FackUp::Image.new(image, 'r', true) {|img|
73
+ img.each {|file|
74
+ puts file.name
75
+ }
76
+ }
77
+ else
78
+ DB.each {|file|
79
+ puts file
80
+ } if File.exists?(DB.path)
81
+ end
82
+ end
83
+ end
84
+ end
data/lib/fackup/db.rb ADDED
@@ -0,0 +1,76 @@
1
+ #--
2
+ # Copyleft shura. [ shura1991@gmail.com ]
3
+ #
4
+ # This file is part of fackup.
5
+ #
6
+ # fackup is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # fackup is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with fackup. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'csv'
21
+
22
+ module FackUp
23
+ module DB
24
+ PATH = File.join(Dir.home, '.fackup_files')
25
+
26
+ class << self
27
+ def path= (p)
28
+ @path = File.realpath(p)
29
+ end
30
+
31
+ def path
32
+ @path ||= PATH
33
+ end
34
+
35
+ def all
36
+ touch(DB.path)
37
+ CSV.read(DB.path).map(&:first)
38
+ end
39
+
40
+ def each (&blk)
41
+ all.each(&blk)
42
+ end
43
+
44
+ def push (path)
45
+ path = File.realpath(path)
46
+
47
+ return false if all.include?(path)
48
+
49
+ CSV.open(DB.path, 'ab') {|csv|
50
+ csv << [path]
51
+ }
52
+ true
53
+ end
54
+ alias << push
55
+
56
+ def delete (path)
57
+ path = File.realpath(path)
58
+
59
+ a = all
60
+ return false unless a.include?(path)
61
+
62
+ CSV.open(DB.path, 'wb') {|csv|
63
+ (a - [path]).each {|p|
64
+ csv << [p]
65
+ }
66
+ }
67
+ true
68
+ end
69
+
70
+ private
71
+ def touch (file)
72
+ File.open(file, 'a') {}
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,120 @@
1
+ #--
2
+ # Copyleft shura. [ shura1991@gmail.com ]
3
+ #
4
+ # This file is part of fackup.
5
+ #
6
+ # fackup is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # fackup is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with fackup. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ module FackUp
21
+ class Image
22
+ class File < Struct.new(:name, :io, :time, :start, :size, :uid, :gid, :mode)
23
+ def pack
24
+ "%s\0%s" % [name, [time.to_i, start, size, uid, gid, mode].pack(PACK_TEMPLATE)]
25
+ end
26
+
27
+ def content
28
+ io.seek(start)
29
+ io.read(size)
30
+ end
31
+ end
32
+
33
+ PACK_TEMPLATE = "LLLIII"
34
+ PACK_SIZE = [0, 0, 0, 0, 0, 0].pack(PACK_TEMPLATE).bytesize
35
+
36
+ def initialize (file, mode='r', force=false)
37
+ @files = []
38
+
39
+ ::File.open(file, 'a'){} if force
40
+
41
+ if mode == 'r'
42
+ raise Errno::ENOENT, "File doesn't exist" unless ::File.exists?(file)
43
+
44
+ @fd = ::File.open(file, 'rb')
45
+
46
+ load_file
47
+
48
+ [:push, :<<, :save].each {|meth|
49
+ (class << self; self; end).class_eval { undef_method(meth) }
50
+ }
51
+ elsif mode == 'w'
52
+ @fd = ::File.open(file, 'wb')
53
+ (class << self; self; end).class_eval { undef_method(:each) }
54
+ else
55
+ raise "Unrecognized mode"
56
+ end
57
+
58
+ if block_given?
59
+ yield self
60
+ close
61
+ end
62
+ end
63
+
64
+ def push (path)
65
+ path = ::File.realpath(path)
66
+ io = ::File.open(path, 'rb')
67
+ stat = io.stat
68
+
69
+ @files << File.new(path, io, stat.mtime, 0, io.size, stat.uid, stat.gid, stat.mode)
70
+ self
71
+ end
72
+ alias << push
73
+
74
+ def each(&blk)
75
+ @files.each(&blk)
76
+ end
77
+
78
+ def save
79
+ @fd.write(header)
80
+ @fd.write("\0")
81
+ @files.each {|file|
82
+ @fd.write(file.content)
83
+ }
84
+ end
85
+
86
+ def close
87
+ if self.respond_to?(:save)
88
+ save
89
+ end
90
+
91
+ @fd.close
92
+ end
93
+
94
+ private
95
+ def header
96
+ last = @files.map {|x| x.name.bytesize }.inject(:+) + @files.size * (1 + PACK_SIZE) + 1
97
+ @files.map {|file|
98
+ file.dup.tap {|x| x.start = last }.pack.tap {
99
+ last += file.size
100
+ }
101
+ }.join
102
+ end
103
+
104
+ def load_file
105
+ @files = []
106
+ loop {
107
+ if @fd.gets("\0").size > 1
108
+ path = $_.strip
109
+
110
+ args = @fd.read(PACK_SIZE).unpack(PACK_TEMPLATE)
111
+ args[0] = Time.at(args[0])
112
+
113
+ @files << File.new(path, @fd, *args)
114
+ else
115
+ break
116
+ end
117
+ }
118
+ end
119
+ end
120
+ end
data/lib/fackup.rb ADDED
@@ -0,0 +1,85 @@
1
+ #--
2
+ # Copyleft shura. [ shura1991@gmail.com ]
3
+ #
4
+ # This file is part of fackup.
5
+ #
6
+ # fackup is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # fackup is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with fackup. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'fileutils'
21
+ require 'fackup/db'
22
+ require 'fackup/image'
23
+
24
+ module FackUp
25
+ VERSION = '0.0.1'
26
+
27
+ def self.restore (image)
28
+ FackUp::Image.new(image, 'r', true) {|img|
29
+ img.each {|file|
30
+ FileUtils.mkdir_p(File.dirname(file.name))
31
+
32
+ if File.exists?(file.name)
33
+ stat = File.stat(file.name)
34
+
35
+ next if file.time == stat.mtime and file.size == File.size(file.name) and
36
+ file.uid == stat.uid and file.gid == stat.gid and file.mode == stat.mod
37
+ end
38
+
39
+ begin
40
+ File.open(file.name, 'wb') {|f|
41
+ f.write(file.content)
42
+ }
43
+ rescue Errno::EACCES
44
+ next
45
+ end
46
+
47
+ File.chown(file.uid, file.gid, file.name)
48
+ File.chmod(file.mode, file.name)
49
+ File.utime(file.time, file.time, file.name)
50
+ }
51
+ }
52
+ end
53
+
54
+ def self.restore_f (image)
55
+ FackUp::Image.new(image, 'r', true) {|img|
56
+ img.each {|file|
57
+ FileUtils.mkdir_p(File.dirname(file.name))
58
+
59
+ begin
60
+ File.open(file.name, 'wb') {|f|
61
+ f.write(file.content)
62
+ }
63
+ rescue Errno::EACCES
64
+ next
65
+ end
66
+
67
+ File.chown(file.uid, file.gid, file.name)
68
+ File.chmod(file.mode, file.name)
69
+ File.utime(file.time, file.time, file.name)
70
+ }
71
+ }
72
+ end
73
+
74
+ def self.backup (file)
75
+ FackUp::Image.new(file, 'w', true) {|img|
76
+ DB.each {|file|
77
+ unless File.readable?(file)
78
+ next
79
+ end
80
+
81
+ img << file
82
+ }
83
+ }
84
+ end
85
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fackup
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - shura
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-07-05 00:00:00 +02:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: thor
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description: Simple tool to do simple backups
34
+ email: shura1991@gmail.com
35
+ executables:
36
+ - fackup
37
+ extensions: []
38
+
39
+ extra_rdoc_files: []
40
+
41
+ files:
42
+ - lib/fackup/cli.rb
43
+ - lib/fackup/image.rb
44
+ - lib/fackup/db.rb
45
+ - lib/fackup.rb
46
+ - bin/fackup
47
+ has_rdoc: true
48
+ homepage: http://github.com/shurizzle/fackup
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options: []
53
+
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ segments:
62
+ - 0
63
+ version: "0"
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project:
75
+ rubygems_version: 1.3.7
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Simple tool to do simple backups
79
+ test_files: []
80
+