higgs 0.1.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.
- 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:
|