kindle_hacks 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1 @@
1
+ v0.1. Initial release.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 Zergling.Net
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ CHANGELOG
2
+ LICENSE
3
+ Manifest
4
+ README.textile
5
+ Rakefile
6
+ bin/kindle_update
7
+ lib/kindle_hacks.rb
8
+ lib/kindle_hacks/update.rb
9
+ lib/kindle_hacks/update_hl.rb
10
+ test/fixtures/update.dat
11
+ test/fixtures/update/001-savory.sh
12
+ test/fixtures/update/999-reboot.sh
13
+ test/fixtures/update/savoryctl
14
+ test/fixtures/update/update.yml
15
+ test/fixtures/update_dx_web.bin
16
+ test/fixtures/update_k2_pdf.bin
17
+ test/update_test.rb
@@ -0,0 +1,18 @@
1
+ h1. KindleHacks
2
+
3
+ This gem contains code that's useful for hacking on the kindle.
4
+
5
+ h2. Features
6
+
7
+ Currently, the gem installs the command-line tool kindle_update that can be used
8
+ to build and unpack Kindle updates. KindleHacks::Update implements the API used
9
+ by the command-line tool.
10
+
11
+ h2. Acknowledgments
12
+
13
+ The update packing and unpacking is based on igorsk's article available at
14
+ http://igorsk.blogspot.com/2007/12/hacking-kindle-part-2-bootloader-and.html
15
+
16
+ h2. Contributions
17
+
18
+ Please don't hesitate to fork the project and send pull requests.
@@ -0,0 +1,31 @@
1
+ # Rakefile that uses echoe to manage kindle_hacks' gemspec.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'rubygems'
8
+ require 'echoe'
9
+
10
+ Echoe.new('kindle_hacks') do |p|
11
+ p.project = 'zerglings' # rubyforge project
12
+
13
+ p.author = 'Victor Costan'
14
+ p.email = 'victor@zergling.net'
15
+ p.summary = "Assorted toolbox for hacking into Kindles."
16
+ p.url = 'http://github.com/costan/kindle_hacks'
17
+ p.dependencies = ['archive-tar-minitar >=0.5.2']
18
+ p.development_dependencies = ["echoe >=3.1.1", "flexmock >=0.8.6"]
19
+ p.eval = proc do |p|
20
+ p.default_executable = 'bin/kindle_update'
21
+ end
22
+
23
+ p.need_tar_gz = true
24
+ p.need_zip = true
25
+ p.rdoc_pattern = /^(lib|bin|tasks|ext)|^BUILD|^README|^CHANGELOG|^TODO|^LICENSE|^COPYING$/
26
+ end
27
+
28
+ if $0 == __FILE__
29
+ Rake.application = Rake::Application.new
30
+ Rake.application.run
31
+ end
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Command-line tool for packing and unpacking updates.
4
+ #
5
+ # Author:: Victor Costan
6
+ # Copyright:: Copyright (C) 2009 Zergling.Net
7
+ # License:: MIT
8
+
9
+ require 'fileutils'
10
+ require 'open-uri'
11
+
12
+ require 'rubygems'
13
+ require 'kindle_hacks'
14
+
15
+ case ARGV[0]
16
+ when 'pack'
17
+ update = KindleHacks::Update.read_dir ARGV[1] || '.'
18
+ File.open(File.join(ARGV[2] || '.', update.binary_file_name), 'w') do |f|
19
+ f.write update.to_binary
20
+ end
21
+ when 'unpack'
22
+ update = KindleHacks::Update.read File.read(ARGV[1])
23
+ FileUtils.mkdir_p ARGV[2] if ARGV[2]
24
+ update.to_dir ARGV[2] || '.'
25
+ else
26
+ print <<END_USAGE
27
+ Pack an update: #{$0} unpack [update_directory] [target_directory]
28
+ Unpack an update: #{$0} pack update_file [target_directory]
29
+ END_USAGE
30
+ end
@@ -0,0 +1,42 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{kindle_hacks}
5
+ s.version = "0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Victor Costan"]
9
+ s.date = %q{2009-10-09}
10
+ s.default_executable = %q{bin/kindle_update}
11
+ s.description = %q{Assorted toolbox for hacking into Kindles.}
12
+ s.email = %q{victor@zergling.net}
13
+ s.executables = ["kindle_update"]
14
+ s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README.textile", "bin/kindle_update", "lib/kindle_hacks.rb", "lib/kindle_hacks/update.rb", "lib/kindle_hacks/update_hl.rb"]
15
+ s.files = ["CHANGELOG", "LICENSE", "Manifest", "README.textile", "Rakefile", "bin/kindle_update", "lib/kindle_hacks.rb", "lib/kindle_hacks/update.rb", "lib/kindle_hacks/update_hl.rb", "test/fixtures/update.dat", "test/fixtures/update/001-savory.sh", "test/fixtures/update/999-reboot.sh", "test/fixtures/update/savoryctl", "test/fixtures/update/update.yml", "test/fixtures/update_dx_web.bin", "test/fixtures/update_k2_pdf.bin", "test/update_test.rb", "kindle_hacks.gemspec"]
16
+ s.homepage = %q{http://github.com/costan/kindle_hacks}
17
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Kindle_hacks", "--main", "README.textile"]
18
+ s.require_paths = ["lib"]
19
+ s.rubyforge_project = %q{zerglings}
20
+ s.rubygems_version = %q{1.3.5}
21
+ s.summary = %q{Assorted toolbox for hacking into Kindles.}
22
+ s.test_files = ["test/update_test.rb"]
23
+
24
+ if s.respond_to? :specification_version then
25
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
26
+ s.specification_version = 3
27
+
28
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
+ s.add_runtime_dependency(%q<archive-tar-minitar>, [">= 0.5.2"])
30
+ s.add_development_dependency(%q<echoe>, [">= 3.1.1"])
31
+ s.add_development_dependency(%q<flexmock>, [">= 0.8.6"])
32
+ else
33
+ s.add_dependency(%q<archive-tar-minitar>, [">= 0.5.2"])
34
+ s.add_dependency(%q<echoe>, [">= 3.1.1"])
35
+ s.add_dependency(%q<flexmock>, [">= 0.8.6"])
36
+ end
37
+ else
38
+ s.add_dependency(%q<archive-tar-minitar>, [">= 0.5.2"])
39
+ s.add_dependency(%q<echoe>, [">= 3.1.1"])
40
+ s.add_dependency(%q<flexmock>, [">= 0.8.6"])
41
+ end
42
+ end
@@ -0,0 +1,8 @@
1
+ # Top-level include file for KindleHacks.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'kindle_hacks/update.rb'
8
+ require 'kindle_hacks/update_hl.rb'
@@ -0,0 +1,248 @@
1
+ # Code for dealing with the binary update file format.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'digest/md5'
8
+ require 'zlib'
9
+
10
+ require 'rubygems'
11
+ require 'archive/tar/minitar'
12
+
13
+ # :nodoc: namespace
14
+ module KindleHacks
15
+
16
+
17
+ # :nodoc: documented in update_hl.rb
18
+ class Update
19
+ # The device that the update targets, e.g. :kindle_dx.
20
+ attr_reader :device
21
+
22
+ # The files that constitute the update.
23
+ attr_reader :files
24
+
25
+ # The minimum firmware version that this update applies to.
26
+ #
27
+ # The recommended value is DEFAULT_MIN_VERSION.
28
+ attr_reader :min_version
29
+
30
+ # The maximum firmware version that this update applies to.
31
+ #
32
+ # The recommended value is DEFAULT_MAX_VERSION.
33
+ attr_reader :max_version
34
+
35
+ # The update's name, appended at the end of the update image and manifest.
36
+ #
37
+ # For example, an update named _Savory-0.06 will have its update file named
38
+ # update_Savory-0.06.bin and the manifest will be named
39
+ # update-Savory-0.06.dat.
40
+ attr_reader :name
41
+
42
+ # Whether this update is optional or not.
43
+ attr_reader :optional
44
+
45
+ # The update's type, e.g. :ota.
46
+ attr_reader :update
47
+
48
+ # Initializes an update from a potentially incomplete manifest.
49
+ def initialize(manifest = {})
50
+ @device = manifest[:device] || :kindle
51
+ @device = @device.to_sym
52
+ @files = manifest[:files] || []
53
+ @files.each do |file|
54
+ file[:display] ||= file[:name] + '_file'
55
+ file[:target] = file[:target].to_sym
56
+ end
57
+ @min_version = manifest[:min_version] || DEFAULT_MIN_VERSION
58
+ @max_version = manifest[:max_version] || DEFAULT_MAX_VERSION
59
+ @name = manifest[:name] || ''
60
+ @optional = manifest[:optional] || 0
61
+ @unknown = manifest[:unknown] || DEFAULT_UNKNOWN
62
+ @update = manifest[:signature] || :ota
63
+ @update = @update.to_sym
64
+ end
65
+
66
+ # Creates an Update from raw update bytes.
67
+ #
68
+ # Returns an Update object.
69
+ def self.read(raw_update)
70
+ header = decode_header raw_update
71
+
72
+ header_size = HEADER_SIZES[header[:update]]
73
+ descramble_key = (SCRAMBLE_KEY >> 4 | SCRAMBLE_KEY << 4) & 0xff
74
+ md5 = scramble! raw_update[16, 32], descramble_key
75
+ tgz = scramble! raw_update[header_size..-1], descramble_key
76
+ raise 'Update signature is invalid' unless Digest::MD5.hexdigest(tgz) == md5
77
+
78
+ Update.new header.merge(decode_files(tgz))
79
+ end
80
+
81
+ # Raw update bytes corresponding to an update.
82
+ #
83
+ # Returns a string that can be written to a .bin file for updating a device.
84
+ def to_binary
85
+ header = encoded_header
86
+ tgz = encoded_files
87
+ md5 = Digest::MD5.hexdigest tgz
88
+ scrambled_md5 = Update.scramble! md5, SCRAMBLE_KEY
89
+ scrambled_tgz = Update.scramble! tgz, SCRAMBLE_KEY
90
+
91
+ [header, scrambled_md5,
92
+ "\0" * (HEADER_SIZES[@update] - header.length - md5.length),
93
+ scrambled_tgz].join
94
+ end
95
+
96
+ # The update's binary file name.
97
+ def binary_file_name
98
+ "update#{@name}.bin"
99
+ end
100
+
101
+ # Decodes an update's header.
102
+ #
103
+ # Args:
104
+ # raw_header:: a string containing the raw update header
105
+ #
106
+ # Returns a hash that can be used as an argument to Update's constructor.
107
+ def self.decode_header(raw_header)
108
+ update_id, min_version, max_version, device_id, optional, unknown =
109
+ *raw_header[0, 16].unpack('a4VVvCC')
110
+
111
+ update = UPDATE_IDS.keys.find { |k| UPDATE_IDS[k] == update_id }
112
+ device = DEVICE_IDS.keys.find { |k| DEVICE_IDS[k] == device_id }
113
+
114
+ { :update => update, :min_version => min_version,
115
+ :max_version => max_version, :device => device, :optional => optional,
116
+ :unknown => unknown }
117
+ end
118
+
119
+ # The encoded header for this update.
120
+ def encoded_header
121
+ [UPDATE_IDS[@update], @min_version, @max_version, DEVICE_IDS[@device],
122
+ @optional, @unknown].pack('a4VVvCC')
123
+ end
124
+
125
+ # Decodes an update's file contents.
126
+ #
127
+ # Args:
128
+ # raw_tgz:: a string containing the raw update tar.gz bytes
129
+ #
130
+ # Returns a hash that can be used as the options argument to Update's
131
+ # constructor.
132
+ def self.decode_files(raw_tgz)
133
+ raw_files = {}
134
+
135
+ gz_reader = Zlib::GzipReader.new StringIO.new(raw_tgz)
136
+ tar_reader = Archive::Tar::Minitar::Reader.new gz_reader
137
+ tar_reader.each_entry do |entry|
138
+ raise 'Directories unsupported' if entry.directory?
139
+
140
+ name = entry.full_name
141
+ contents = entry.read
142
+ raw_files[name] = contents
143
+ end
144
+ tar_reader.close
145
+ gz_reader.close
146
+
147
+ manifest_file = raw_files.keys.find { |name| /^update.*\.dat$/ =~ name }
148
+ raise 'No update*.dat manifest found' unless manifest_file
149
+
150
+ files = decode_manifest(raw_files[manifest_file])
151
+ files.each { |file| file[:contents] = raw_files[file[:name]] }
152
+ { :name => manifest_file[6...-4], :files => files }
153
+ end
154
+
155
+ # The encoded file data (tgz) for this update.
156
+ def encoded_files
157
+ raw_tgz = ""
158
+ gz_writer = Zlib::GzipWriter.new StringIO.new(raw_tgz)
159
+ tar_writer = Archive::Tar::Minitar::Writer.new gz_writer
160
+
161
+ data = Hash[*@files.map { |file| [file[:name], file[:contents]] }.flatten]
162
+ data["update#{@name}.dat"] = encoded_manifest
163
+ mtime = Time.now.to_i
164
+ data.each do |name, contents|
165
+ tar_writer.add_file_simple(name, :mode => 0100755, :uid => 0, :gid => 0,
166
+ :user => 'root', :group => 'root', :mtime => mtime,
167
+ :size => contents.length) do |file|
168
+ file.write contents
169
+ end
170
+ end
171
+ tar_writer.close
172
+ gz_writer.close
173
+
174
+ raw_tgz
175
+ end
176
+
177
+ # Decodes an update file's manifest (update*.dat).
178
+ #
179
+ # Args:
180
+ # raw_manifest:: a string containing the raw bytes in the manifest file
181
+ #
182
+ # Returns a hash that can be used as the :files key in the options argument to
183
+ # Update's constructor.
184
+ def self.decode_manifest(raw_manifest)
185
+ raw_manifest.split("\n").map do |line|
186
+ target_id, md5, filename, block_count, display_name = *line.split
187
+
188
+ target = TARGET_IDS.keys.find { |k| TARGET_IDS[k].to_s == target_id }
189
+ { :target => target, :name => filename, :display => display_name }
190
+ end
191
+ end
192
+
193
+ # The encoded manifest for this update.
194
+ def encoded_manifest
195
+ @files.map { |file|
196
+ target_id = TARGET_IDS[file[:target]]
197
+ md5 = Digest::MD5.hexdigest file[:contents]
198
+ block_count = file[:contents].length / BLOCK_SIZE[@device]
199
+ [target_id, md5, file[:name], block_count, file[:display]].join ' '
200
+ }.join("\n") + "\n"
201
+ end
202
+
203
+ # Scrambles update bytes (either the MD5 or the TGZ).
204
+ #
205
+ # Args:
206
+ # bytes:: the bytes to be scrambled (in-place)
207
+ # key:: the scrambling key (between 0 and 255)
208
+ #
209
+ # Returns the same array passed in the bytes argument.
210
+ def self.scramble!(bytes, key)
211
+ 0.upto(bytes.length - 1) do |i|
212
+ b = bytes[i]
213
+ b = (((b >> 4) | (b << 4)) & 0xFF) ^ key
214
+ bytes[i] = b
215
+ end
216
+ bytes
217
+ end
218
+
219
+ # Default value for the unknown byte in the update header.
220
+ DEFAULT_UNKNOWN = 0x13
221
+
222
+ # Default value for updates' minimum-allowed version.
223
+ DEFAULT_MIN_VERSION = 0
224
+
225
+ # Default value for updates' maximum-allowed version.
226
+ DEFAULT_MAX_VERSION = 0x7fffffff
227
+
228
+ # Kindle devices ID (numbers in the 3rd and 4th digits of the serial number).
229
+ DEVICE_IDS = {:kindle => 1, :kindle2 => 2, :kindle_dx => 4}
230
+
231
+ # Flash partition block size.
232
+ BLOCK_SIZE = {:kindle => 131072, :kindle2 => 131072, :kindle_dx => 131072 }
233
+
234
+ # Header sizes, based on update types.
235
+ HEADER_SIZES = {:manual => 131072, :ota => 64}
236
+
237
+ # Target IDs in the update manifest.
238
+ TARGET_IDS = { :base_fs => 6, :contents_fs => 7, :temp => 128,
239
+ :exec => 129 }
240
+
241
+ # Update file signatures.
242
+ UPDATE_IDS = {:manual => 'FB01', :ota => 'FC02'}
243
+
244
+ # The key used to scramble update bytes.
245
+ SCRAMBLE_KEY = 0x7A
246
+ end # class
247
+
248
+ end # namespace
@@ -0,0 +1,82 @@
1
+ # Code for dealing with the high-level update file format.
2
+ #
3
+ # Author:: Victor Costan
4
+ # Copyright:: Copyright (C) 2009 Zergling.Net
5
+ # License:: MIT
6
+
7
+ require 'yaml'
8
+
9
+ # :nodoc: namespace
10
+ module KindleHacks
11
+
12
+
13
+ class Update
14
+ def self.read_dir(path)
15
+ manifest = YAML.load File.read(File.join(path, 'update.yml'))
16
+ symbolize_keys! manifest
17
+
18
+ manifest[:files].each do |file|
19
+ file[:contents] = File.read File.join(path, file[:name])
20
+ end
21
+
22
+ Update.new manifest
23
+ end
24
+
25
+ def to_dir(path)
26
+ m = Update.stringify_keys! manifest
27
+ File.open(File.join(path, 'update.yml'), 'w') { |f| YAML.dump m, f }
28
+ @files.each do |file|
29
+ File.open File.join(path, file[:name]), 'w' do |f|
30
+ f.write file[:contents]
31
+ end
32
+ end
33
+ end
34
+
35
+ # The update's manifest.
36
+ def manifest
37
+ m = { :device => @device, :files => @files.map { |f| f.clone },
38
+ :min_version => @min_version, :max_version => @max_version,
39
+ :name => @name, :optional => @optional, :update => @update }
40
+
41
+ m[:files].each do |file|
42
+ file.delete :contents
43
+ file.delete :display if file[:display] == file[:name] + '_file'
44
+ end
45
+ m.delete :min_version if m[:min_version] == DEFAULT_MIN_VERSION
46
+ m.delete :max_version if m[:max_version] == DEFAULT_MAX_VERSION
47
+ m
48
+ end
49
+
50
+ # Recursively converts a hash's keys to symbols.
51
+ #
52
+ # Returns the same hash given as an argument.
53
+ def self.symbolize_keys!(obj)
54
+ if obj.kind_of? Hash
55
+ obj.keys.each do |key|
56
+ value = obj.delete key
57
+ symbolize_keys! value
58
+ obj[key.to_sym] = value
59
+ end
60
+ elsif obj.kind_of? Array
61
+ obj.each { |value| symbolize_keys! value }
62
+ end
63
+ obj
64
+ end
65
+
66
+ # Recursively converts a hash's keys and symbol values to strings.
67
+ def self.stringify_keys!(obj)
68
+ if obj.kind_of? Hash
69
+ obj.keys.each do |key|
70
+ value = obj.delete key
71
+ value = value.to_s if value.kind_of? Symbol
72
+ stringify_keys! value
73
+ obj[key.to_s] = value
74
+ end
75
+ elsif obj.kind_of? Array
76
+ obj.each { |value| stringify_keys! value }
77
+ end
78
+ obj
79
+ end
80
+ end # class
81
+
82
+ end # namespace
@@ -0,0 +1,3 @@
1
+ 128 de22e1808bd84edc219c91cff1ca89fc savoryctl 0 savoryctl_file
2
+ 129 90c7f4645f661c95f3de9893f8492506 001-savory.sh 0 001-savory.sh_file
3
+ 129 f64d5afa016a15ef6046be34067269bb 999-reboot.sh 0 999-reboot.sh_file
@@ -0,0 +1,52 @@
1
+ #!/bin/sh
2
+
3
+ _FUNCTIONS=/etc/rc.d/functions
4
+ [ -f ${_FUNCTIONS} ] && . ${_FUNCTIONS}
5
+
6
+ . /etc/sysconfig/mntus
7
+
8
+ VERSION_FILE=/etc/prettyversion.txt
9
+ SAVE_PRETTY=${VERSION_FILE}-beforesavory
10
+ NF_INI=/opt/amazon/ebook/config/netfront.ini
11
+ SAVE_NF_INI=${NF_INI}-beforesavory
12
+ SAVORYCTL=/etc/init.d/savoryctl
13
+
14
+ # If savory is already there, bail out
15
+ if [ -f $SAVORYCTL ]; then
16
+ update_progressbar 90
17
+ return 0
18
+ fi
19
+
20
+
21
+ update_progressbar 20
22
+
23
+ cp savoryctl $SAVORYCTL
24
+ chown root.root $SAVORYCTL
25
+ chmod 755 $SAVORYCTL
26
+ ln -s $SAVORYCTL /etc/rc5.d/S99savoryctl
27
+
28
+ update_progressbar 50
29
+
30
+
31
+ if [ -f $NF_INI ]; then
32
+ cp $NF_INI $SAVE_NF_INI
33
+ sed 's/dlexts=prc mobi txt azw$/dlexts=prc mobi txt azw epub lrf lit pdf fb2 odt/' $SAVE_NF_INI > /tmp/netfront-temp
34
+ sed 's/dlmimes=application\/x-mobipocket-ebook text\/x-prc$/dlmimes=application\/x-mobipocket-ebook text\/x-prc application\/pdf/' /tmp/netfront-temp > /tmp/netfront-temp-2
35
+
36
+ if [ -s /tmp/netfront-temp-2 ]; then
37
+ cp /tmp/netfront-temp-2 $NF_INI
38
+ fi
39
+
40
+ fi
41
+
42
+ update_progressbar 60
43
+
44
+
45
+ cp $VERSION_FILE $SAVE_PRETTY
46
+ sed 's/$/ UNSUPPORTED SAVORY-0.06/' $SAVE_PRETTY > $VERSION_FILE
47
+
48
+ update_progressbar 100
49
+
50
+ return 0
51
+
52
+
@@ -0,0 +1,18 @@
1
+ #!/bin/sh
2
+
3
+ _FUNCTIONS=/etc/rc.d/functions
4
+ [ -f ${_FUNCTIONS} ] && . ${_FUNCTIONS}
5
+
6
+ . /etc/sysconfig/mntus
7
+
8
+ rm -f ${MNTUS_MP}/update*.bin
9
+
10
+ display_update_screen_success
11
+
12
+ sync
13
+ sleep 10
14
+
15
+ /sbin/reboot
16
+
17
+ return 0
18
+
@@ -0,0 +1,35 @@
1
+ #!/bin/sh
2
+ PATH=/tmp/savory/bin:$PATH
3
+ NAME="savory file conversion daemon"
4
+ DAEMON=/tmp/savory/bin/savory_daemon
5
+ PIDFILE=/var/run/savory-daemon.pid
6
+ _FUNCTIONS=/etc/rc.d/functions
7
+ [ -f ${_FUNCTIONS} ] && . ${_FUNCTIONS}
8
+
9
+ case "$1" in
10
+
11
+ start)
12
+ mkdir /tmp/savory
13
+ mount -o loop -o ro /mnt/us/system/savory-image.ext3 /tmp/savory
14
+ if [ -x "$DAEMON" ]; then
15
+ msg "starting $NAME" I
16
+ $DAEMON -p $PIDFILE &
17
+ fi
18
+ ;;
19
+
20
+ stop)
21
+ if [ -r "$PIDFILE" ]; then
22
+ msg "stopping $NAME" I
23
+ kill `cat $PIDFILE`
24
+ rm -f $PIDFILE
25
+ umount /tmp/savory
26
+ fi
27
+ ;;
28
+ *)
29
+ msg "Usage: /etc/init.d/$NAME {start|stop}" W >&2
30
+ exit 1
31
+ ;;
32
+ esac
33
+
34
+ exit 0
35
+
@@ -0,0 +1,12 @@
1
+ ---
2
+ name: _Savory-0.06
3
+ device: kindle2
4
+ optional: 0
5
+ files:
6
+ - name: savoryctl
7
+ target: temp
8
+ - name: 001-savory.sh
9
+ target: exec
10
+ - name: 999-reboot.sh
11
+ target: exec
12
+ update: ota
@@ -0,0 +1,124 @@
1
+ # Author:: Victor Costan
2
+ # Copyright:: Copyright (C) 2009 Zergling.Net
3
+ # License:: MIT
4
+
5
+ require 'kindle_hacks'
6
+ require 'fileutils'
7
+ require 'test/unit'
8
+
9
+ class UpdateTest < Test::Unit::TestCase
10
+ Update = KindleHacks::Update
11
+
12
+ def setup
13
+ @fixtures_path = File.join(File.dirname(__FILE__), 'fixtures')
14
+ @update_path = File.join(@fixtures_path, 'update')
15
+
16
+ @kindle2_update = File.read File.join(@fixtures_path, 'update_k2_pdf.bin')
17
+ @kindledx_update = File.read File.join(@fixtures_path, 'update_dx_web.bin')
18
+
19
+ @manifest = File.read File.join(@fixtures_path, 'update.dat')
20
+
21
+ FileUtils.mkdir_p 'test_tmp'
22
+ end
23
+
24
+ def teardown
25
+ FileUtils.rm_r 'test_tmp'
26
+ end
27
+
28
+ def test_header_encoding_decoding
29
+ k2_golden = { :optional => 0, :update => :ota, :device => :kindle2,
30
+ :unknown => Update::DEFAULT_UNKNOWN,
31
+ :min_version => Update::DEFAULT_MIN_VERSION,
32
+ :max_version => Update::DEFAULT_MAX_VERSION }
33
+ dx_golden = { :optional => 0, :update => :ota, :device => :kindle_dx,
34
+ :unknown => Update::DEFAULT_UNKNOWN,
35
+ :min_version => Update::DEFAULT_MIN_VERSION,
36
+ :max_version => Update::DEFAULT_MAX_VERSION }
37
+
38
+ assert_equal k2_golden, Update.decode_header(@kindle2_update),
39
+ 'decoding kindle2_pdf'
40
+ assert_equal dx_golden, Update.decode_header(@kindledx_update),
41
+ 'decoding kindle_dx_web'
42
+
43
+ assert_equal @kindle2_update[0, 16], Update.new(k2_golden).encoded_header,
44
+ 'encoding kindle2_pdf'
45
+ assert_equal @kindledx_update[0, 16], Update.new(dx_golden).encoded_header,
46
+ 'encoding kindle_dx_pdf'
47
+ end
48
+
49
+ def test_decode_manifest
50
+ golden = [{ :display => "savoryctl_file", :name => "savoryctl",
51
+ :target => :temp },
52
+ { :display => "001-savory.sh_file", :name => "001-savory.sh",
53
+ :target => :exec },
54
+ { :display => "999-reboot.sh_file", :name => "999-reboot.sh",
55
+ :target => :exec }]
56
+ assert_equal golden, Update.decode_manifest(@manifest), 'decoding manifest'
57
+ end
58
+
59
+ def test_read
60
+ update = Update.read @kindle2_update
61
+ assert_equal :kindle2, update.device, 'Update device'
62
+ assert_equal '_Savory-0.06', update.name, 'Update name'
63
+ assert_equal :ota, update.update, 'Update update (type)'
64
+ update.files.each do |file|
65
+ golden = File.read File.join(@update_path, file[:name])
66
+ assert_equal "#{file[:name]}_file", file[:display],
67
+ "File #{file[:name]} display name"
68
+ assert_equal golden, file[:contents], "File #{file[:name]} contents"
69
+ end
70
+ end
71
+
72
+ def test_manifest
73
+ golden_file = File.read File.join(@update_path, 'update.yml')
74
+
75
+ golden = Update.symbolize_keys! YAML.load(golden_file)
76
+ golden[:device] = golden[:device].to_sym
77
+ golden[:update] = golden[:update].to_sym
78
+ golden[:files].each { |f| f[:target] = f[:target].to_sym }
79
+
80
+ assert_equal golden, Update.read(@kindle2_update).manifest
81
+ end
82
+
83
+ def test_to_dir
84
+ update = Update.read @kindle2_update
85
+ update.to_dir 'test_tmp'
86
+
87
+ Dir.entries(@update_path).each do |entry|
88
+ next if File.directory?(entry)
89
+
90
+ assert File.exist?(File.join('test_tmp', entry)),
91
+ "Serialized update doesn't have #{entry}"
92
+ assert_equal File.read(File.join(@update_path, entry)),
93
+ File.read(File.join('test_tmp', entry)),
94
+ "Bad contents in serialized update's #{entry}"
95
+ end
96
+ end
97
+
98
+ def test_read_dir
99
+ golden_update = Update.read @kindle2_update
100
+ update = Update.read_dir @update_path
101
+
102
+ assert_equal golden_update.manifest, update.manifest
103
+ assert_equal golden_update.files, update.files
104
+ end
105
+
106
+ def test_encoded_manifest
107
+ update = Update.read @kindle2_update
108
+ assert_equal @manifest, update.encoded_manifest
109
+ end
110
+
111
+ def test_to_binary
112
+ golden_update = Update.read @kindle2_update
113
+ binary = golden_update.to_binary
114
+ update = Update.read binary
115
+
116
+ assert_equal golden_update.manifest, update.manifest
117
+ assert_equal golden_update.files, update.files
118
+ end
119
+
120
+ def test_binary_file_name
121
+ update = Update.read @kindle2_update
122
+ assert_equal "update_Savory-0.06.bin", update.binary_file_name
123
+ end
124
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: kindle_hacks
3
+ version: !ruby/object:Gem::Version
4
+ version: "0.1"
5
+ platform: ruby
6
+ authors:
7
+ - Victor Costan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-09 00:00:00 -04:00
13
+ default_executable: bin/kindle_update
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: archive-tar-minitar
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.5.2
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: echoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.1.1
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: flexmock
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.8.6
44
+ version:
45
+ description: Assorted toolbox for hacking into Kindles.
46
+ email: victor@zergling.net
47
+ executables:
48
+ - kindle_update
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - CHANGELOG
53
+ - LICENSE
54
+ - README.textile
55
+ - bin/kindle_update
56
+ - lib/kindle_hacks.rb
57
+ - lib/kindle_hacks/update.rb
58
+ - lib/kindle_hacks/update_hl.rb
59
+ files:
60
+ - CHANGELOG
61
+ - LICENSE
62
+ - Manifest
63
+ - README.textile
64
+ - Rakefile
65
+ - bin/kindle_update
66
+ - lib/kindle_hacks.rb
67
+ - lib/kindle_hacks/update.rb
68
+ - lib/kindle_hacks/update_hl.rb
69
+ - test/fixtures/update.dat
70
+ - test/fixtures/update/001-savory.sh
71
+ - test/fixtures/update/999-reboot.sh
72
+ - test/fixtures/update/savoryctl
73
+ - test/fixtures/update/update.yml
74
+ - test/fixtures/update_dx_web.bin
75
+ - test/fixtures/update_k2_pdf.bin
76
+ - test/update_test.rb
77
+ - kindle_hacks.gemspec
78
+ has_rdoc: true
79
+ homepage: http://github.com/costan/kindle_hacks
80
+ licenses: []
81
+
82
+ post_install_message:
83
+ rdoc_options:
84
+ - --line-numbers
85
+ - --inline-source
86
+ - --title
87
+ - Kindle_hacks
88
+ - --main
89
+ - README.textile
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: "0"
97
+ version:
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: "1.2"
103
+ version:
104
+ requirements: []
105
+
106
+ rubyforge_project: zerglings
107
+ rubygems_version: 1.3.5
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: Assorted toolbox for hacking into Kindles.
111
+ test_files:
112
+ - test/update_test.rb