higgs 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/ChangeLog +208 -0
- data/LICENSE +26 -0
- data/README +2 -0
- data/Rakefile +75 -0
- data/bin/higgs_backup +67 -0
- data/bin/higgs_dump_index +43 -0
- data/bin/higgs_dump_jlog +42 -0
- data/bin/higgs_verify +37 -0
- data/lib/cgi/session/higgs.rb +72 -0
- data/lib/higgs/block.rb +192 -0
- data/lib/higgs/cache.rb +117 -0
- data/lib/higgs/dbm.rb +55 -0
- data/lib/higgs/exceptions.rb +31 -0
- data/lib/higgs/flock.rb +77 -0
- data/lib/higgs/index.rb +164 -0
- data/lib/higgs/jlog.rb +159 -0
- data/lib/higgs/lock.rb +189 -0
- data/lib/higgs/storage.rb +1086 -0
- data/lib/higgs/store.rb +228 -0
- data/lib/higgs/tar.rb +390 -0
- data/lib/higgs/thread.rb +370 -0
- data/lib/higgs/tman.rb +513 -0
- data/lib/higgs/utils/bman.rb +285 -0
- data/lib/higgs/utils.rb +22 -0
- data/lib/higgs/version.rb +21 -0
- data/lib/higgs.rb +59 -0
- data/misc/cache_bench/cache_bench.rb +43 -0
- data/misc/dbm_bench/.strc +8 -0
- data/misc/dbm_bench/Rakefile +78 -0
- data/misc/dbm_bench/dbm_multi_thread.rb +199 -0
- data/misc/dbm_bench/dbm_rnd_delete.rb +43 -0
- data/misc/dbm_bench/dbm_rnd_read.rb +44 -0
- data/misc/dbm_bench/dbm_rnd_update.rb +44 -0
- data/misc/dbm_bench/dbm_seq_read.rb +45 -0
- data/misc/dbm_bench/dbm_seq_write.rb +44 -0
- data/misc/dbm_bench/st_verify.rb +28 -0
- data/misc/io_bench/cksum_bench.rb +48 -0
- data/misc/io_bench/jlog_bench.rb +71 -0
- data/misc/io_bench/write_bench.rb +128 -0
- data/misc/thread_bench/lock_bench.rb +132 -0
- data/mkrdoc.rb +8 -0
- data/rdoc.yml +13 -0
- data/sample/count.rb +60 -0
- data/sample/dbmtest.rb +38 -0
- data/test/Rakefile +45 -0
- data/test/run.rb +32 -0
- data/test/test_block.rb +163 -0
- data/test/test_cache.rb +214 -0
- data/test/test_cgi_session.rb +142 -0
- data/test/test_flock.rb +162 -0
- data/test/test_index.rb +258 -0
- data/test/test_jlog.rb +180 -0
- data/test/test_lock.rb +320 -0
- data/test/test_online_backup.rb +169 -0
- data/test/test_storage.rb +439 -0
- data/test/test_storage_conf.rb +202 -0
- data/test/test_storage_init_opts.rb +89 -0
- data/test/test_store.rb +211 -0
- data/test/test_tar.rb +432 -0
- data/test/test_thread.rb +541 -0
- data/test/test_tman.rb +875 -0
- data/test/test_tman_init_opts.rb +56 -0
- data/test/test_utils_bman.rb +234 -0
- metadata +115 -0
data/lib/higgs/store.rb
ADDED
@@ -0,0 +1,228 @@
|
|
1
|
+
# = storage like pstore
|
2
|
+
#
|
3
|
+
# Author:: $Author: toki $
|
4
|
+
# Date:: $Date: 2007-09-26 00:20:20 +0900 (Wed, 26 Sep 2007) $
|
5
|
+
# Revision:: $Revision: 559 $
|
6
|
+
#
|
7
|
+
# == license
|
8
|
+
# :include:LICENSE
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'higgs/storage'
|
12
|
+
require 'higgs/tman'
|
13
|
+
|
14
|
+
module Higgs
|
15
|
+
# = storage like pstore
|
16
|
+
# == tutorial
|
17
|
+
#
|
18
|
+
# === 1. open a new store
|
19
|
+
#
|
20
|
+
# % irb
|
21
|
+
# irb(main):001:0> require 'higgs'
|
22
|
+
# => true
|
23
|
+
# irb(main):002:0> st = Higgs::Store.new('foo')
|
24
|
+
# => #<Higgs::Store:0xb7cac7d8
|
25
|
+
#
|
26
|
+
# === 2. write an object
|
27
|
+
#
|
28
|
+
# irb(main):003:0> st.transaction{|tx|
|
29
|
+
# irb(main):004:1* tx[:foo] = Object.new
|
30
|
+
# irb(main):005:1> }
|
31
|
+
# => #<Object:0xb7d18adc>
|
32
|
+
#
|
33
|
+
# === 3. read an object
|
34
|
+
#
|
35
|
+
# irb(main):006:0> st.transaction{|tx|
|
36
|
+
# irb(main):007:1* tx[:foo]
|
37
|
+
# irb(main):008:1> }
|
38
|
+
# => #<Object:0xb7a3b1e8>
|
39
|
+
#
|
40
|
+
# === 4. custom property
|
41
|
+
#
|
42
|
+
# custom property name shuold be a string.
|
43
|
+
#
|
44
|
+
# irb(main):009:0> st.transaction{|tx|
|
45
|
+
# irb(main):010:1* tx.set_property(:foo, 'list', %w[ apple banana orange ])
|
46
|
+
# irb(main):011:1> }
|
47
|
+
# => nil
|
48
|
+
# irb(main):012:0> st.transaction{|tx|
|
49
|
+
# irb(main):013:1* tx.property(:foo, 'list')
|
50
|
+
# irb(main):014:1> }
|
51
|
+
# => ["apple", "banana", "orange"]
|
52
|
+
#
|
53
|
+
# === 4. write a string with <tt>:string_only</tt> property
|
54
|
+
#
|
55
|
+
# <tt>:string_only</tt> is system property.
|
56
|
+
# system property name should be a symbol.
|
57
|
+
#
|
58
|
+
# irb(main):015:0> st.transaction{|tx|
|
59
|
+
# irb(main):016:1* tx['bar'] = "Hello world.\n"
|
60
|
+
# irb(main):017:1> tx.set_property('bar', :string_only, true)
|
61
|
+
# irb(main):018:1> }
|
62
|
+
# => nil
|
63
|
+
#
|
64
|
+
# === 5. read a string
|
65
|
+
#
|
66
|
+
# irb(main):019:0> st.transaction{|tx|
|
67
|
+
# irb(main):020:1* tx['bar']
|
68
|
+
# irb(main):021:1> }
|
69
|
+
# => "Hello world.\n"
|
70
|
+
#
|
71
|
+
# === 6. <tt>:string_only</tt> property rejects an object
|
72
|
+
#
|
73
|
+
# irb(main):022:0> st.transaction{|tx|
|
74
|
+
# irb(main):023:1* tx['bar'] = Object.new
|
75
|
+
# irb(main):024:1> }
|
76
|
+
# TypeError: can't convert Object (value) to String
|
77
|
+
#
|
78
|
+
# === 7. read-only transaction
|
79
|
+
#
|
80
|
+
# irb(main):025:0> st.transaction{|tx|
|
81
|
+
# irb(main):026:1* p tx[:foo]
|
82
|
+
# irb(main):027:1> p tx.property(:foo, 'list')
|
83
|
+
# irb(main):028:1> p tx['bar']
|
84
|
+
# irb(main):029:1> p tx.property('bar', :string_only)
|
85
|
+
# irb(main):030:1> }
|
86
|
+
# #<Object:0xb79dd764>
|
87
|
+
# ["apple", "banana", "orange"]
|
88
|
+
# "Hello world.\n"
|
89
|
+
# true
|
90
|
+
# => nil
|
91
|
+
#
|
92
|
+
# === 8. shutdown
|
93
|
+
#
|
94
|
+
# irb(main):031:0> st.shutdown
|
95
|
+
# => nil
|
96
|
+
# irb(main):032:0> exit
|
97
|
+
#
|
98
|
+
# === 9. unix TAR storage and some files
|
99
|
+
#
|
100
|
+
# % ls -ln
|
101
|
+
# total 20
|
102
|
+
# -rw-r----- 1 1000 1000 1024 Sep 24 20:59 foo.idx
|
103
|
+
# -rw-r----- 1 1000 1000 4096 Sep 24 20:59 foo.jlog
|
104
|
+
# -rw-r----- 1 1000 1000 0 Sep 24 20:57 foo.lock
|
105
|
+
# -rw-r--r-- 1 1000 1000 57 Sep 24 20:57 foo.log
|
106
|
+
# -rw-r----- 1 1000 1000 5120 Sep 24 20:58 foo.tar
|
107
|
+
#
|
108
|
+
# === 10. unix TAR contents
|
109
|
+
#
|
110
|
+
# % tar tvf foo.tar
|
111
|
+
# -rw-r--r-- 1000/1000 12 2007-09-24 20:58 foo
|
112
|
+
# -rw-r--r-- 1000/1000 316 2007-09-24 20:58 foo.p
|
113
|
+
# -rw-r--r-- 1000/1000 13 2007-09-24 20:58 bar
|
114
|
+
# -rw-r--r-- 1000/1000 250 2007-09-24 20:58 bar.p
|
115
|
+
# % tar xvf foo.tar
|
116
|
+
# foo
|
117
|
+
# foo.p
|
118
|
+
# bar
|
119
|
+
# bar.p
|
120
|
+
#
|
121
|
+
# === 11. marshaled object
|
122
|
+
#
|
123
|
+
# `foo' is marshaled object
|
124
|
+
#
|
125
|
+
# % ls -ln foo
|
126
|
+
# -rw-r--r-- 1 1000 1000 12 Sep 24 20:58 foo
|
127
|
+
# % od -c foo
|
128
|
+
# 0000000 004 \b o : \v O b j e c t \0
|
129
|
+
# 0000014
|
130
|
+
#
|
131
|
+
# `foo.p' is properties.
|
132
|
+
# <tt>"list"</tt> of custom property is defined.
|
133
|
+
#
|
134
|
+
# % ls -ln foo.p
|
135
|
+
# -rw-r--r-- 1 1000 1000 316 Sep 24 20:58 foo.p
|
136
|
+
# % cat foo.p
|
137
|
+
# # SUM16 21993
|
138
|
+
# ---
|
139
|
+
# custom_properties:
|
140
|
+
# list:
|
141
|
+
# - apple
|
142
|
+
# - banana
|
143
|
+
# - orange
|
144
|
+
# system_properties:
|
145
|
+
# hash_type: MD5
|
146
|
+
# modified_time: &id001 2007-09-24 20:58:03.568701 +09:00
|
147
|
+
# hash_value: 5490d172635c560daaff1de92779d605
|
148
|
+
# created_time: *id001
|
149
|
+
# changed_time: 2007-09-24 20:58:24.502465 +09:00
|
150
|
+
# string_only: false
|
151
|
+
#
|
152
|
+
# === 12. string with <tt>:string_only</tt> property
|
153
|
+
#
|
154
|
+
# `bar' is a string
|
155
|
+
#
|
156
|
+
# % ls -ln bar
|
157
|
+
# -rw-r--r-- 1 1000 1000 13 Sep 24 20:58 bar
|
158
|
+
# % od -c bar
|
159
|
+
# 0000000 H e l l o w o r l d . \n
|
160
|
+
# 0000015
|
161
|
+
# % cat bar
|
162
|
+
# Hello world.
|
163
|
+
#
|
164
|
+
# `bar.p' is properties.
|
165
|
+
# <tt>:string_only</tt> of system property is <tt>true</tt>.
|
166
|
+
#
|
167
|
+
# % ls -ln bar.p
|
168
|
+
# -rw-r--r-- 1 1000 1000 250 Sep 24 20:58 bar.p
|
169
|
+
# % cat bar.p
|
170
|
+
# # SUM16 18034
|
171
|
+
# ---
|
172
|
+
# custom_properties: {}
|
173
|
+
#
|
174
|
+
# system_properties:
|
175
|
+
# hash_type: MD5
|
176
|
+
# modified_time: &id001 2007-09-24 20:58:45.420319 +09:00
|
177
|
+
# created_time: *id001
|
178
|
+
# hash_value: fa093de5fc603823f08524f9801f0546
|
179
|
+
# changed_time: *id001
|
180
|
+
# string_only: true
|
181
|
+
#
|
182
|
+
# === 13. other operations
|
183
|
+
#
|
184
|
+
# <tt>tx</tt> of block argument is an instance of
|
185
|
+
# Higgs::TransactionContext. see Higgs::TransactionContext for all
|
186
|
+
# operations of transaction.
|
187
|
+
#
|
188
|
+
# == sample script
|
189
|
+
#
|
190
|
+
# sample/count.rb
|
191
|
+
# :include: sample/count.rb
|
192
|
+
#
|
193
|
+
class Store
|
194
|
+
# for ident(1)
|
195
|
+
CVS_ID = '$Id: store.rb 559 2007-09-25 15:20:20Z toki $'
|
196
|
+
|
197
|
+
include Storage::Export
|
198
|
+
include TransactionManager::Export
|
199
|
+
|
200
|
+
DECODE = proc{|r| Marshal.load(r) }
|
201
|
+
ENCODE = proc{|w| Marshal.dump(w) }
|
202
|
+
|
203
|
+
# <tt>name</tt> is a storage name and see Higgs::Storage.new for
|
204
|
+
# detail. see Higgs::Storage::InitOptions and
|
205
|
+
# Higgs::TransactionManager::InitOptions for <tt>options</tt>.
|
206
|
+
def initialize(name, options={})
|
207
|
+
options[:decode] = DECODE
|
208
|
+
options[:encode] = ENCODE
|
209
|
+
@storage = Storage.new(name, options)
|
210
|
+
@tman = TransactionManager.new(@storage, options)
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.open(*args)
|
214
|
+
store = new(*args)
|
215
|
+
begin
|
216
|
+
r = yield(store)
|
217
|
+
ensure
|
218
|
+
store.shutdown
|
219
|
+
end
|
220
|
+
r
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# Local Variables:
|
226
|
+
# mode: Ruby
|
227
|
+
# indent-tabs-mode: nil
|
228
|
+
# End:
|
data/lib/higgs/tar.rb
ADDED
@@ -0,0 +1,390 @@
|
|
1
|
+
# = unix TAR utilities
|
2
|
+
#
|
3
|
+
# Author:: $Author: toki $
|
4
|
+
# Date:: $Date: 2007-09-26 00:20:20 +0900 (Wed, 26 Sep 2007) $
|
5
|
+
# Revision:: $Revision: 559 $
|
6
|
+
#
|
7
|
+
# == license
|
8
|
+
# :include:LICENSE
|
9
|
+
#
|
10
|
+
|
11
|
+
require 'forwardable'
|
12
|
+
require 'higgs/exceptions'
|
13
|
+
|
14
|
+
module Higgs
|
15
|
+
module Tar
|
16
|
+
# for ident(1)
|
17
|
+
CVS_ID = '$Id: tar.rb 559 2007-09-25 15:20:20Z toki $'
|
18
|
+
|
19
|
+
include Exceptions
|
20
|
+
|
21
|
+
class Error < HiggsError
|
22
|
+
end
|
23
|
+
|
24
|
+
class FormatError < Error
|
25
|
+
end
|
26
|
+
|
27
|
+
class MagicError < FormatError
|
28
|
+
end
|
29
|
+
|
30
|
+
class VersionError < FormatError
|
31
|
+
end
|
32
|
+
|
33
|
+
class CheckSumError < FormatError
|
34
|
+
end
|
35
|
+
|
36
|
+
class TooLongPathError < FormatError
|
37
|
+
end
|
38
|
+
|
39
|
+
class IOError < Error
|
40
|
+
end
|
41
|
+
|
42
|
+
# tar header format
|
43
|
+
#
|
44
|
+
# name : Z100 : null terminated string, primary hard link name
|
45
|
+
# mode : A8 : octet number format ascii string
|
46
|
+
# uid : A8 : octet number format ascii string
|
47
|
+
# gid : A8 : octet number format ascii string
|
48
|
+
# size : A12 : octet number format ascii string
|
49
|
+
# mtime : A12 : octet number format ascii string, seconds since epoch date-time (UTC 1970-01-01 00:00:00)
|
50
|
+
# cksum : A8 : octet number format ascii string
|
51
|
+
# typeflag : a1 : ascii number character (null character is old tar format)
|
52
|
+
# linkname : Z100 : null terminated string, secondly hard link name
|
53
|
+
# magic : A6 : white space terminated string
|
54
|
+
# version : a2 : 2 ascii characters
|
55
|
+
# uname : Z32 : null terminated string
|
56
|
+
# gname : Z32 : null terminated string
|
57
|
+
# devmajor : Z8 : octet number format ascii string
|
58
|
+
# devminor : Z8 : octet number format ascii string
|
59
|
+
# prefix : Z155 : null terminated string
|
60
|
+
#
|
61
|
+
module Block
|
62
|
+
# block size
|
63
|
+
BLKSIZ = 512
|
64
|
+
|
65
|
+
# limit name length
|
66
|
+
MAX_LEN = 100
|
67
|
+
|
68
|
+
# unix tar format parameters
|
69
|
+
MAGIC = 'ustar'
|
70
|
+
VERSION = '00'
|
71
|
+
REGTYPE = '0'
|
72
|
+
AREGTYPE = "\0"
|
73
|
+
LNKTYPE = '1'
|
74
|
+
SYMTYPE = '2'
|
75
|
+
CHRTYPE = '3'
|
76
|
+
BLKTYPE = '4'
|
77
|
+
DIRTYPE = '5'
|
78
|
+
FIFOTYPE = '6'
|
79
|
+
CONTTYPE = '7' # reserved
|
80
|
+
|
81
|
+
# for pack/unpack
|
82
|
+
HEAD_FMT = 'Z100 A8 A8 A8 A12 A12 A8 a1 Z100 A6 a2 Z32 Z32 Z8 Z8 Z155'
|
83
|
+
|
84
|
+
# end of archive
|
85
|
+
EOA = "\0" * BLKSIZ
|
86
|
+
|
87
|
+
def padding_size(bytes)
|
88
|
+
r = bytes % BLKSIZ
|
89
|
+
(r > 0) ? BLKSIZ - r : 0
|
90
|
+
end
|
91
|
+
module_function :padding_size
|
92
|
+
|
93
|
+
def blocked_size(bytes)
|
94
|
+
bytes + padding_size(bytes)
|
95
|
+
end
|
96
|
+
module_function :blocked_size
|
97
|
+
|
98
|
+
def tar?(path)
|
99
|
+
if (File.file? path) then
|
100
|
+
head = File.open(path, 'rb') {|r_io|
|
101
|
+
r_io.binmode
|
102
|
+
r_io.read(BLKSIZ)
|
103
|
+
}
|
104
|
+
if (head && head.length == BLKSIZ) then
|
105
|
+
if (head.unpack(HEAD_FMT)[9] == MAGIC) then
|
106
|
+
return true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
false
|
111
|
+
end
|
112
|
+
module_function :tar?
|
113
|
+
end
|
114
|
+
|
115
|
+
class RawIO
|
116
|
+
extend Forwardable
|
117
|
+
|
118
|
+
def initialize(io)
|
119
|
+
@io = io
|
120
|
+
end
|
121
|
+
|
122
|
+
def read(*args)
|
123
|
+
begin
|
124
|
+
@io.sysread(*args)
|
125
|
+
rescue EOFError
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def write(*args)
|
131
|
+
@io.syswrite(*args)
|
132
|
+
end
|
133
|
+
|
134
|
+
def seek(*args)
|
135
|
+
@io.sysseek(*args)
|
136
|
+
end
|
137
|
+
|
138
|
+
def tell
|
139
|
+
@io.sysseek(0, IO::SEEK_CUR)
|
140
|
+
end
|
141
|
+
|
142
|
+
alias pos tell
|
143
|
+
|
144
|
+
def pos=(pos)
|
145
|
+
@io.sysseek(pos, IO::SEEK_SET)
|
146
|
+
end
|
147
|
+
|
148
|
+
def_delegator :@io, :flush
|
149
|
+
def_delegator :@io, :fsync
|
150
|
+
def_delegator :@io, :truncate
|
151
|
+
def_delegator :@io, :close
|
152
|
+
def_delegator :@io, :closed?
|
153
|
+
end
|
154
|
+
|
155
|
+
class IOHandler
|
156
|
+
extend Forwardable
|
157
|
+
include Block
|
158
|
+
|
159
|
+
def initialize(io)
|
160
|
+
@io = io
|
161
|
+
end
|
162
|
+
|
163
|
+
def to_io
|
164
|
+
@io
|
165
|
+
end
|
166
|
+
|
167
|
+
def_delegator :@io, :seek
|
168
|
+
def_delegator :@io, :tell
|
169
|
+
def_delegator :@io, :pos
|
170
|
+
def_delegator :@io, :pos=
|
171
|
+
def_delegator :@io, :flush
|
172
|
+
def_delegator :@io, :fsync
|
173
|
+
def_delegator :@io, :truncate
|
174
|
+
def_delegator :@io, :close
|
175
|
+
def_delegator :@io, :closed?
|
176
|
+
end
|
177
|
+
|
178
|
+
class ArchiveReader < IOHandler
|
179
|
+
include Enumerable
|
180
|
+
|
181
|
+
def read_header(skip_body=false)
|
182
|
+
head_data = @io.read(BLKSIZ)
|
183
|
+
unless (head_data) then
|
184
|
+
raise FormatError, 'unexpected EOF'
|
185
|
+
end
|
186
|
+
if (head_data.length != BLKSIZ) then
|
187
|
+
raise FormatError, 'too short header'
|
188
|
+
end
|
189
|
+
if (head_data == EOA) then
|
190
|
+
next_head_data = @io.read(BLKSIZ)
|
191
|
+
if (next_head_data && next_head_data == EOA) then
|
192
|
+
return nil
|
193
|
+
else
|
194
|
+
raise FormatError, "not of EOA: #{head_data.inspect}, #{next_head_data.inspect}"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
chksum = (head_data[0...148] + ' ' * 8 + head_data[156...BLKSIZ]).sum(20)
|
198
|
+
head_list = head_data.unpack(HEAD_FMT)
|
199
|
+
head = {}
|
200
|
+
[ :name, :mode, :uid, :gid, :size, :mtime,
|
201
|
+
:chksum, :typeflag, :linkname, :magic, :version,
|
202
|
+
:uname, :gname, :devmajor, :devminor, :prefix
|
203
|
+
].each_with_index do |k, i|
|
204
|
+
head[k] = head_list[i]
|
205
|
+
end
|
206
|
+
if (head[:typeflag] == AREGTYPE) then
|
207
|
+
head[:typeflag] = REGTYPE
|
208
|
+
end
|
209
|
+
if (head[:magic] != MAGIC) then
|
210
|
+
raise MagicError, "unknown format: #{head[:magic].inspect}"
|
211
|
+
end
|
212
|
+
# why?
|
213
|
+
#if (head[:version] != VERSION) then
|
214
|
+
# raise VersionError, "unknown version: #{head[:version].inspect}"
|
215
|
+
#end
|
216
|
+
[ :mode,
|
217
|
+
:uid,
|
218
|
+
:gid,
|
219
|
+
:size,
|
220
|
+
:mtime,
|
221
|
+
:chksum
|
222
|
+
].each do |sym|
|
223
|
+
head[sym] = head[sym].oct
|
224
|
+
end
|
225
|
+
if (head[:chksum] != chksum) then
|
226
|
+
raise CheckSumError, 'broken tar'
|
227
|
+
end
|
228
|
+
head[:mtime] = Time.at(head[:mtime])
|
229
|
+
if (skip_body) then
|
230
|
+
skip_size = head[:size] + padding_size(head[:size])
|
231
|
+
@io.read(skip_size)
|
232
|
+
end
|
233
|
+
head
|
234
|
+
end
|
235
|
+
|
236
|
+
def fetch
|
237
|
+
head_and_body = read_header or return
|
238
|
+
if (head_and_body[:size] > 0) then
|
239
|
+
blocked_size = blocked_size(head_and_body[:size])
|
240
|
+
head_and_body[:body] = @io.read(blocked_size)
|
241
|
+
unless (head_and_body) then
|
242
|
+
raise FormatError, 'unexpected EOF'
|
243
|
+
end
|
244
|
+
if (head_and_body[:body].size != blocked_size) then
|
245
|
+
raise FormatError, 'mismatch body size'
|
246
|
+
end
|
247
|
+
head_and_body[:body][head_and_body[:size]...blocked_size] = ''
|
248
|
+
else
|
249
|
+
case (head_and_body[:typeflag])
|
250
|
+
when REGTYPE, CONTTYPE
|
251
|
+
head_and_body[:body] = ''
|
252
|
+
else
|
253
|
+
head_and_body[:body] = nil
|
254
|
+
end
|
255
|
+
end
|
256
|
+
head_and_body
|
257
|
+
end
|
258
|
+
|
259
|
+
def each(skip_body=false)
|
260
|
+
if (skip_body) then
|
261
|
+
while (head = read_header(true))
|
262
|
+
yield(head)
|
263
|
+
end
|
264
|
+
else
|
265
|
+
while (head_and_body = fetch)
|
266
|
+
yield(head_and_body)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
self
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
class ArchiveWriter < IOHandler
|
274
|
+
include Block
|
275
|
+
|
276
|
+
def write_header(head)
|
277
|
+
name = head[:name] or raise FormatError, "required name: #{head.inspect}"
|
278
|
+
if (name.length > MAX_LEN) then
|
279
|
+
raise TooLongPathError, "too long path: #{name}"
|
280
|
+
end
|
281
|
+
mode = format('%-8o', head[:mode] || 0100644) # 0100644 => -rw-r--r--
|
282
|
+
uid = format('%-8o', head[:uid] || Process.euid)
|
283
|
+
gid = format('%-8o', head[:gid] || Process.egid)
|
284
|
+
size = format('%-12o', head[:size])
|
285
|
+
mtime = format('%-12o', (head[:mtime] || Time.now).to_i)
|
286
|
+
dummy_chksum = ' ' * 8
|
287
|
+
typeflag = head[:typeflag] || REGTYPE
|
288
|
+
linkname = head[:linkname] || ''
|
289
|
+
magic = head[:magic] || MAGIC
|
290
|
+
version = head[:version] || VERSION
|
291
|
+
uname = head[:uname] || ''
|
292
|
+
gname = head[:gname] || ''
|
293
|
+
devmajor = head[:devmajor] || ''
|
294
|
+
devminor = head[:devminor] || ''
|
295
|
+
prefix = ''
|
296
|
+
header = [
|
297
|
+
name, mode, uid, gid, size, mtime,
|
298
|
+
dummy_chksum, typeflag, linkname, magic, version,
|
299
|
+
uname, gname, devmajor, devminor, prefix
|
300
|
+
].pack(HEAD_FMT)
|
301
|
+
header += "\0" * 12
|
302
|
+
if (head.key? :chksum) then
|
303
|
+
chksum = head[:chksum]
|
304
|
+
else
|
305
|
+
chksum = header.sum(20)
|
306
|
+
end
|
307
|
+
header[148, 8] = format('%-8o', chksum)
|
308
|
+
@io.write(header)
|
309
|
+
nil
|
310
|
+
end
|
311
|
+
|
312
|
+
def write_EOA
|
313
|
+
@io.write(EOA * 2)
|
314
|
+
nil
|
315
|
+
end
|
316
|
+
|
317
|
+
FTYPE_TO_TAR = {
|
318
|
+
'file' => REGTYPE,
|
319
|
+
'directory' => DIRTYPE,
|
320
|
+
'characterSpecial' => CHRTYPE,
|
321
|
+
'blockSpecial' => BLKTYPE,
|
322
|
+
'fifo' => FIFOTYPE,
|
323
|
+
'link' => SYMTYPE,
|
324
|
+
'socket' => FIFOTYPE
|
325
|
+
}
|
326
|
+
|
327
|
+
def add(name, body, options=nil)
|
328
|
+
head = {
|
329
|
+
:name => name,
|
330
|
+
:size => body.length
|
331
|
+
}
|
332
|
+
if (options) then
|
333
|
+
head.update(options)
|
334
|
+
end
|
335
|
+
if (block_given?) then
|
336
|
+
yield(head)
|
337
|
+
end
|
338
|
+
write_header(head)
|
339
|
+
body += "\0" * padding_size(body.length)
|
340
|
+
@io.write(body)
|
341
|
+
nil
|
342
|
+
end
|
343
|
+
|
344
|
+
def add_file(path)
|
345
|
+
stat = File.stat(path)
|
346
|
+
unless (FTYPE_TO_TAR.key? stat.ftype) then
|
347
|
+
raise FormatError, "unknown file type: #{stat.ftype}"
|
348
|
+
end
|
349
|
+
head = {
|
350
|
+
:name => path,
|
351
|
+
:mode => stat.mode,
|
352
|
+
:uid => stat.uid,
|
353
|
+
:gid => stat.gid,
|
354
|
+
:size => (stat.file?) ? stat.size : 0,
|
355
|
+
:mtime => stat.mtime,
|
356
|
+
:typeflag => FTYPE_TO_TAR[stat.ftype]
|
357
|
+
}
|
358
|
+
if (block_given?) then
|
359
|
+
yield(head)
|
360
|
+
end
|
361
|
+
write_header(head)
|
362
|
+
if (stat.ftype == 'file') then
|
363
|
+
File.open(path, 'rb') {|r_io|
|
364
|
+
chunk_size = BLKSIZ * 128
|
365
|
+
remaining_size = stat.size
|
366
|
+
while (remaining_size > chunk_size)
|
367
|
+
s = r_io.read(chunk_size) or raise IOError, 'unexpected EOF'
|
368
|
+
@io.write(s)
|
369
|
+
remaining_size -= chunk_size
|
370
|
+
end
|
371
|
+
s = r_io.read(chunk_size) or raise IOError, 'unexpected EOF'
|
372
|
+
s += "\0" * padding_size(stat.size)
|
373
|
+
@io.write(s)
|
374
|
+
}
|
375
|
+
end
|
376
|
+
nil
|
377
|
+
end
|
378
|
+
|
379
|
+
def close(eoa=true)
|
380
|
+
write_EOA if eoa
|
381
|
+
super()
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
# Local Variables:
|
388
|
+
# mode: Ruby
|
389
|
+
# indent-tabs-mode: nil
|
390
|
+
# End:
|