fackup 0.0.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/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
+