time_machine_tools 1.0.0

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.
Files changed (3) hide show
  1. data/bin/tm_copy +52 -0
  2. data/lib/time_machine_tools.rb +179 -0
  3. metadata +49 -0
data/bin/tm_copy ADDED
@@ -0,0 +1,52 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #--
4
+ # /* ***** BEGIN LICENSE BLOCK *****
5
+ # *
6
+ # * time_machine_tools.rb
7
+ # * Copyright (c) 2012 Colin J. Fuller
8
+ # *
9
+ # * Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ # * of this software and associated documentation files (the Software), to deal
11
+ # * in the Software without restriction, including without limitation the rights
12
+ # * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ # * copies of the Software, and to permit persons to whom the Software is
14
+ # * furnished to do so, subject to the following conditions:
15
+ # *
16
+ # * The above copyright notice and this permission notice shall be included in
17
+ # * all copies or substantial portions of the Software.
18
+ # *
19
+ # * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ # * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ # * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ # * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ # * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ # * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ # * SOFTWARE.
26
+ # *
27
+ # * ***** END LICENSE BLOCK ***** */
28
+ #++
29
+
30
+ require 'time_machine_tools'
31
+
32
+ # usage:
33
+ # tm_copy mountpoint sourcepath destpath
34
+ #
35
+ # where mountpoint is the location to which the backup drive is mounted (e.g. /mnt/my_backup)
36
+ # sourcepath is the source folder you want to copy, specified relative to mountpoint. Its contents
37
+ # will be copied, but the folder itself will not.
38
+ # destpath is the destination folder. The contents of sourcepath will be copied (including
39
+ # subfolders, which will be copied recursively) into destpath.
40
+ #
41
+
42
+ mount_point = ARGV[0]
43
+ source_path = ARGV[1]
44
+ dest_path = ARGV[2]
45
+
46
+ tmc = TimeMachineTools::TMCopier.new(mount_point)
47
+
48
+ origin_path = File.join(mount_point, source_path)
49
+
50
+ tmc.copy_dir(origin_path, dest_path)
51
+
52
+
@@ -0,0 +1,179 @@
1
+ #--
2
+ # /* ***** BEGIN LICENSE BLOCK *****
3
+ # *
4
+ # * time_machine_tools.rb
5
+ # * Copyright (c) 2012 Colin J. Fuller
6
+ # *
7
+ # * Permission is hereby granted, free of charge, to any person obtaining a copy
8
+ # * of this software and associated documentation files (the Software), to deal
9
+ # * in the Software without restriction, including without limitation the rights
10
+ # * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11
+ # * copies of the Software, and to permit persons to whom the Software is
12
+ # * furnished to do so, subject to the following conditions:
13
+ # *
14
+ # * The above copyright notice and this permission notice shall be included in
15
+ # * all copies or substantial portions of the Software.
16
+ # *
17
+ # * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
+ # * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
+ # * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20
+ # * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
+ # * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22
+ # * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ # * SOFTWARE.
24
+ # *
25
+ # * ***** END LICENSE BLOCK ***** */
26
+ #++
27
+
28
+ ##
29
+ # Time machine tools is a utility for copying data off an Apple time machine
30
+ # backup on a system (e.g. linux) that does not support the type of hard links
31
+ # used for the backup.
32
+ #
33
+ # usage (with install through rubygems):
34
+ #
35
+ # gem install time_machine_tools
36
+ # tm_copy mountpoint sourcepath destpath
37
+ #
38
+ # where mountpoint is the location to which the backup drive is mounted (e.g. /mnt/my_backup)
39
+ # sourcepath is the source folder you want to copy, specified relative to mountpoint. Its contents
40
+ # will be copied, but the folder itself will not.
41
+ # destpath is the destination folder. The contents of sourcepath will be copied (including
42
+ # subfolders, which will be copied recursively) into destpath.
43
+ #
44
+
45
+ require 'fileutils'
46
+
47
+ module TimeMachineTools
48
+
49
+ Dirlisting_prefix = "dir_"
50
+ Entries_dir = ".HFS+ Private Directory Data\r"
51
+
52
+
53
+ class TMCopier
54
+
55
+ ##
56
+ # Creates a new TMCopier.
57
+ # @param mount_point the path to which the backup drive is mounted
58
+ # @param path_to_dir_entries the path relative to mount_point that contains the numbered directories for the backup
59
+ # this defaults to the hidden folder ".HFS+ Private Directory Data\r", which to my knowledge
60
+ # is the standard location for time machine backups.
61
+ #
62
+ def initialize(mount_point, path_to_dir_entries=Entries_dir)
63
+
64
+ @path_to_dir_entries = path_to_dir_entries
65
+ @mount_point = mount_point
66
+
67
+ #I think all the linked directories are numbered higher than this.
68
+ @min_link_count = 1000
69
+
70
+ end
71
+
72
+ attr_accessor :min_link_count
73
+
74
+ ##
75
+ # Maps a path that would have existed on the system being backed up to its actual path on the time machine drive.
76
+ # @param path the path to be mapped (which is relative to the root of the filesystem being backed up).
77
+ # @return a string containing the path to the actual location of the directory on the backup drive.
78
+ #
79
+ def get_non_linked_dir_path_for_path(path)
80
+
81
+ #check if the directory exists (either as an actual directory or hard link) or whether we need to get the parent first
82
+
83
+ if File.exist?(path) then
84
+
85
+ #check first if we can go and get it directly
86
+
87
+ if File.directory?(path) then
88
+
89
+ return path
90
+
91
+ end
92
+
93
+ #otherwise, find the index from hard link count
94
+
95
+ index = File.stat(path).nlink
96
+
97
+ final_dir = File.join(@mount_point, @path_to_dir_entries, Dirlisting_prefix + index.to_s)
98
+
99
+ return final_dir
100
+
101
+ end
102
+
103
+ #recursively get the parent directory
104
+
105
+ split_path = File.split(path)
106
+
107
+ parent_path = get_non_linked_dir_path_for_path(split_path[0])
108
+
109
+ get_non_linked_dir_path_for_path(File.join(parent_path, split_path[1]))
110
+
111
+ end
112
+
113
+ ##
114
+ # Copy the contents of the named directory into the named destination.
115
+ # @param origin_path the directory whose *contents* will be copied. The directory itself will not be copied.
116
+ # @param dest_path the directory into which the contents of origin_path will be copied.
117
+ #
118
+ def copy_dir(origin_path, dest_path)
119
+
120
+ p origin_path if $DEBUG
121
+
122
+ real_dir = get_non_linked_dir_path_for_path(origin_path)
123
+
124
+ unless File.exist?(dest_path) and File.directory?(dest_path) then
125
+
126
+ Dir.mkdir(dest_path)
127
+
128
+ end
129
+
130
+ Dir.foreach(real_dir) do |f|
131
+
132
+ next if f == "." or f == ".."
133
+
134
+ full_path= File.join(real_dir, f)
135
+
136
+ #check if this is a hard link to a directory
137
+ if File.exist?(full_path) and (not File.directory?(full_path)) and File.stat(full_path).nlink > @min_link_count then
138
+ full_path = get_non_linked_dir_path_for_path(full_path)
139
+ end
140
+
141
+ if File.directory?(full_path) then
142
+ copy_dir(full_path, File.join(dest_path, f))
143
+ else
144
+
145
+ if File.exist?(full_path) and not File.symlink?(full_path) then
146
+
147
+ FileUtils.cp(full_path, dest_path)
148
+
149
+ end
150
+
151
+ end
152
+
153
+ end
154
+
155
+ nil
156
+
157
+ end
158
+
159
+ end
160
+
161
+ end
162
+
163
+ if __FILE__ == $0 then
164
+
165
+ mount_point = ARGV[0]
166
+ source_path = ARGV[1]
167
+ dest_path = ARGV[2]
168
+
169
+ tmc = TimeMachineTools::TMCopier.new(mount_point)
170
+
171
+ origin_path = File.join(mount_point, source_path)
172
+
173
+ tmc.copy_dir(origin_path, dest_path)
174
+
175
+ end
176
+
177
+
178
+
179
+
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: time_machine_tools
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Colin J. Fuller
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-30 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Tools that copy data off Apple time machine backups on systems that do
15
+ not support the style of directory hard links used by those backups
16
+ email: cjfuller@gmail.com
17
+ executables:
18
+ - tm_copy
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - lib/time_machine_tools.rb
23
+ - bin/tm_copy
24
+ homepage: http://code.google.com/p/timemachinetools
25
+ licenses:
26
+ - MIT
27
+ post_install_message:
28
+ rdoc_options: []
29
+ require_paths:
30
+ - lib
31
+ required_ruby_version: !ruby/object:Gem::Requirement
32
+ none: false
33
+ requirements:
34
+ - - ! '>='
35
+ - !ruby/object:Gem::Version
36
+ version: '0'
37
+ required_rubygems_version: !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ! '>='
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 1.8.24
46
+ signing_key:
47
+ specification_version: 3
48
+ summary: Tools for copying data off of Apple time machine backups
49
+ test_files: []