| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Crypt::IDA::ShareFile; |
|
2
|
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
24648
|
use 5.008008; |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
40
|
|
|
4
|
1
|
|
|
1
|
|
6
|
use strict; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
46
|
|
|
5
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
35
|
|
|
6
|
|
|
|
|
|
|
|
|
7
|
1
|
|
|
1
|
|
4
|
use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
103
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
1
|
|
|
1
|
|
12
|
use Carp; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
107
|
|
|
10
|
1
|
|
|
1
|
|
5
|
use Fcntl qw(:DEFAULT :seek); |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
1042
|
|
|
11
|
1
|
|
|
1
|
|
968
|
use Crypt::IDA qw(:all); |
|
|
1
|
|
|
|
|
3
|
|
|
|
1
|
|
|
|
|
11960
|
|
|
12
|
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
require Exporter; |
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
my @export_default = qw( sf_calculate_chunk_sizes |
|
16
|
|
|
|
|
|
|
sf_split sf_combine); |
|
17
|
|
|
|
|
|
|
my @export_extras = qw( sf_sprintf_filename ); |
|
18
|
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
|
20
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
|
21
|
|
|
|
|
|
|
'all' => [ @export_extras, @export_default ], |
|
22
|
|
|
|
|
|
|
'default' => [ @export_default ], |
|
23
|
|
|
|
|
|
|
); |
|
24
|
|
|
|
|
|
|
our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); |
|
25
|
|
|
|
|
|
|
our @EXPORT = qw( ); |
|
26
|
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
our $VERSION = '0.01'; |
|
28
|
|
|
|
|
|
|
our $classname="Crypt::IDA::ShareFile"; |
|
29
|
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub sf_sprintf_filename { |
|
31
|
10
|
|
|
10
|
0
|
25
|
my ($self,$class); |
|
32
|
10
|
50
|
33
|
|
|
54
|
if ($_[0] eq $classname or ref($_[0]) eq $classname) { |
|
33
|
0
|
|
|
|
|
0
|
$self=shift; |
|
34
|
0
|
|
|
|
|
0
|
$class=ref($self); |
|
35
|
|
|
|
|
|
|
} else { |
|
36
|
10
|
|
|
|
|
16
|
$self=$classname; |
|
37
|
|
|
|
|
|
|
} |
|
38
|
10
|
|
|
|
|
14
|
my ($format,$filename,$chunk,$share)=@_; |
|
39
|
|
|
|
|
|
|
|
|
40
|
10
|
|
|
|
|
44
|
$format=~s/\%f/$filename/; |
|
41
|
10
|
|
|
|
|
21
|
$format=~s/\%c/$chunk/; |
|
42
|
10
|
|
|
|
|
29
|
$format=~s/\%s/$share/; |
|
43
|
|
|
|
|
|
|
|
|
44
|
10
|
|
|
|
|
28
|
return $format; |
|
45
|
|
|
|
|
|
|
} |
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
# I could eliminate the use of closures below, but it makes for a |
|
49
|
|
|
|
|
|
|
# convenient wrapper for handling byte order issues, and also for |
|
50
|
|
|
|
|
|
|
# implementing the "dry run" option to sf_write_ida_header later. |
|
51
|
|
|
|
|
|
|
sub sf_mk_file_istream { |
|
52
|
9
|
|
|
9
|
0
|
11
|
my ($self,$class); |
|
53
|
9
|
50
|
33
|
|
|
46
|
if ($_[0] eq $classname or ref($_[0]) eq $classname) { |
|
54
|
0
|
|
|
|
|
0
|
$self=shift; |
|
55
|
0
|
|
|
|
|
0
|
$class=ref($self); |
|
56
|
|
|
|
|
|
|
} else { |
|
57
|
9
|
|
|
|
|
18
|
$self=$classname; |
|
58
|
|
|
|
|
|
|
} |
|
59
|
9
|
|
|
|
|
14
|
my ($filename,$default_bytes_per_read)=@_; |
|
60
|
9
|
|
|
|
|
13
|
my ($fh,$eof)=(undef,0); |
|
61
|
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# basic checking of args |
|
63
|
9
|
50
|
|
|
|
27
|
$default_bytes_per_read=1 unless defined($default_bytes_per_read); |
|
64
|
9
|
50
|
33
|
|
|
78
|
if (!defined($filename) or |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
65
|
|
|
|
|
|
|
$default_bytes_per_read <= 0 or $default_bytes_per_read > 4 or |
|
66
|
|
|
|
|
|
|
int($default_bytes_per_read) != $default_bytes_per_read) { |
|
67
|
0
|
|
|
|
|
0
|
return undef; |
|
68
|
|
|
|
|
|
|
} |
|
69
|
9
|
50
|
|
|
|
21
|
die "using istream with >32 bits would lead to precision errors\n" |
|
70
|
|
|
|
|
|
|
if $default_bytes_per_read > 4; |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# try opening the file; use sysopen to match better with later sysreads |
|
73
|
9
|
50
|
|
|
|
437
|
return undef unless sysopen $fh,$filename,O_RDONLY; |
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
# Use closure/callback technique to provide an iterator for this file |
|
76
|
|
|
|
|
|
|
my $methods= |
|
77
|
|
|
|
|
|
|
{ |
|
78
|
0
|
|
|
0
|
|
0
|
FILENAME => sub { $filename }, |
|
79
|
0
|
|
|
0
|
|
0
|
FH => sub { $fh }, |
|
80
|
|
|
|
|
|
|
READ => sub { |
|
81
|
|
|
|
|
|
|
# This reads words from the file in network (big-endian) byte |
|
82
|
|
|
|
|
|
|
# order, with zero padding in the least significant bytes. So, |
|
83
|
|
|
|
|
|
|
# for example, if we are using 2-byte chunks and the file |
|
84
|
|
|
|
|
|
|
# contains three bytes 0d fe 2d, then two reads on the file |
|
85
|
|
|
|
|
|
|
# will return the values 0dfe and 2d00. Return values are |
|
86
|
|
|
|
|
|
|
# integers or undef on eof. |
|
87
|
|
|
|
|
|
|
|
|
88
|
99
|
|
|
99
|
|
97
|
my ($override_bytes,$bytes_to_read); |
|
89
|
|
|
|
|
|
|
|
|
90
|
99
|
50
|
|
|
|
201
|
if ($override_bytes=shift) { |
|
91
|
99
|
|
|
|
|
110
|
$bytes_to_read=$override_bytes; |
|
92
|
99
|
50
|
33
|
|
|
390
|
die "Invalid bytes to read $bytes_to_read" if |
|
93
|
|
|
|
|
|
|
int($bytes_to_read) != $bytes_to_read or $bytes_to_read <= 0; |
|
94
|
99
|
50
|
|
|
|
184
|
die "using istream with >32 bits would lead to precision errors\n" |
|
95
|
|
|
|
|
|
|
if $bytes_to_read > 4; |
|
96
|
|
|
|
|
|
|
} else { |
|
97
|
0
|
|
|
|
|
0
|
$bytes_to_read=$default_bytes_per_read; |
|
98
|
|
|
|
|
|
|
} |
|
99
|
|
|
|
|
|
|
|
|
100
|
99
|
|
|
|
|
109
|
my $buf=""; |
|
101
|
99
|
|
|
|
|
530
|
my $bytes_read=sysread $fh, $buf, $bytes_to_read; |
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
# There are three possible return value from sysread: |
|
104
|
|
|
|
|
|
|
# undef there was a problem with the read (caller should check $!) |
|
105
|
|
|
|
|
|
|
# 0 no bytes read (eof) |
|
106
|
|
|
|
|
|
|
# >0 some bytes read (maybe fewer than we wanted, due to eof) |
|
107
|
99
|
50
|
|
|
|
186
|
return undef unless defined($bytes_read); |
|
108
|
99
|
50
|
|
|
|
171
|
if ($bytes_read == 0) { |
|
109
|
0
|
|
|
|
|
0
|
$eof=1; |
|
110
|
0
|
|
|
|
|
0
|
return undef; |
|
111
|
|
|
|
|
|
|
} |
|
112
|
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
# we don't need to set eof, but it might be useful for callers |
|
114
|
99
|
50
|
|
|
|
153
|
$eof=1 if ($bytes_read < $bytes_to_read); |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# Convert these bytes into a number (first byte is high byte ) |
|
117
|
99
|
|
|
|
|
240
|
$buf=pack "a$bytes_to_read", $buf; # pad zeroes on right |
|
118
|
|
|
|
|
|
|
#$buf.="\0" x ($bytes_to_read - length $buf); |
|
119
|
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# hex() can only handle values up to 32 bits, but perl scalars |
|
121
|
|
|
|
|
|
|
# can handle up to 64 bits (they're upgraded to floats |
|
122
|
|
|
|
|
|
|
# internally after 32 bits, though). I'm disabling this since I |
|
123
|
|
|
|
|
|
|
# don't think it's acceptable. The only upshot for the rest of |
|
124
|
|
|
|
|
|
|
# the program is that file size is now limited to 4Gb - 1 byte. |
|
125
|
99
|
|
|
|
|
99
|
my $val=0; |
|
126
|
|
|
|
|
|
|
# while ($bytes_to_read > 4) { |
|
127
|
|
|
|
|
|
|
# $val=unpack "N", (substr $buf,0,4,""); |
|
128
|
|
|
|
|
|
|
# $bytes_to_read-=4; |
|
129
|
|
|
|
|
|
|
# $val <<= 32; |
|
130
|
|
|
|
|
|
|
# } |
|
131
|
99
|
|
|
|
|
144
|
my $hex_format="H" . ($bytes_to_read * 2); # count nibbles |
|
132
|
99
|
|
|
|
|
352
|
return hex unpack $hex_format, $buf; |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
}, |
|
135
|
0
|
|
|
0
|
|
0
|
EOF => sub { return $eof; }, |
|
136
|
0
|
|
|
0
|
|
0
|
SEEK => sub { seek $fh, shift, 0; }, |
|
137
|
0
|
|
|
0
|
|
0
|
TELL => sub { tell $fh; }, |
|
138
|
0
|
|
|
0
|
|
0
|
CLOSE => sub { close $fh; } |
|
139
|
9
|
|
|
|
|
149
|
}; |
|
140
|
9
|
|
|
|
|
26
|
return $methods; |
|
141
|
|
|
|
|
|
|
} |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
sub sf_mk_file_ostream { |
|
144
|
9
|
|
|
9
|
0
|
14
|
my ($filename,$default_bytes_per_write)=@_; |
|
145
|
9
|
|
|
|
|
17
|
my ($fh,$eof)=(undef,0); |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# basic checking of args |
|
148
|
9
|
50
|
33
|
|
|
57
|
if (!defined($filename) or !defined($default_bytes_per_write) or |
|
|
|
|
33
|
|
|
|
|
|
149
|
|
|
|
|
|
|
$default_bytes_per_write <= 0) { |
|
150
|
0
|
|
|
|
|
0
|
return undef; |
|
151
|
|
|
|
|
|
|
} |
|
152
|
|
|
|
|
|
|
|
|
153
|
|
|
|
|
|
|
# try opening the file; use sysopen to match later sysreads |
|
154
|
9
|
50
|
|
|
|
577
|
return undef unless sysopen $fh,$filename,O_CREAT|O_TRUNC|O_WRONLY; |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
my $methods= |
|
157
|
|
|
|
|
|
|
{ |
|
158
|
0
|
|
|
0
|
|
0
|
FILENAME => sub { $filename }, |
|
159
|
9
|
|
|
9
|
|
34
|
FH => sub { $fh }, |
|
160
|
|
|
|
|
|
|
WRITE => sub { |
|
161
|
99
|
|
|
99
|
|
193
|
my $num=shift; |
|
162
|
|
|
|
|
|
|
|
|
163
|
99
|
|
|
|
|
88
|
my ($override_bytes,$bytes_to_write); |
|
164
|
|
|
|
|
|
|
|
|
165
|
99
|
50
|
|
|
|
152
|
if ($override_bytes=shift) { |
|
166
|
99
|
|
|
|
|
125
|
$bytes_to_write=$override_bytes; |
|
167
|
|
|
|
|
|
|
} else { |
|
168
|
0
|
|
|
|
|
0
|
$bytes_to_write=$default_bytes_per_write; |
|
169
|
|
|
|
|
|
|
} |
|
170
|
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
# Writing is a little easier than reading, but we have to take |
|
172
|
|
|
|
|
|
|
# care if the number passed is too large to fit in the |
|
173
|
|
|
|
|
|
|
# requested number of bytes. If it's too large a warning will |
|
174
|
|
|
|
|
|
|
# be emitted and we discard any extra *high* bits. |
|
175
|
|
|
|
|
|
|
|
|
176
|
99
|
|
|
|
|
120
|
my $buf=""; |
|
177
|
|
|
|
|
|
|
|
|
178
|
99
|
50
|
|
|
|
200
|
if ($num >= 256 ** $bytes_to_write) { |
|
179
|
0
|
|
|
|
|
0
|
carp "ostream: Number too large. Discarded high bits."; |
|
180
|
0
|
|
|
|
|
0
|
$num %= (256 ** ($bytes_to_write) - 1); |
|
181
|
|
|
|
|
|
|
} |
|
182
|
|
|
|
|
|
|
|
|
183
|
99
|
|
|
|
|
144
|
my $hex_format="H" . ($bytes_to_write * 2); |
|
184
|
99
|
|
|
|
|
282
|
$buf=pack $hex_format, sprintf "%0*x", $bytes_to_write*2, $num; |
|
185
|
99
|
|
|
|
|
5894
|
syswrite $fh,$buf,$bytes_to_write; |
|
186
|
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
}, |
|
188
|
0
|
|
|
0
|
|
0
|
EOF => sub { 0; }, |
|
189
|
0
|
|
|
0
|
|
0
|
FILENAME => sub { return $filename; }, |
|
190
|
0
|
|
|
0
|
|
0
|
FLUSH => sub { 0; }, |
|
191
|
0
|
|
|
0
|
|
0
|
SEEK => sub { seek $fh, shift, 0; }, |
|
192
|
0
|
|
|
0
|
|
0
|
TELL => sub { tell $fh; }, |
|
193
|
0
|
|
|
0
|
|
0
|
CLOSE => sub { close $fh; } |
|
194
|
9
|
|
|
|
|
168
|
}; |
|
195
|
9
|
|
|
|
|
47
|
return $methods; # be explicit |
|
196
|
|
|
|
|
|
|
} |
|
197
|
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# Routines to read/write share file header |
|
199
|
|
|
|
|
|
|
# |
|
200
|
|
|
|
|
|
|
# header version 1 |
|
201
|
|
|
|
|
|
|
# |
|
202
|
|
|
|
|
|
|
# bytes name value |
|
203
|
|
|
|
|
|
|
# 2 magic marker for "Share File" format; "SF" = {5346} |
|
204
|
|
|
|
|
|
|
# 1 version file format version = 1 |
|
205
|
|
|
|
|
|
|
# 1 options options bits (see below) |
|
206
|
|
|
|
|
|
|
# 1-2 k,quorum quorum k-value (set both names on read) |
|
207
|
|
|
|
|
|
|
# 1-2 s,security security level s-value (width in bytes) |
|
208
|
|
|
|
|
|
|
# var chunk_start absolute offset of chunk in file |
|
209
|
|
|
|
|
|
|
# var chunk_next absolute offset of next chunk in file |
|
210
|
|
|
|
|
|
|
# var transform transform matrix row |
|
211
|
|
|
|
|
|
|
# |
|
212
|
|
|
|
|
|
|
# The options bits are as follows: |
|
213
|
|
|
|
|
|
|
# |
|
214
|
|
|
|
|
|
|
# Bit name Settings |
|
215
|
|
|
|
|
|
|
# 0 opt_large_k Large (2-byte) k value? |
|
216
|
|
|
|
|
|
|
# 1 opt_large_w Large (2-byte) s value? |
|
217
|
|
|
|
|
|
|
# 2 opt_final Final chunk in file? (1=full file/final chunk) |
|
218
|
|
|
|
|
|
|
# 3 opt_transform Is transform data included? |
|
219
|
|
|
|
|
|
|
# |
|
220
|
|
|
|
|
|
|
# Note that the chunk_next field is 1 greater than the actual offset |
|
221
|
|
|
|
|
|
|
# of the chunk end. In other words, the chunk ranges from the byte |
|
222
|
|
|
|
|
|
|
# starting at chunk_start up to, but not including the byte at |
|
223
|
|
|
|
|
|
|
# chunk_next. That's why it's called chunk_next rather than chunk_end. |
|
224
|
|
|
|
|
|
|
# |
|
225
|
|
|
|
|
|
|
# More on this: it might seem that it's ok to refuse to split a |
|
226
|
|
|
|
|
|
|
# zero-length file, but if we're using this for backups, it's not a |
|
227
|
|
|
|
|
|
|
# good idea to fail just because we don't like zero-length |
|
228
|
|
|
|
|
|
|
# files. Also, splitting a zero-length file might be useful in some |
|
229
|
|
|
|
|
|
|
# cases, since we might be interested in just creating and storing a |
|
230
|
|
|
|
|
|
|
# transform matrix for later use, or maybe generating test cases or |
|
231
|
|
|
|
|
|
|
# debugging a matrix inverse routine. |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
sub sf_read_ida_header { |
|
234
|
9
|
|
|
9
|
0
|
13
|
my $istream=shift; # assume istream is at start of file |
|
235
|
9
|
|
|
|
|
13
|
my $header_info={}; # values will be returned in this hash |
|
236
|
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
#warn "Reading header from istream " . ($istream->{FILENAME}->()) . "\n"; |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
# When calling this routine the caller can specify any |
|
240
|
|
|
|
|
|
|
# previously-read values for k, s, and so on and have us check the |
|
241
|
|
|
|
|
|
|
# values in the current header against these values for consistency. |
|
242
|
|
|
|
|
|
|
# This implies that all the shares we're being presented for |
|
243
|
|
|
|
|
|
|
# processing will combine to form a single chunk (or full file). If |
|
244
|
|
|
|
|
|
|
# this is the first share header being read, the following may be |
|
245
|
|
|
|
|
|
|
# undefined. We store any read values in the returned hash, so it's |
|
246
|
|
|
|
|
|
|
# up to the caller to take them out and pass them back to us when |
|
247
|
|
|
|
|
|
|
# reading the next header in the batch. |
|
248
|
9
|
|
|
|
|
15
|
my ($k,$w,$start,$next,$hdr)=@_; |
|
249
|
|
|
|
|
|
|
|
|
250
|
9
|
|
|
|
|
10
|
my $header_size=0; # we also send this back in hash |
|
251
|
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# error reporting |
|
253
|
9
|
|
|
|
|
21
|
$header_info->{header_error}=0; # 0=no error, 1=failure |
|
254
|
9
|
|
|
|
|
17
|
$header_info->{error_message}=""; # text of error |
|
255
|
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
# use a local subroutine to save the tedium of checking for eof and |
|
257
|
|
|
|
|
|
|
# doing conversion of input. Updates variables from our local scope |
|
258
|
|
|
|
|
|
|
# directly, so we don't need to pass them in or out. (Actually, |
|
259
|
|
|
|
|
|
|
# technically speaking, this creates an anonymous closure and |
|
260
|
|
|
|
|
|
|
# locally assigns a name to it for the current scope, but it's |
|
261
|
|
|
|
|
|
|
# pretty much the same thing as a local subroutine) |
|
262
|
|
|
|
|
|
|
local *read_some = sub { |
|
263
|
99
|
|
|
99
|
|
212
|
my ($bytes,$field,$conversion)=@_; |
|
264
|
99
|
|
|
|
|
107
|
my ($vec,$hex); |
|
265
|
99
|
50
|
|
|
|
161
|
if ($vec=$istream->{READ}->($bytes), defined($vec)) { |
|
266
|
99
|
100
|
|
|
|
180
|
if (defined ($conversion)) { |
|
267
|
72
|
100
|
|
|
|
168
|
if ($conversion eq "hex") { |
|
|
|
50
|
|
|
|
|
|
|
268
|
9
|
|
|
|
|
57
|
$vec=sprintf "%*x", $bytes, $vec; |
|
269
|
|
|
|
|
|
|
} elsif ($conversion eq "dec") { |
|
270
|
63
|
|
|
|
|
72
|
$vec=$vec; # istream already returns integers |
|
271
|
|
|
|
|
|
|
} else { |
|
272
|
0
|
|
|
|
|
0
|
die "Unknown format conversion (use undef, hex or dec)\n"; |
|
273
|
|
|
|
|
|
|
} |
|
274
|
|
|
|
|
|
|
} |
|
275
|
99
|
|
|
|
|
173
|
$header_info->{$field}=$vec; |
|
276
|
99
|
|
|
|
|
111
|
$header_size+=$bytes; |
|
277
|
99
|
|
|
|
|
271
|
return 1; # read some? got some. |
|
278
|
|
|
|
|
|
|
} else { |
|
279
|
0
|
|
|
|
|
0
|
$header_info->{error}++; |
|
280
|
0
|
|
|
|
|
0
|
$header_info->{error_message}="Premature end of stream\n"; |
|
281
|
0
|
|
|
|
|
0
|
return 0; # read some? got none! |
|
282
|
|
|
|
|
|
|
} |
|
283
|
9
|
|
|
|
|
40
|
}; |
|
284
|
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
# same idea for saving and reporting errors |
|
286
|
|
|
|
|
|
|
local *header_error = sub { |
|
287
|
0
|
|
|
0
|
|
0
|
$header_info->{error}++; |
|
288
|
0
|
|
|
|
|
0
|
$header_info->{error_message}=shift; |
|
289
|
0
|
|
|
|
|
0
|
return $header_info; |
|
290
|
9
|
|
|
|
|
30
|
}; |
|
291
|
|
|
|
|
|
|
|
|
292
|
9
|
50
|
|
|
|
23
|
return $header_info unless read_some(2,"magic","hex"); |
|
293
|
9
|
50
|
|
|
|
27
|
if ($header_info->{magic} ne "5346") { |
|
294
|
0
|
|
|
|
|
0
|
return header_error("This doesn't look like a share file\n" . |
|
295
|
|
|
|
|
|
|
"Magic is $header_info->{magic}\n"); |
|
296
|
|
|
|
|
|
|
} |
|
297
|
|
|
|
|
|
|
|
|
298
|
9
|
50
|
|
|
|
22
|
return $header_info unless read_some(1,"version","dec"); |
|
299
|
9
|
50
|
|
|
|
25
|
if ($header_info->{version} != 1) { |
|
300
|
0
|
|
|
|
|
0
|
return header_error("Don't know how to handle header version " . |
|
301
|
|
|
|
|
|
|
$header_info->{version} . "\n"); |
|
302
|
|
|
|
|
|
|
} |
|
303
|
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# read options field and split out into separate names for each bit |
|
305
|
9
|
50
|
|
|
|
17
|
return $header_info unless read_some(1,"options","dec"); |
|
306
|
9
|
|
|
|
|
22
|
$header_info->{opt_large_k} = ($header_info->{options} & 1); |
|
307
|
9
|
|
|
|
|
19
|
$header_info->{opt_large_w} = ($header_info->{options} & 2) >> 1; |
|
308
|
9
|
|
|
|
|
28
|
$header_info->{opt_final} = ($header_info->{options} & 4) >> 2; |
|
309
|
9
|
|
|
|
|
16
|
$header_info->{opt_transform} = ($header_info->{options} & 8) >> 3; |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
# read k (regular or large variety) and check for consistency |
|
312
|
9
|
50
|
|
|
|
27
|
return $header_info unless |
|
|
|
50
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
read_some($header_info->{opt_large_k} ? 2 : 1 ,"k","dec"); |
|
314
|
9
|
50
|
66
|
|
|
42
|
if (defined($k) and $k != $header_info->{k}) { |
|
315
|
0
|
|
|
|
|
0
|
return header_error("Inconsistent quorum value read from streams\n"); |
|
316
|
|
|
|
|
|
|
} else { |
|
317
|
9
|
|
|
|
|
19
|
$header_info->{quorum} = $header_info->{k}; |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
# read s (regular or large variety) and check for consistency |
|
321
|
9
|
50
|
|
|
|
22
|
return $header_info unless |
|
|
|
50
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
read_some($header_info->{opt_large_w} ? 2 : 1 ,"w","dec"); |
|
323
|
9
|
50
|
66
|
|
|
45
|
if (defined($w) and $w != $header_info->{w}) { |
|
324
|
|
|
|
|
|
|
return |
|
325
|
0
|
|
|
|
|
0
|
header_error("Inconsistent security values read from streams\n"); |
|
326
|
|
|
|
|
|
|
} else { |
|
327
|
9
|
|
|
|
|
19
|
$header_info->{security} = $header_info->{w}; |
|
328
|
|
|
|
|
|
|
} |
|
329
|
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
# File offsets can be of variable width, so we precede each offset |
|
331
|
|
|
|
|
|
|
# with a length field. For an offset of 0, we only have to store a |
|
332
|
|
|
|
|
|
|
# single byte of zero (since it takes zero bytes to store the value |
|
333
|
|
|
|
|
|
|
# zero). So while storing the start offset for a complete file is a |
|
334
|
|
|
|
|
|
|
# little bit wasteful (1 extra byte) compared to just using an |
|
335
|
|
|
|
|
|
|
# options bit to indicate that the share is a share for a complete |
|
336
|
|
|
|
|
|
|
# file and just storing the file length, it helps us to keep the |
|
337
|
|
|
|
|
|
|
# code simpler and less prone to errors by not having to treat full |
|
338
|
|
|
|
|
|
|
# files any differently than chunks. |
|
339
|
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
# Read in the chunk_start value. We'll re-use the offset_width key |
|
341
|
|
|
|
|
|
|
# for the chunk_next code, and then delete that key before pasing the |
|
342
|
|
|
|
|
|
|
# hash back to the caller (provided we don't run into errors). |
|
343
|
9
|
50
|
|
|
|
21
|
return $header_info unless read_some(1 ,"offset_width","dec"); |
|
344
|
9
|
|
|
|
|
16
|
my $offset_width=$header_info->{offset_width}; |
|
345
|
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
# Perl has no problem working with values as big as 2 ** 41 == 2Tb, but |
|
347
|
|
|
|
|
|
|
# we should probably impose a sane limit on file sizes here. |
|
348
|
9
|
50
|
|
|
|
22
|
if ($offset_width > 4) { |
|
349
|
0
|
|
|
|
|
0
|
return header_error("File size must be less than 4Gb!\n"); |
|
350
|
|
|
|
|
|
|
} |
|
351
|
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
# now read in chunk_start and check that that it is a multiple of k * w |
|
353
|
9
|
|
|
|
|
18
|
my $colsize=$header_info->{k} * $header_info->{w}; |
|
354
|
9
|
50
|
|
|
|
14
|
if ($offset_width) { |
|
355
|
0
|
0
|
|
|
|
0
|
return $header_info unless |
|
356
|
|
|
|
|
|
|
read_some($offset_width ,"chunk_start","dec"); |
|
357
|
0
|
0
|
|
|
|
0
|
if ($header_info->{chunk_start} % ($colsize)) { |
|
358
|
0
|
|
|
|
|
0
|
return header_error("Alignment error on chunk start offset\n"); |
|
359
|
|
|
|
|
|
|
} |
|
360
|
|
|
|
|
|
|
} else { |
|
361
|
9
|
|
|
|
|
16
|
$header_info->{chunk_start}=0; |
|
362
|
|
|
|
|
|
|
} |
|
363
|
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
# and also that it's consistent with other shares |
|
365
|
9
|
50
|
66
|
|
|
34
|
if (defined($start) and $start != $header_info->{chunk_start}) { |
|
366
|
0
|
|
|
|
|
0
|
return header_error("Inconsistent chunk_start values read from streams\n"); |
|
367
|
|
|
|
|
|
|
} else { |
|
368
|
9
|
|
|
|
|
11
|
$start=$header_info->{chunk_start}; |
|
369
|
|
|
|
|
|
|
} |
|
370
|
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
# now get in the offset of the end of the chunk |
|
372
|
|
|
|
|
|
|
# Note that chunk_next must be a multiple of k * s |
|
373
|
9
|
50
|
|
|
|
24
|
return $header_info unless read_some(1 ,"offset_width","dec"); |
|
374
|
9
|
|
|
|
|
13
|
$offset_width=$header_info->{offset_width}; |
|
375
|
9
|
50
|
|
|
|
20
|
if ($offset_width > 4) { |
|
376
|
0
|
|
|
|
|
0
|
return header_error("File size must be less than 4Gb!\n"); |
|
377
|
|
|
|
|
|
|
} |
|
378
|
9
|
50
|
|
|
|
15
|
if ($offset_width) { |
|
379
|
9
|
50
|
|
|
|
16
|
return $header_info unless |
|
380
|
|
|
|
|
|
|
read_some($offset_width, "chunk_next","dec"); |
|
381
|
9
|
50
|
33
|
|
|
36
|
if (!$header_info->{opt_final} and |
|
382
|
|
|
|
|
|
|
($header_info->{chunk_next}) % ($colsize)) { |
|
383
|
0
|
|
|
|
|
0
|
return header_error("Alignment error on non-final chunk end offset\n"); |
|
384
|
|
|
|
|
|
|
} |
|
385
|
|
|
|
|
|
|
} else { |
|
386
|
|
|
|
|
|
|
# header end of 0 is strange, but we'll allow it for now and only |
|
387
|
|
|
|
|
|
|
# raise an error later if chunk_next <= chunk_start. Test code |
|
388
|
|
|
|
|
|
|
# should make sure that the program works correctly when |
|
389
|
|
|
|
|
|
|
# splitting/combining zero-length files. |
|
390
|
0
|
|
|
|
|
0
|
$header_info->{chunk_next}=0; |
|
391
|
|
|
|
|
|
|
} |
|
392
|
9
|
50
|
66
|
|
|
36
|
if (defined($next) and $next != $header_info->{chunk_next}) { |
|
393
|
0
|
|
|
|
|
0
|
return header_error("Inconsistent chunk_next values read from streams\n"); |
|
394
|
|
|
|
|
|
|
} else { |
|
395
|
9
|
|
|
|
|
13
|
$next=$header_info->{chunk_next}; |
|
396
|
|
|
|
|
|
|
} |
|
397
|
9
|
|
|
|
|
17
|
delete $header_info->{offset_width}; # caller doesn't need or want this |
|
398
|
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
# don't allow chunk_start > chunk_next, but allow chunk_start == |
|
400
|
|
|
|
|
|
|
# chunk_next to represent an empty file |
|
401
|
9
|
50
|
|
|
|
27
|
if ($header_info->{chunk_start} > $header_info->{chunk_next}) { |
|
402
|
0
|
|
|
|
|
0
|
return header_error("Invalid chunk range: chunk_start > chunk_next\n"); |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
# If transform data is included in the header, then read in a matrix |
|
406
|
|
|
|
|
|
|
# row of $k values of $s bytes apiece |
|
407
|
9
|
50
|
|
|
|
17
|
if ($header_info->{opt_transform}) { |
|
408
|
9
|
|
|
|
|
16
|
my $matrix_row=[]; |
|
409
|
9
|
|
|
|
|
26
|
for my $i (1 .. $header_info->{k}) { |
|
410
|
27
|
50
|
|
|
|
58
|
return $header_info unless read_some($header_info->{w},"element"); |
|
411
|
27
|
|
|
|
|
69
|
push @$matrix_row, $header_info->{element}; |
|
412
|
|
|
|
|
|
|
} |
|
413
|
9
|
|
|
|
|
14
|
delete $header_info->{element}; |
|
414
|
9
|
|
|
|
|
19
|
$header_info->{transform}=$matrix_row; |
|
415
|
|
|
|
|
|
|
#warn "Read transform row: [" . (join ", ", map |
|
416
|
|
|
|
|
|
|
# {sprintf("%02x",$_) } @{ |
|
417
|
|
|
|
|
|
|
# $header_info->{transform} |
|
418
|
|
|
|
|
|
|
# }) . "]\n"; |
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# Now that we've read in all the header bytes, check that header |
|
422
|
|
|
|
|
|
|
# size is consistent with expectations. |
|
423
|
9
|
50
|
66
|
|
|
34
|
if (defined($hdr) and $hdr != $header_size) { |
|
424
|
0
|
|
|
|
|
0
|
return header_error("Inconsistent header sizes read from streams\n"); |
|
425
|
|
|
|
|
|
|
} else { |
|
426
|
9
|
|
|
|
|
16
|
$header_info->{header_size}=$header_size; |
|
427
|
|
|
|
|
|
|
} |
|
428
|
|
|
|
|
|
|
|
|
429
|
9
|
|
|
|
|
813
|
return $header_info; |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
# When writing the header, we return number of header bytes written or |
|
433
|
|
|
|
|
|
|
# zero in the event of some error. |
|
434
|
|
|
|
|
|
|
sub sf_write_ida_header { |
|
435
|
12
|
|
|
12
|
0
|
242
|
my ($self,$class); |
|
436
|
12
|
50
|
33
|
|
|
63
|
if ($_[0] eq $classname or ref($_[0]) eq $classname) { |
|
437
|
0
|
|
|
|
|
0
|
$self=shift; |
|
438
|
0
|
|
|
|
|
0
|
$class=ref($self); |
|
439
|
|
|
|
|
|
|
} else { |
|
440
|
12
|
|
|
|
|
20
|
$self=$classname; |
|
441
|
|
|
|
|
|
|
} |
|
442
|
12
|
|
|
|
|
131
|
my %header_info=( |
|
443
|
|
|
|
|
|
|
ostream => undef, |
|
444
|
|
|
|
|
|
|
version => undef, |
|
445
|
|
|
|
|
|
|
quorum => undef, |
|
446
|
|
|
|
|
|
|
width => undef, |
|
447
|
|
|
|
|
|
|
chunk_start => undef, |
|
448
|
|
|
|
|
|
|
chunk_next => undef, |
|
449
|
|
|
|
|
|
|
transform => undef, |
|
450
|
|
|
|
|
|
|
opt_final => undef, |
|
451
|
|
|
|
|
|
|
dry_run => 0, |
|
452
|
|
|
|
|
|
|
@_ |
|
453
|
|
|
|
|
|
|
); |
|
454
|
12
|
|
|
|
|
18
|
my $header_size=0; |
|
455
|
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
# save to local variables |
|
457
|
108
|
50
|
|
|
|
212
|
my ($ostream,$version,$k,$s,$chunk_start,$chunk_next, |
|
458
|
|
|
|
|
|
|
$transform,$opt_final,$dry_run) = |
|
459
|
|
|
|
|
|
|
map { |
|
460
|
12
|
|
|
|
|
19
|
exists($header_info{$_}) ? $header_info{$_} : undef |
|
461
|
|
|
|
|
|
|
} qw(ostream version quorum width chunk_start chunk_next transform |
|
462
|
|
|
|
|
|
|
opt_final dry_run); |
|
463
|
|
|
|
|
|
|
|
|
464
|
12
|
50
|
33
|
|
|
55
|
return 0 unless defined($version) and $version == 1; |
|
465
|
12
|
50
|
33
|
|
|
96
|
return 0 unless defined($k) and defined($s) and |
|
|
|
|
33
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
466
|
|
|
|
|
|
|
defined($chunk_start) and defined($chunk_next); |
|
467
|
|
|
|
|
|
|
|
|
468
|
12
|
50
|
33
|
|
|
53
|
return 0 if defined($transform) and scalar(@$transform) != $k; |
|
469
|
|
|
|
|
|
|
|
|
470
|
12
|
100
|
|
|
|
24
|
if ($dry_run) { |
|
471
|
|
|
|
|
|
|
$ostream={ |
|
472
|
33
|
|
|
33
|
|
46
|
WRITE => sub { "do nothing" }, |
|
473
|
3
|
|
|
|
|
19
|
}; |
|
474
|
|
|
|
|
|
|
} |
|
475
|
|
|
|
|
|
|
|
|
476
|
|
|
|
|
|
|
# magic |
|
477
|
12
|
|
|
|
|
31
|
$ostream->{WRITE}->(0x5346,2); |
|
478
|
12
|
|
|
|
|
17
|
$header_size += 2; |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
# version |
|
481
|
12
|
|
|
|
|
30
|
$ostream->{WRITE}->($version,1); |
|
482
|
12
|
|
|
|
|
13
|
$header_size += 1; |
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
# Set up and write options byte |
|
485
|
12
|
|
|
|
|
15
|
my ($opt_large_k,$opt_large_w,$opt_transform); |
|
486
|
|
|
|
|
|
|
|
|
487
|
12
|
50
|
|
|
|
23
|
if ($k < 256) { |
|
|
|
0
|
|
|
|
|
|
|
488
|
12
|
|
|
|
|
13
|
$opt_large_k=0; |
|
489
|
|
|
|
|
|
|
} elsif ($k < 65536) { |
|
490
|
0
|
|
|
|
|
0
|
$opt_large_k=1; |
|
491
|
|
|
|
|
|
|
} else { |
|
492
|
0
|
|
|
|
|
0
|
return 0; |
|
493
|
|
|
|
|
|
|
} |
|
494
|
|
|
|
|
|
|
|
|
495
|
12
|
50
|
|
|
|
15
|
if ($s < 256) { |
|
|
|
0
|
|
|
|
|
|
|
496
|
12
|
|
|
|
|
22
|
$opt_large_w=0; |
|
497
|
|
|
|
|
|
|
} elsif ($s < 65536) { |
|
498
|
0
|
|
|
|
|
0
|
$opt_large_w=1; |
|
499
|
|
|
|
|
|
|
} else { |
|
500
|
0
|
|
|
|
|
0
|
return 0; |
|
501
|
|
|
|
|
|
|
} |
|
502
|
|
|
|
|
|
|
|
|
503
|
12
|
50
|
|
|
|
22
|
$opt_transform=(defined($transform) ? 1 : 0); |
|
504
|
|
|
|
|
|
|
|
|
505
|
12
|
|
|
|
|
52
|
$ostream->{WRITE}->(( |
|
506
|
|
|
|
|
|
|
($opt_large_k) | |
|
507
|
|
|
|
|
|
|
($opt_large_w) << 1 | |
|
508
|
|
|
|
|
|
|
($opt_final) << 2 | |
|
509
|
|
|
|
|
|
|
($opt_transform) << 3), |
|
510
|
|
|
|
|
|
|
1); |
|
511
|
12
|
|
|
|
|
15
|
$header_size += 1; |
|
512
|
|
|
|
|
|
|
|
|
513
|
|
|
|
|
|
|
# write k and s values |
|
514
|
12
|
|
|
|
|
32
|
$ostream->{WRITE}->($k, $opt_large_k + 1); |
|
515
|
12
|
|
|
|
|
17
|
$header_size += $opt_large_k + 1; |
|
516
|
|
|
|
|
|
|
|
|
517
|
12
|
|
|
|
|
30
|
$ostream->{WRITE}->($s, $opt_large_w + 1); |
|
518
|
12
|
|
|
|
|
16
|
$header_size += $opt_large_w + 1; |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
# chunk_start, chunk_next |
|
521
|
12
|
|
|
|
|
18
|
my ($width,$topval); |
|
522
|
|
|
|
|
|
|
|
|
523
|
12
|
50
|
|
|
|
33
|
if ($chunk_start == 0) { |
|
524
|
12
|
|
|
|
|
27
|
$ostream->{WRITE}->(0,1); |
|
525
|
12
|
|
|
|
|
18
|
$header_size += 1; |
|
526
|
|
|
|
|
|
|
} else { |
|
527
|
0
|
|
|
|
|
0
|
($width,$topval)=(1,255); |
|
528
|
0
|
|
|
|
|
0
|
while ($chunk_start > $topval) { # need another byte? |
|
529
|
0
|
|
|
|
|
0
|
++$width; $topval = ($topval << 8) + 255; |
|
|
0
|
|
|
|
|
0
|
|
|
530
|
|
|
|
|
|
|
}; |
|
531
|
0
|
|
|
|
|
0
|
$ostream->{WRITE}->($width,1); |
|
532
|
0
|
|
|
|
|
0
|
$ostream->{WRITE}->($chunk_start, $width); |
|
533
|
0
|
|
|
|
|
0
|
$header_size += 1 + $width; |
|
534
|
|
|
|
|
|
|
} |
|
535
|
|
|
|
|
|
|
|
|
536
|
12
|
50
|
|
|
|
24
|
if ($chunk_next == 0) { |
|
537
|
0
|
|
|
|
|
0
|
$ostream->{WRITE}->(0,1); |
|
538
|
0
|
|
|
|
|
0
|
$header_size += 1; |
|
539
|
|
|
|
|
|
|
} else { |
|
540
|
12
|
|
|
|
|
24
|
($width,$topval)=(1,255); |
|
541
|
12
|
|
|
|
|
28
|
while ($chunk_next > $topval) { # need another byte? |
|
542
|
0
|
|
|
|
|
0
|
++$width; $topval = ($topval << 8) + 255; |
|
|
0
|
|
|
|
|
0
|
|
|
543
|
|
|
|
|
|
|
}; |
|
544
|
12
|
|
|
|
|
26
|
$ostream->{WRITE}->($width,1); |
|
545
|
12
|
|
|
|
|
26
|
$ostream->{WRITE}->($chunk_next,$width); |
|
546
|
12
|
|
|
|
|
17
|
$header_size += 1 + $width; |
|
547
|
|
|
|
|
|
|
} |
|
548
|
|
|
|
|
|
|
|
|
549
|
12
|
50
|
|
|
|
33
|
if ($opt_transform) { |
|
550
|
12
|
|
|
|
|
21
|
foreach my $elem (@$transform) { |
|
551
|
36
|
|
|
|
|
66
|
$ostream->{WRITE}->($elem,$s); |
|
552
|
36
|
|
|
|
|
68
|
$header_size += $s; |
|
553
|
|
|
|
|
|
|
} |
|
554
|
|
|
|
|
|
|
} |
|
555
|
|
|
|
|
|
|
|
|
556
|
12
|
|
|
|
|
79
|
return $header_size; |
|
557
|
|
|
|
|
|
|
} |
|
558
|
|
|
|
|
|
|
|
|
559
|
|
|
|
|
|
|
# The following routine is exportable, since the caller may wish to |
|
560
|
|
|
|
|
|
|
# know how large chunks are going to be before actually generating |
|
561
|
|
|
|
|
|
|
# them. This could be useful, for example, if the caller needs to know |
|
562
|
|
|
|
|
|
|
# how large the chunks are before deciding where to put them, or for |
|
563
|
|
|
|
|
|
|
# trying out a different chunk size/strategy if the first one didn't |
|
564
|
|
|
|
|
|
|
# suit their requirements. |
|
565
|
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
sub sf_calculate_chunk_sizes { |
|
567
|
3
|
|
|
3
|
0
|
5
|
my ($self,$class); |
|
568
|
0
|
|
|
|
|
0
|
my %o; |
|
569
|
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
# despite the routine name, we'll calculate several different values |
|
571
|
|
|
|
|
|
|
# relating to each chunk: |
|
572
|
|
|
|
|
|
|
# chunk_start |
|
573
|
|
|
|
|
|
|
# chunk_next |
|
574
|
|
|
|
|
|
|
# chunk_size (chunk_next - chunk_start) |
|
575
|
|
|
|
|
|
|
# file_size (output file size, including header) |
|
576
|
|
|
|
|
|
|
# opt_final (is the last chunk in the file?) |
|
577
|
|
|
|
|
|
|
# padding (number of padding bytes in (final) chunk) |
|
578
|
|
|
|
|
|
|
# |
|
579
|
|
|
|
|
|
|
# We store these in a hash, and return a list of references to |
|
580
|
|
|
|
|
|
|
# hashes, one for each chunk. |
|
581
|
|
|
|
|
|
|
|
|
582
|
3
|
50
|
33
|
|
|
21
|
if ($_[0] eq $classname or ref($_[0]) eq $classname) { |
|
583
|
0
|
|
|
|
|
0
|
$self=shift; |
|
584
|
0
|
|
|
|
|
0
|
$class=ref($self); |
|
585
|
|
|
|
|
|
|
} else { |
|
586
|
3
|
|
|
|
|
6
|
$self=$classname; |
|
587
|
|
|
|
|
|
|
} |
|
588
|
3
|
|
|
|
|
38
|
%o=( |
|
589
|
|
|
|
|
|
|
quorum => undef, |
|
590
|
|
|
|
|
|
|
width => undef, |
|
591
|
|
|
|
|
|
|
filename => undef, |
|
592
|
|
|
|
|
|
|
# misc options |
|
593
|
|
|
|
|
|
|
version => 1, # header version |
|
594
|
|
|
|
|
|
|
save_transform => 1, # whether to store transform in header |
|
595
|
|
|
|
|
|
|
# pick one method of calculating chunk size. The file is not |
|
596
|
|
|
|
|
|
|
# broken into chunks unless one of these is defined. |
|
597
|
|
|
|
|
|
|
n_chunks => undef, |
|
598
|
|
|
|
|
|
|
in_chunk_size => undef, |
|
599
|
|
|
|
|
|
|
out_chunk_size => undef, |
|
600
|
|
|
|
|
|
|
out_file_size => undef, |
|
601
|
|
|
|
|
|
|
@_, |
|
602
|
|
|
|
|
|
|
dry_run => 1, # for call to sf_write_ida_header |
|
603
|
|
|
|
|
|
|
); |
|
604
|
3
|
|
|
|
|
5
|
my @chunks=(); |
|
605
|
3
|
|
|
|
|
5
|
my ($hs,$cb,$cn,$cs,$nc); |
|
606
|
|
|
|
|
|
|
|
|
607
|
|
|
|
|
|
|
# Copy options into local variables |
|
608
|
27
|
50
|
|
|
|
159
|
my ($k, $w, $filename, $version, $save_transform, |
|
609
|
|
|
|
|
|
|
$n_chunks, $in_chunk_size, $out_chunk_size, $out_file_size) = |
|
610
|
|
|
|
|
|
|
map { |
|
611
|
3
|
|
|
|
|
7
|
exists($o{$_}) ? $o{$_} : undef |
|
612
|
|
|
|
|
|
|
} qw(quorum width filename version save_transform |
|
613
|
|
|
|
|
|
|
n_chunks in_chunk_size out_chunk_size out_file_size); |
|
614
|
|
|
|
|
|
|
|
|
615
|
|
|
|
|
|
|
# Check some input values (more checks later) |
|
616
|
3
|
0
|
33
|
|
|
22
|
unless ($w == 1 or $w == 2 or $w == 4) { |
|
|
|
|
33
|
|
|
|
|
|
617
|
0
|
|
|
|
|
0
|
carp "Invalid width value"; |
|
618
|
0
|
|
|
|
|
0
|
return undef; |
|
619
|
|
|
|
|
|
|
} |
|
620
|
3
|
50
|
33
|
|
|
37
|
if ($k < 1 or $k >= 256 ** $w) { |
|
621
|
0
|
|
|
|
|
0
|
carp "quorum value out of range"; |
|
622
|
0
|
|
|
|
|
0
|
return undef; |
|
623
|
|
|
|
|
|
|
} |
|
624
|
|
|
|
|
|
|
# leave version check until call to sf_write_ida_header |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
# In all cases, we'll try to make all non-final chunks align to |
|
627
|
|
|
|
|
|
|
# $quorum x $width bytes. Whichever method is used, we need to know |
|
628
|
|
|
|
|
|
|
# what the total file size with/without padding will be. |
|
629
|
3
|
|
|
|
|
54
|
my $file_size=-s $filename; |
|
630
|
3
|
50
|
|
|
|
8
|
unless (defined($file_size)) { |
|
631
|
0
|
|
|
|
|
0
|
return undef; |
|
632
|
|
|
|
|
|
|
} |
|
633
|
3
|
|
|
|
|
3
|
my $padded_file_size=$file_size; |
|
634
|
3
|
|
|
|
|
14
|
while ($padded_file_size % ($k * $w)) { |
|
635
|
0
|
|
|
|
|
0
|
++$padded_file_size; # not very efficient, but it is easy |
|
636
|
|
|
|
|
|
|
} |
|
637
|
|
|
|
|
|
|
|
|
638
|
|
|
|
|
|
|
# We'll pass %o onto sf_write_ida_header later, so we need a dummy |
|
639
|
|
|
|
|
|
|
# value for transform if "save_transform" is set. |
|
640
|
3
|
50
|
33
|
|
|
16
|
if (defined($save_transform) and $save_transform) { |
|
641
|
|
|
|
|
|
|
#warn "making dummy transform array\n"; |
|
642
|
3
|
|
|
|
|
17
|
$o{"transform"} = [ (0) x ($k * $w) ]; |
|
643
|
|
|
|
|
|
|
} else { |
|
644
|
|
|
|
|
|
|
#warn "save_transform not defined\n"; |
|
645
|
0
|
|
|
|
|
0
|
$o{"transform"} = undef; |
|
646
|
|
|
|
|
|
|
} |
|
647
|
|
|
|
|
|
|
|
|
648
|
|
|
|
|
|
|
# Check that no more than one chunking method is set |
|
649
|
3
|
|
|
|
|
5
|
my $defined_methods=0; |
|
650
|
3
|
50
|
|
|
|
11
|
++$defined_methods if (defined($n_chunks)); |
|
651
|
3
|
50
|
|
|
|
10
|
++$defined_methods if (defined($in_chunk_size)); |
|
652
|
3
|
50
|
|
|
|
8
|
++$defined_methods if (defined($out_chunk_size)); |
|
653
|
3
|
50
|
|
|
|
9
|
++$defined_methods if (defined($out_file_size)); |
|
654
|
|
|
|
|
|
|
|
|
655
|
3
|
50
|
33
|
|
|
20
|
if ($defined_methods > 1) { |
|
|
|
50
|
|
|
|
|
|
|
656
|
0
|
|
|
|
|
0
|
carp "please select at most one method of calculating chunk sizes"; |
|
657
|
0
|
|
|
|
|
0
|
return undef; |
|
658
|
|
|
|
|
|
|
} elsif ($file_size == 0 or $defined_methods == 0) { |
|
659
|
|
|
|
|
|
|
# we can also handle the special case where $file_size == 0 here |
|
660
|
3
|
50
|
|
|
|
7
|
unless ($file_size) { |
|
661
|
0
|
|
|
|
|
0
|
carp "warning: zero-sized file $filename; will use single chunk"; |
|
662
|
|
|
|
|
|
|
} |
|
663
|
3
|
|
|
|
|
17
|
($cb,$cn,$cs)=(0,$file_size,$file_size); |
|
664
|
3
|
|
|
|
|
8
|
$o{"chunk_start"} = $cb; |
|
665
|
3
|
|
|
|
|
6
|
$o{"chunk_next"} = $cn; |
|
666
|
3
|
|
|
|
|
25
|
$hs=sf_write_ida_header(%o); |
|
667
|
3
|
50
|
33
|
|
|
34
|
unless (defined ($hs) and $hs > 0) { |
|
668
|
0
|
|
|
|
|
0
|
carp "Something wrong with header options."; |
|
669
|
0
|
|
|
|
|
0
|
return undef; |
|
670
|
|
|
|
|
|
|
} |
|
671
|
|
|
|
|
|
|
#warn "Single chunk\n"; |
|
672
|
|
|
|
|
|
|
return ( { |
|
673
|
3
|
|
|
|
|
39
|
"chunk_start" => $cb, |
|
674
|
|
|
|
|
|
|
"chunk_next" => $cn, |
|
675
|
|
|
|
|
|
|
"chunk_size" => $cs, |
|
676
|
|
|
|
|
|
|
"file_size" => $hs + $cs, |
|
677
|
|
|
|
|
|
|
"opt_final" => 1, |
|
678
|
|
|
|
|
|
|
"padding" => $padded_file_size - $file_size, |
|
679
|
|
|
|
|
|
|
} ); |
|
680
|
|
|
|
|
|
|
} |
|
681
|
|
|
|
|
|
|
|
|
682
|
|
|
|
|
|
|
# on to the various multi-chunk methods ... |
|
683
|
0
|
0
|
0
|
|
|
0
|
if (defined($n_chunks)) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
684
|
0
|
0
|
|
|
|
0
|
unless ($n_chunks > 0) { |
|
685
|
0
|
|
|
|
|
0
|
carp "Number of chunks must be greater than zero!"; |
|
686
|
0
|
|
|
|
|
0
|
return undef; |
|
687
|
|
|
|
|
|
|
} |
|
688
|
0
|
|
|
|
|
0
|
my $max_n_chunks=$padded_file_size / ($k * $w); |
|
689
|
0
|
0
|
|
|
|
0
|
if ( $n_chunks > $max_n_chunks ) { |
|
690
|
0
|
|
|
|
|
0
|
carp "File is too small for n_chunks=$n_chunks; using " . |
|
691
|
|
|
|
|
|
|
"$max_n_chunks instead"; |
|
692
|
0
|
|
|
|
|
0
|
$n_chunks=$max_n_chunks; |
|
693
|
|
|
|
|
|
|
} |
|
694
|
|
|
|
|
|
|
# creating chunks of exactly the same size may not be possible |
|
695
|
|
|
|
|
|
|
# since we have to round to matrix column size. Rounding down |
|
696
|
|
|
|
|
|
|
# means we'll end up with a larger chunk at the end, while |
|
697
|
|
|
|
|
|
|
# rounding up means we might produce some zero-sized chunks at the |
|
698
|
|
|
|
|
|
|
# end. The former option is most likely the Right Thing. Even |
|
699
|
|
|
|
|
|
|
# though it might be nice to make the first chunk bigger, it's |
|
700
|
|
|
|
|
|
|
# easier to code if we let the last chunk take up any excess. To |
|
701
|
|
|
|
|
|
|
# do this we can round the chunk size up to the nearest multiple |
|
702
|
|
|
|
|
|
|
# of $n_chunks first, then round down to the nearest column |
|
703
|
|
|
|
|
|
|
# size. We should end up with a non-zero value since we've imposed |
|
704
|
|
|
|
|
|
|
# a limit on the maximum size of $n_chunks above. |
|
705
|
0
|
|
|
|
|
0
|
$cs = int(($padded_file_size + $n_chunks - 1) / $n_chunks); |
|
706
|
0
|
|
|
|
|
0
|
$cs -= $cs % ($k * $w); |
|
707
|
0
|
0
|
|
|
|
0
|
die "Got chunk size of zero with padded file_size $padded_file_size," . |
|
708
|
|
|
|
|
|
|
" n_chunks=$n_chunks (this shouldn't happen)\n" unless $cs; |
|
709
|
0
|
|
|
|
|
0
|
($cb,$cn)=(0,$cs); |
|
710
|
0
|
|
|
|
|
0
|
for my $i (0 .. $n_chunks - 2) { # all pre-final chunks |
|
711
|
0
|
|
|
|
|
0
|
$o{"chunk_start"} = $cb; |
|
712
|
0
|
|
|
|
|
0
|
$o{"chunk_next"} = $cn; |
|
713
|
0
|
|
|
|
|
0
|
$hs=sf_write_ida_header(%o); |
|
714
|
0
|
0
|
0
|
|
|
0
|
unless (defined ($hs) and $hs > 0) { |
|
715
|
0
|
|
|
|
|
0
|
carp "Something wrong with header options for chunk $i."; |
|
716
|
0
|
|
|
|
|
0
|
return undef; |
|
717
|
|
|
|
|
|
|
} |
|
718
|
|
|
|
|
|
|
#warn "Chunk $cb-$cn, size $cs, fs=$hs + $cs, final=0\n"; |
|
719
|
0
|
|
|
|
|
0
|
push @chunks, { |
|
720
|
|
|
|
|
|
|
"chunk_start" => $cb, |
|
721
|
|
|
|
|
|
|
"chunk_next" => $cn, |
|
722
|
|
|
|
|
|
|
"chunk_size" => $cs, |
|
723
|
|
|
|
|
|
|
"file_size" => $hs + $cs, |
|
724
|
|
|
|
|
|
|
"opt_final" => 0, |
|
725
|
|
|
|
|
|
|
"padding" => 0, |
|
726
|
|
|
|
|
|
|
}; |
|
727
|
0
|
|
|
|
|
0
|
$cb += $cs; |
|
728
|
0
|
|
|
|
|
0
|
$cn += $cs; |
|
729
|
|
|
|
|
|
|
} |
|
730
|
|
|
|
|
|
|
# final chunk; need to do this separately since we need to pass |
|
731
|
|
|
|
|
|
|
# correct values for chunk range to accurately calculate the |
|
732
|
|
|
|
|
|
|
# header size (ie, a rough figure won't do if chunk_next is close |
|
733
|
|
|
|
|
|
|
# to 256 ** width) |
|
734
|
0
|
|
|
|
|
0
|
$o{"chunk_start"} = $cb; |
|
735
|
0
|
|
|
|
|
0
|
$o{"chunk_next"} = $file_size; # without padding |
|
736
|
0
|
|
|
|
|
0
|
$hs=sf_write_ida_header(%o); |
|
737
|
0
|
|
|
|
|
0
|
push @chunks, { |
|
738
|
|
|
|
|
|
|
"chunk_start" => $cb, |
|
739
|
|
|
|
|
|
|
"chunk_next" => $file_size, |
|
740
|
|
|
|
|
|
|
"chunk_size" => $file_size - $cb, |
|
741
|
|
|
|
|
|
|
"file_size" => $hs + $padded_file_size, |
|
742
|
|
|
|
|
|
|
"opt_final" => 1, |
|
743
|
|
|
|
|
|
|
"padding" => $padded_file_size - $file_size, |
|
744
|
|
|
|
|
|
|
}; |
|
745
|
|
|
|
|
|
|
#warn "Last chunk: $cb-$padded_file_size, size ". |
|
746
|
|
|
|
|
|
|
# ($padded_file_size - $cb) . ", fs=$hs + $padded_file_size - $cb, ". |
|
747
|
|
|
|
|
|
|
# "final=1\n"; |
|
748
|
|
|
|
|
|
|
|
|
749
|
0
|
0
|
|
|
|
0
|
die "last chunk starts beyond eof (this shouldn't happen)\n" if |
|
750
|
|
|
|
|
|
|
($cb >= $padded_file_size); |
|
751
|
|
|
|
|
|
|
# ... and return the array |
|
752
|
0
|
|
|
|
|
0
|
return @chunks; |
|
753
|
|
|
|
|
|
|
} elsif (defined($in_chunk_size) or defined($out_chunk_size)) { |
|
754
|
|
|
|
|
|
|
# this can actually be rolled into the above n_chunks method |
|
755
|
0
|
|
|
|
|
0
|
carp "not implemented yet"; |
|
756
|
0
|
|
|
|
|
0
|
return undef; |
|
757
|
|
|
|
|
|
|
} elsif (defined($out_chunk_size)) { |
|
758
|
0
|
|
|
|
|
0
|
carp "not implemented yet"; |
|
759
|
0
|
|
|
|
|
0
|
return undef; |
|
760
|
|
|
|
|
|
|
} else { |
|
761
|
0
|
|
|
|
|
0
|
1; |
|
762
|
|
|
|
|
|
|
#die "problem deciding chunking method (shouldn't get here)\n"; |
|
763
|
|
|
|
|
|
|
} |
|
764
|
|
|
|
|
|
|
} |
|
765
|
|
|
|
|
|
|
|
|
766
|
|
|
|
|
|
|
sub sf_split { |
|
767
|
3
|
|
|
3
|
0
|
615
|
my ($self,$class); |
|
768
|
3
|
50
|
33
|
|
|
25
|
if ($_[0] eq $classname or ref($_[0]) eq $classname) { |
|
769
|
0
|
|
|
|
|
0
|
$self=shift; |
|
770
|
0
|
|
|
|
|
0
|
$class=ref($self); |
|
771
|
|
|
|
|
|
|
} else { |
|
772
|
3
|
|
|
|
|
7
|
$self=$classname; |
|
773
|
|
|
|
|
|
|
} |
|
774
|
3
|
|
|
|
|
66
|
my %o=( |
|
775
|
|
|
|
|
|
|
# We'll be passing this hash on directly to ida_split later on |
|
776
|
|
|
|
|
|
|
# so option names here will overlap with the option names needed |
|
777
|
|
|
|
|
|
|
# by that routine. The same applies to option names in |
|
778
|
|
|
|
|
|
|
# sf_write_ida_header. |
|
779
|
|
|
|
|
|
|
shares => undef, |
|
780
|
|
|
|
|
|
|
quorum => undef, |
|
781
|
|
|
|
|
|
|
width => 1, |
|
782
|
|
|
|
|
|
|
filename => undef, |
|
783
|
|
|
|
|
|
|
# supply a key, a matrix or neither |
|
784
|
|
|
|
|
|
|
key => undef, |
|
785
|
|
|
|
|
|
|
matrix => undef, |
|
786
|
|
|
|
|
|
|
# misc options |
|
787
|
|
|
|
|
|
|
version => 1, # header version |
|
788
|
|
|
|
|
|
|
rand => "/dev/urandom", |
|
789
|
|
|
|
|
|
|
bufsize => 4096, |
|
790
|
|
|
|
|
|
|
save_transform => 1, |
|
791
|
|
|
|
|
|
|
# pick at most one chunking method. The file is not broken into |
|
792
|
|
|
|
|
|
|
# chunks unless one of these is defined. |
|
793
|
|
|
|
|
|
|
n_chunks => undef, |
|
794
|
|
|
|
|
|
|
in_chunk_size => undef, |
|
795
|
|
|
|
|
|
|
out_chunk_size => undef, |
|
796
|
|
|
|
|
|
|
out_file_size => undef, |
|
797
|
|
|
|
|
|
|
# allow creation of a subset of shares, chunks |
|
798
|
|
|
|
|
|
|
sharelist => undef, # [ $row1, $row2, ... ] |
|
799
|
|
|
|
|
|
|
chunklist => undef, # [ $chunk1, $chunk2, ... ] |
|
800
|
|
|
|
|
|
|
# specify pattern to use for share filenames |
|
801
|
|
|
|
|
|
|
filespec => undef, # default value set later on |
|
802
|
|
|
|
|
|
|
@_, |
|
803
|
|
|
|
|
|
|
# The file format uses network (big-endian) byte order, so store |
|
804
|
|
|
|
|
|
|
# this info after all the user-supplied options have been read |
|
805
|
|
|
|
|
|
|
# in |
|
806
|
|
|
|
|
|
|
inorder => 2, |
|
807
|
|
|
|
|
|
|
outorder => 2, |
|
808
|
|
|
|
|
|
|
opt_final => 0, |
|
809
|
|
|
|
|
|
|
); |
|
810
|
|
|
|
|
|
|
|
|
811
|
3
|
|
|
|
|
4
|
my (@chunks, @results); |
|
812
|
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
# Copy options into local variables |
|
814
|
51
|
50
|
|
|
|
122
|
my ($n, $k, $w, $filename, |
|
815
|
|
|
|
|
|
|
$key, $mat, $version, |
|
816
|
|
|
|
|
|
|
$rng, $bufsize, |
|
817
|
|
|
|
|
|
|
$save_transform, |
|
818
|
|
|
|
|
|
|
$n_chunks, $in_chunk_size, $out_chunk_size, $out_file_size, |
|
819
|
|
|
|
|
|
|
$sharelist, $chunklist,$filespec |
|
820
|
|
|
|
|
|
|
) = |
|
821
|
|
|
|
|
|
|
map { |
|
822
|
3
|
|
|
|
|
10
|
exists($o{$_}) ? $o{$_} : undef |
|
823
|
|
|
|
|
|
|
} qw( |
|
824
|
|
|
|
|
|
|
shares quorum width filename |
|
825
|
|
|
|
|
|
|
key matrix version |
|
826
|
|
|
|
|
|
|
rand bufsize save_transform |
|
827
|
|
|
|
|
|
|
n_chunks in_chunk_size out_chunk_size out_file_size |
|
828
|
|
|
|
|
|
|
sharelist chunklist filespec); |
|
829
|
|
|
|
|
|
|
|
|
830
|
|
|
|
|
|
|
|
|
831
|
|
|
|
|
|
|
# Pass all options to sf_calculate_chunk_sizes and let it figure out |
|
832
|
|
|
|
|
|
|
# all the details for each chunk. |
|
833
|
3
|
|
|
|
|
28
|
@chunks=sf_calculate_chunk_sizes(%o); |
|
834
|
3
|
50
|
|
|
|
13
|
unless (defined($chunks[0])) { |
|
835
|
0
|
|
|
|
|
0
|
carp "Problem calculating chunk sizes from given options"; |
|
836
|
0
|
|
|
|
|
0
|
return undef; |
|
837
|
|
|
|
|
|
|
} |
|
838
|
|
|
|
|
|
|
|
|
839
|
|
|
|
|
|
|
# Now that we know how many chunks there are, we can check that the |
|
840
|
|
|
|
|
|
|
# filespec mentions "%c" for the chunk number. The "%s" specifier is |
|
841
|
|
|
|
|
|
|
# also always required. Also, we can set up different default |
|
842
|
|
|
|
|
|
|
# filespecs for single-chunk and multi-chunk splits. |
|
843
|
3
|
50
|
|
|
|
7
|
if (defined($filespec)) { |
|
844
|
0
|
0
|
|
|
|
0
|
unless ($filespec =~ /\%s/) { |
|
845
|
0
|
|
|
|
|
0
|
carp "filespec must include \%s for share number"; |
|
846
|
0
|
|
|
|
|
0
|
return undef; |
|
847
|
|
|
|
|
|
|
} |
|
848
|
0
|
0
|
0
|
|
|
0
|
unless (scalar (@chunks) == 1 or $filespec =~ /\%c/) { |
|
849
|
0
|
|
|
|
|
0
|
carp "filespec must include \%c for multi-chunk splits"; |
|
850
|
0
|
|
|
|
|
0
|
return undef; |
|
851
|
|
|
|
|
|
|
} |
|
852
|
|
|
|
|
|
|
} else { |
|
853
|
3
|
50
|
|
|
|
11
|
$filespec=(scalar (@chunks) == 1) ? '%f-%s.sf' : '%f-%c-%s.sf'; |
|
854
|
|
|
|
|
|
|
} |
|
855
|
|
|
|
|
|
|
|
|
856
|
|
|
|
|
|
|
# check the sharelist and chunklist arrays to weed out dups and |
|
857
|
|
|
|
|
|
|
# invalid share/chunk numbers. If we weren't passed a value for one |
|
858
|
|
|
|
|
|
|
# or the other, then we'll default to processing all shares/all |
|
859
|
|
|
|
|
|
|
# chunks. |
|
860
|
3
|
100
|
|
|
|
6
|
if (defined($sharelist)) { |
|
861
|
1
|
|
|
|
|
8
|
ida_check_list($sharelist,"share",0,$n-1); |
|
862
|
1
|
50
|
|
|
|
43
|
unless (scalar(@$sharelist) == $n) { |
|
863
|
0
|
|
|
|
|
0
|
carp "sharelist does not contain n=$n share numbers; aborting"; |
|
864
|
0
|
|
|
|
|
0
|
return undef; |
|
865
|
|
|
|
|
|
|
} |
|
866
|
|
|
|
|
|
|
} else { |
|
867
|
2
|
|
|
|
|
6
|
$sharelist=[ 0 .. $n - 1 ]; |
|
868
|
|
|
|
|
|
|
} |
|
869
|
|
|
|
|
|
|
|
|
870
|
3
|
50
|
|
|
|
6
|
if (defined($chunklist)) { |
|
871
|
0
|
|
|
|
|
0
|
ida_check_list($chunklist,"chunk",0,scalar(@chunks)-1); |
|
872
|
0
|
0
|
|
|
|
0
|
unless (scalar(@$chunklist) > 0) { |
|
873
|
0
|
|
|
|
|
0
|
carp "chunklist does not contain any valid chunk numbers; aborting"; |
|
874
|
0
|
|
|
|
|
0
|
return undef; |
|
875
|
|
|
|
|
|
|
} |
|
876
|
|
|
|
|
|
|
} else { |
|
877
|
3
|
|
|
|
|
11
|
$chunklist=[ 0 .. scalar(@chunks) - 1 ]; |
|
878
|
|
|
|
|
|
|
} |
|
879
|
|
|
|
|
|
|
|
|
880
|
|
|
|
|
|
|
# Now loop through each chunk that we've been asked to create |
|
881
|
3
|
|
|
|
|
5
|
for my $i (@$chunklist) { |
|
882
|
|
|
|
|
|
|
|
|
883
|
3
|
|
|
|
|
71
|
my $chunk=$chunks[$i]; |
|
884
|
3
|
|
|
|
|
9
|
my @sharefiles=(); # we return a list of files in each |
|
885
|
|
|
|
|
|
|
# chunk at the end of the routine. |
|
886
|
|
|
|
|
|
|
|
|
887
|
|
|
|
|
|
|
# Unpack chunk details into local variables. Not all these |
|
888
|
|
|
|
|
|
|
# variables are needed, but we might as well unpack them anyway. |
|
889
|
18
|
|
|
|
|
30
|
my ($chunk_start,$chunk_next,$chunk_size,$file_size, |
|
890
|
|
|
|
|
|
|
$opt_final,$padding) = |
|
891
|
3
|
|
|
|
|
5
|
map { $chunk->{$_} } |
|
892
|
|
|
|
|
|
|
qw ( |
|
893
|
|
|
|
|
|
|
chunk_start chunk_next chunk_size file_size |
|
894
|
|
|
|
|
|
|
opt_final padding |
|
895
|
|
|
|
|
|
|
); |
|
896
|
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
# We should only really need to open the input file once, |
|
898
|
|
|
|
|
|
|
# regardless of how many chunks/shares we're creating. But since |
|
899
|
|
|
|
|
|
|
# we're using Crypt::IDA's file reader, and it allows us to seek |
|
900
|
|
|
|
|
|
|
# to the start of the chunk when we create the callback, it's |
|
901
|
|
|
|
|
|
|
# easier to (re-)open and seek once per chunk. |
|
902
|
3
|
|
|
|
|
15
|
my $filler=fill_from_file($filename, $k * $w, $chunk_start); |
|
903
|
3
|
50
|
|
|
|
10
|
unless (defined($filler)) { |
|
904
|
0
|
|
|
|
|
0
|
carp "Failed to open input file: $!"; |
|
905
|
0
|
|
|
|
|
0
|
return undef; |
|
906
|
|
|
|
|
|
|
} |
|
907
|
|
|
|
|
|
|
|
|
908
|
|
|
|
|
|
|
# Unfortunately, creating a new share isn't quite as simple as |
|
909
|
|
|
|
|
|
|
# calling ida_split with all our parameters. The job is |
|
910
|
|
|
|
|
|
|
# complicated by the fact that we need to store both the share |
|
911
|
|
|
|
|
|
|
# data and (usually) a row of the transform matrix. In the case |
|
912
|
|
|
|
|
|
|
# where a new transform matrix would be created by the call to |
|
913
|
|
|
|
|
|
|
# ida_split, then we would have to wait until it returned before |
|
914
|
|
|
|
|
|
|
# writing the transform rows for it to each share header. But that |
|
915
|
|
|
|
|
|
|
# would require that we write the header after the share, which |
|
916
|
|
|
|
|
|
|
# isn't a very nice solution. Also, we'd still have to calculate |
|
917
|
|
|
|
|
|
|
# the correct amount of space to allocate for the header before |
|
918
|
|
|
|
|
|
|
# setting up the empty handlers, which is also a bit messy. |
|
919
|
|
|
|
|
|
|
# |
|
920
|
|
|
|
|
|
|
# The simplest solution is to examine the key/matrix and |
|
921
|
|
|
|
|
|
|
# save_transform options we've been given and call the |
|
922
|
|
|
|
|
|
|
# ida_generate_key and/or ida_key_to_matrix routines ourselves, if |
|
923
|
|
|
|
|
|
|
# necessary. Then we will know which transform rows to save with |
|
924
|
|
|
|
|
|
|
# each share and we can pass our generated key/matrix directly on |
|
925
|
|
|
|
|
|
|
# to ida_split. |
|
926
|
|
|
|
|
|
|
|
|
927
|
3
|
50
|
|
|
|
24
|
if (ida_check_transform_opts(%o)) { |
|
928
|
0
|
|
|
|
|
0
|
carp "Can't proceed due to problem with transform options"; |
|
929
|
0
|
|
|
|
|
0
|
return undef; |
|
930
|
|
|
|
|
|
|
} |
|
931
|
3
|
100
|
|
|
|
12
|
unless (defined($mat)) { |
|
932
|
2
|
100
|
|
|
|
6
|
if (defined ($key)) { |
|
933
|
1
|
50
|
|
|
|
7
|
if (ida_check_key($k,$n,$w,$key)) { |
|
934
|
0
|
|
|
|
|
0
|
carp "Problem with supplied key"; |
|
935
|
0
|
|
|
|
|
0
|
return undef; |
|
936
|
|
|
|
|
|
|
} |
|
937
|
|
|
|
|
|
|
} else { |
|
938
|
1
|
|
|
|
|
4
|
$rng=ida_rng_init($w,$rng); # swap string for closure |
|
939
|
1
|
50
|
|
|
|
5
|
unless (defined($rng)) { |
|
940
|
0
|
|
|
|
|
0
|
carp "Failed to initialise random number generator"; |
|
941
|
0
|
|
|
|
|
0
|
return undef; |
|
942
|
|
|
|
|
|
|
} |
|
943
|
1
|
|
|
|
|
4
|
$key=ida_generate_key($k,$n,$w,$rng); |
|
944
|
|
|
|
|
|
|
} |
|
945
|
|
|
|
|
|
|
|
|
946
|
|
|
|
|
|
|
# now generate matrix from key |
|
947
|
2
|
|
|
|
|
13
|
$mat=ida_key_to_matrix( "quorum" => $k, |
|
948
|
|
|
|
|
|
|
"shares" => $n, |
|
949
|
|
|
|
|
|
|
"width" => $w, |
|
950
|
|
|
|
|
|
|
"sharelist" => $sharelist, |
|
951
|
|
|
|
|
|
|
"key" => $key, |
|
952
|
|
|
|
|
|
|
"skipchecks?" => 0); |
|
953
|
2
|
50
|
|
|
|
7
|
unless (defined($mat)) { |
|
954
|
0
|
|
|
|
|
0
|
carp "bad return value from ida_key_to_matrix"; |
|
955
|
0
|
|
|
|
|
0
|
return undef; |
|
956
|
|
|
|
|
|
|
} |
|
957
|
2
|
|
|
|
|
4
|
$o{"matrix"}=$mat; # stash new matrix |
|
958
|
2
|
|
|
|
|
8
|
$o{"key"}=undef; # and undefine key (if any) |
|
959
|
|
|
|
|
|
|
} |
|
960
|
|
|
|
|
|
|
|
|
961
|
3
|
|
|
|
|
7
|
$o{"chunk_start"}= $chunk_start; # same values for all shares |
|
962
|
3
|
|
|
|
|
5
|
$o{"chunk_next"} = $chunk_next; # in this chunk |
|
963
|
3
|
|
|
|
|
5
|
$o{"opt_final"} = $opt_final; |
|
964
|
|
|
|
|
|
|
#warn "Going to create chunk $chunk_start - $chunk_next (final $opt_final)\n"; |
|
965
|
3
|
|
|
|
|
6
|
my $emptiers=[]; |
|
966
|
3
|
|
|
|
|
8
|
for my $j (@$sharelist) { |
|
967
|
|
|
|
|
|
|
# For opening output files, we're responsible for writing the file |
|
968
|
|
|
|
|
|
|
# header, so we first make one of our ostreams, write the header, |
|
969
|
|
|
|
|
|
|
# then create a new empty_to_fh handler which will seek past the |
|
970
|
|
|
|
|
|
|
# header. |
|
971
|
9
|
|
|
|
|
26
|
my $sharename = sf_sprintf_filename($filespec, $filename, $i, $j); |
|
972
|
9
|
|
|
|
|
151
|
unlink $sharename; # remove any existing file |
|
973
|
9
|
|
|
|
|
22
|
my $sharestream = sf_mk_file_ostream($sharename, $w); |
|
974
|
9
|
50
|
|
|
|
19
|
unless (defined($sharestream)) { |
|
975
|
0
|
|
|
|
|
0
|
carp "Failed to create share file (chunk $i, share $j): $!"; |
|
976
|
0
|
|
|
|
|
0
|
return undef; |
|
977
|
|
|
|
|
|
|
} |
|
978
|
9
|
|
|
|
|
67
|
my $hs=sf_write_ida_header(%o, ostream => $sharestream, |
|
979
|
|
|
|
|
|
|
transform => [$mat->getvals($j,0,$k)]); |
|
980
|
9
|
50
|
33
|
|
|
61
|
unless (defined ($hs) and $hs > 0) { |
|
981
|
0
|
|
|
|
|
0
|
carp "Problem writing header for share (chunk $i, share $j)"; |
|
982
|
0
|
|
|
|
|
0
|
return undef; |
|
983
|
|
|
|
|
|
|
} |
|
984
|
9
|
50
|
|
|
|
24
|
unless ($hs + $chunk_size == $file_size) { |
|
985
|
0
|
|
|
|
|
0
|
carp "file size mismatch ($i,$j) (this shouldn't happen)"; |
|
986
|
0
|
|
|
|
|
0
|
carp "hs=$hs; chunk_size=$chunk_size; file_size=$file_size; pad=$padding"; |
|
987
|
0
|
|
|
|
|
0
|
return undef; |
|
988
|
|
|
|
|
|
|
} |
|
989
|
9
|
|
|
|
|
20
|
my $emptier=empty_to_fh($sharestream->{"FH"}->(),$hs); |
|
990
|
9
|
|
|
|
|
20
|
push @$emptiers, $emptier; |
|
991
|
9
|
|
|
|
|
123
|
push @sharefiles, $sharename; |
|
992
|
|
|
|
|
|
|
} |
|
993
|
|
|
|
|
|
|
|
|
994
|
|
|
|
|
|
|
# Now that we've written the headers and set up the fill and empty |
|
995
|
|
|
|
|
|
|
# handlers, we only need to add details of the filler and |
|
996
|
|
|
|
|
|
|
# emptiers, then pass the entire options array on to ida_split to |
|
997
|
|
|
|
|
|
|
# create all shares for this chunk. |
|
998
|
3
|
|
|
|
|
9
|
$o{"filler"} = $filler; |
|
999
|
3
|
|
|
|
|
7
|
$o{"emptiers"} = $emptiers; |
|
1000
|
3
|
|
|
|
|
25
|
my ($key,$mat,$bytes)=ida_split(%o); |
|
1001
|
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
# check for success, then save the results |
|
1003
|
3
|
50
|
|
|
|
14
|
unless (defined($mat)) { |
|
1004
|
0
|
|
|
|
|
0
|
carp "detected failure in ida_split; quitting"; |
|
1005
|
0
|
|
|
|
|
0
|
return undef; |
|
1006
|
|
|
|
|
|
|
} |
|
1007
|
3
|
|
|
|
|
19
|
push @results, [$key,$mat,$bytes, @sharefiles]; |
|
1008
|
|
|
|
|
|
|
|
|
1009
|
|
|
|
|
|
|
# Perl should handle closing file handles for us once they go out |
|
1010
|
|
|
|
|
|
|
# of scope and they're destroyed. |
|
1011
|
|
|
|
|
|
|
|
|
1012
|
|
|
|
|
|
|
} |
|
1013
|
|
|
|
|
|
|
|
|
1014
|
3
|
|
|
|
|
195
|
return @results; |
|
1015
|
|
|
|
|
|
|
|
|
1016
|
|
|
|
|
|
|
} |
|
1017
|
|
|
|
|
|
|
|
|
1018
|
|
|
|
|
|
|
sub sf_combine { |
|
1019
|
|
|
|
|
|
|
|
|
1020
|
|
|
|
|
|
|
# Combining files is complicated by two issues: |
|
1021
|
|
|
|
|
|
|
# |
|
1022
|
|
|
|
|
|
|
# * Given a list of files, we don't know anything about which files |
|
1023
|
|
|
|
|
|
|
# are supposed to belong to which chunk, so we would need to read |
|
1024
|
|
|
|
|
|
|
# through the file headers to determine the chunk_start, |
|
1025
|
|
|
|
|
|
|
# chunk_next values and use these to group the files. |
|
1026
|
|
|
|
|
|
|
# * The file format allows for omission of the transform data, so we |
|
1027
|
|
|
|
|
|
|
# have to support having a key or transform matrix passed to us |
|
1028
|
|
|
|
|
|
|
# for each chunk. |
|
1029
|
|
|
|
|
|
|
# |
|
1030
|
|
|
|
|
|
|
# The simplest solution to the first problem is to place |
|
1031
|
|
|
|
|
|
|
# responsibilty for identifying which files go together to form a |
|
1032
|
|
|
|
|
|
|
# complete chunk on the user. This should not be too onerous a task, |
|
1033
|
|
|
|
|
|
|
# since the sf_split routine allows the caller to store the share |
|
1034
|
|
|
|
|
|
|
# number and chunk number in each output filename. It also returns |
|
1035
|
|
|
|
|
|
|
# the names of the sharefiles for each chunk. |
|
1036
|
|
|
|
|
|
|
# |
|
1037
|
|
|
|
|
|
|
# That still leaves the problem of passing in a key or transform |
|
1038
|
|
|
|
|
|
|
# matrix and a row list (to associate each share with a particular |
|
1039
|
|
|
|
|
|
|
# row of the transform matrix). The problem isn't so much with being |
|
1040
|
|
|
|
|
|
|
# able to support this method of operation (since ida_combine |
|
1041
|
|
|
|
|
|
|
# already supports it), but with coming up with a calling convention |
|
1042
|
|
|
|
|
|
|
# which won't be overly complex. |
|
1043
|
|
|
|
|
|
|
# |
|
1044
|
|
|
|
|
|
|
# Note that the this issue of the key/transform matrix being stored |
|
1045
|
|
|
|
|
|
|
# outside the file highlights a potential problem with the file |
|
1046
|
|
|
|
|
|
|
# format. Namely, if the transform data isn't stored in the file, |
|
1047
|
|
|
|
|
|
|
# there's nothing within the file itself to indicate which row of |
|
1048
|
|
|
|
|
|
|
# the transform matrix the share corresponds to. The filename itself |
|
1049
|
|
|
|
|
|
|
# should provide this data, but if the contents of the file are |
|
1050
|
|
|
|
|
|
|
# transmitted and the filename gets lost or changed, then there's a |
|
1051
|
|
|
|
|
|
|
# possibility that the person combining the files will have |
|
1052
|
|
|
|
|
|
|
# problems. There are two solutions to this: either the split |
|
1053
|
|
|
|
|
|
|
# routine incorporates a share number within the header, or it's up |
|
1054
|
|
|
|
|
|
|
# to the central issuing authority (Dealer) to, eg, store a hash of |
|
1055
|
|
|
|
|
|
|
# each share and the associated row number for that share in the |
|
1056
|
|
|
|
|
|
|
# same database it uses to store the key/transform matrix. Partly |
|
1057
|
|
|
|
|
|
|
# because there are solutions, but mostly because I don't think that |
|
1058
|
|
|
|
|
|
|
# the centrally-stored key/transform matrix idea is a very good one, |
|
1059
|
|
|
|
|
|
|
# I don't feel inclined to change the current file format to include |
|
1060
|
|
|
|
|
|
|
# row numbers in the header. At least not for the time being. |
|
1061
|
|
|
|
|
|
|
# |
|
1062
|
|
|
|
|
|
|
# Getting back to the issue at hand, namely the calling convention, |
|
1063
|
|
|
|
|
|
|
# it seems that the best solution would be keep the options here |
|
1064
|
|
|
|
|
|
|
# as close as possible to the ones accepted by ida_combine. The two |
|
1065
|
|
|
|
|
|
|
# differences are: |
|
1066
|
|
|
|
|
|
|
# |
|
1067
|
|
|
|
|
|
|
# * instead of fillers and an emptier, we handle infiles and an |
|
1068
|
|
|
|
|
|
|
# outfile |
|
1069
|
|
|
|
|
|
|
# |
|
1070
|
|
|
|
|
|
|
# * since we might need to handle multiple chunks, but ida_combine |
|
1071
|
|
|
|
|
|
|
# only operates on a single set of shares, we should either accept |
|
1072
|
|
|
|
|
|
|
# an array of options (one for each chunk) or only operate on |
|
1073
|
|
|
|
|
|
|
# one chunk at a time. I'll go with the latter option since it |
|
1074
|
|
|
|
|
|
|
# doesn't place much extra work (if any) on the calling program |
|
1075
|
|
|
|
|
|
|
# and it probably makes it less error-prone since the user doesn't |
|
1076
|
|
|
|
|
|
|
# have to remember to pass an array rather than a list of options. |
|
1077
|
|
|
|
|
|
|
# Having finer granularity might help the caller with regards to |
|
1078
|
|
|
|
|
|
|
# handling return values and error returns, too. |
|
1079
|
|
|
|
|
|
|
# |
|
1080
|
|
|
|
|
|
|
# That said, on with the code ... |
|
1081
|
|
|
|
|
|
|
|
|
1082
|
3
|
|
|
3
|
1
|
248
|
my ($self,$class); |
|
1083
|
3
|
50
|
33
|
|
|
25
|
if ($_[0] eq $classname or ref($_[0]) eq $classname) { |
|
1084
|
0
|
|
|
|
|
0
|
$self=shift; |
|
1085
|
0
|
|
|
|
|
0
|
$class=ref($self); |
|
1086
|
|
|
|
|
|
|
} else { |
|
1087
|
3
|
|
|
|
|
6
|
$self=$classname; |
|
1088
|
|
|
|
|
|
|
} |
|
1089
|
3
|
|
|
|
|
35
|
my %o= |
|
1090
|
|
|
|
|
|
|
( |
|
1091
|
|
|
|
|
|
|
# Options for source, sinks. These are the only required options. |
|
1092
|
|
|
|
|
|
|
infiles => undef, # [ $file1, $file2, ... ] |
|
1093
|
|
|
|
|
|
|
outfile => undef, # "filename" |
|
1094
|
|
|
|
|
|
|
# If specified, the following must agree with the values stored |
|
1095
|
|
|
|
|
|
|
# in the sharefiles. There's normally no need to set these. |
|
1096
|
|
|
|
|
|
|
quorum => undef, |
|
1097
|
|
|
|
|
|
|
width => undef, |
|
1098
|
|
|
|
|
|
|
# If matrix is set, it must be a pre-inverted matrix, and it will |
|
1099
|
|
|
|
|
|
|
# override any values read in from the file (along with emitting |
|
1100
|
|
|
|
|
|
|
# a warning if both are found). Alternatively, if a key is |
|
1101
|
|
|
|
|
|
|
# supplied, the 'shares' and 'sharelist' options must also be |
|
1102
|
|
|
|
|
|
|
# given. A 'key' will also override any values stored in the file |
|
1103
|
|
|
|
|
|
|
# and also emit a warning if both are found. |
|
1104
|
|
|
|
|
|
|
key => undef, |
|
1105
|
|
|
|
|
|
|
matrix => undef, |
|
1106
|
|
|
|
|
|
|
shares => undef, # only needed if key supplied |
|
1107
|
|
|
|
|
|
|
sharelist => undef, # only needed if key supplied |
|
1108
|
|
|
|
|
|
|
# misc options |
|
1109
|
|
|
|
|
|
|
bufsize => 4096, |
|
1110
|
|
|
|
|
|
|
@_, |
|
1111
|
|
|
|
|
|
|
# byte order options (can't be overriden) |
|
1112
|
|
|
|
|
|
|
inorder => 2, |
|
1113
|
|
|
|
|
|
|
outorder => 2, |
|
1114
|
|
|
|
|
|
|
# no point in accepting a user-supplied value of $bytes, since we |
|
1115
|
|
|
|
|
|
|
# determine this from the share headers |
|
1116
|
|
|
|
|
|
|
bytes => undef, |
|
1117
|
|
|
|
|
|
|
); |
|
1118
|
|
|
|
|
|
|
|
|
1119
|
|
|
|
|
|
|
# copy all options into local variables |
|
1120
|
39
|
50
|
|
|
|
95
|
my ($k,$n,$w,$key,$mat,$shares,$sharelist,$infiles,$outfile, |
|
1121
|
|
|
|
|
|
|
$bufsize,$inorder,$outorder,$bytes) = |
|
1122
|
|
|
|
|
|
|
map { |
|
1123
|
3
|
|
|
|
|
8
|
exists($o{$_}) ? $o{$_} : undef; |
|
1124
|
|
|
|
|
|
|
} qw(quorum shares width key matrix shares sharelist |
|
1125
|
|
|
|
|
|
|
infiles outfile bufsize inorder outorder bytes); |
|
1126
|
3
|
|
|
|
|
9
|
my $fillers=[]; |
|
1127
|
|
|
|
|
|
|
|
|
1128
|
|
|
|
|
|
|
# Check options |
|
1129
|
3
|
50
|
33
|
|
|
12
|
if (defined($key) and defined($mat)) { |
|
1130
|
0
|
|
|
|
|
0
|
carp "Conflicting key/matrix options given."; |
|
1131
|
0
|
|
|
|
|
0
|
return undef; |
|
1132
|
|
|
|
|
|
|
} |
|
1133
|
3
|
50
|
0
|
|
|
12
|
if (defined($key) and !(defined($shares) and defined($sharelist))) { |
|
|
|
|
33
|
|
|
|
|
|
1134
|
0
|
|
|
|
|
0
|
carp "key option also requires shares and sharelist options."; |
|
1135
|
0
|
|
|
|
|
0
|
return undef; |
|
1136
|
|
|
|
|
|
|
} |
|
1137
|
|
|
|
|
|
|
|
|
1138
|
|
|
|
|
|
|
# Weed out any duplicate input file names. If duplicates are found, |
|
1139
|
|
|
|
|
|
|
# and the sharelist option is given, then some of the share numbers |
|
1140
|
|
|
|
|
|
|
# in that list will probably now be wrong. Rather than trying to fix |
|
1141
|
|
|
|
|
|
|
# up the numbers, we'll take the easy way out and simply report it |
|
1142
|
|
|
|
|
|
|
# as an error and return. |
|
1143
|
3
|
50
|
|
|
|
12
|
unless (scalar(@$infiles)) { |
|
1144
|
0
|
|
|
|
|
0
|
carp "No input files to process; aborting."; |
|
1145
|
0
|
|
|
|
|
0
|
return undef; |
|
1146
|
|
|
|
|
|
|
} |
|
1147
|
3
|
|
|
|
|
5
|
my %saw_file; |
|
1148
|
3
|
|
|
|
|
6
|
my $new_filelist=[]; |
|
1149
|
3
|
|
|
|
|
75
|
foreach my $infile (@$infiles) { |
|
1150
|
9
|
50
|
|
|
|
57
|
if (exists($saw_file{$infile})) { |
|
1151
|
0
|
|
|
|
|
0
|
carp "Ignoring duplicate input file: $infile"; |
|
1152
|
|
|
|
|
|
|
} else { |
|
1153
|
9
|
50
|
|
|
|
23
|
if (defined ($sharelist)) { |
|
1154
|
0
|
|
|
|
|
0
|
carp "Duplicate file invalidates supplied sharelist; aborting"; |
|
1155
|
0
|
|
|
|
|
0
|
return undef; |
|
1156
|
|
|
|
|
|
|
} |
|
1157
|
9
|
|
|
|
|
25
|
$saw_file{$infile} = 1; |
|
1158
|
9
|
|
|
|
|
25
|
push @$new_filelist, $infile; |
|
1159
|
|
|
|
|
|
|
} |
|
1160
|
|
|
|
|
|
|
} |
|
1161
|
3
|
|
|
|
|
8
|
$infiles=$new_filelist; |
|
1162
|
|
|
|
|
|
|
|
|
1163
|
|
|
|
|
|
|
# If k-value is given, only check it after we've de-duped the input |
|
1164
|
|
|
|
|
|
|
# file list. |
|
1165
|
3
|
50
|
33
|
|
|
11
|
if (defined($k) and scalar(@$infiles) < $k) { |
|
1166
|
0
|
|
|
|
|
0
|
carp "For given quorum value $k, I need (at least) $k infiles"; |
|
1167
|
0
|
|
|
|
|
0
|
return undef; |
|
1168
|
|
|
|
|
|
|
} |
|
1169
|
|
|
|
|
|
|
|
|
1170
|
|
|
|
|
|
|
# We won't build the transform matrix until later (or not at all if |
|
1171
|
|
|
|
|
|
|
# we're supplied with a matrix or key option). We'll store the |
|
1172
|
|
|
|
|
|
|
# values returned from sf_read_ida_header in a regular array and |
|
1173
|
|
|
|
|
|
|
# then convert them into a Math::FastGF2::Matrix object when we come |
|
1174
|
|
|
|
|
|
|
# to calculating the inverse matrix. |
|
1175
|
3
|
|
|
|
|
8
|
my @matrix=(); |
|
1176
|
|
|
|
|
|
|
|
|
1177
|
|
|
|
|
|
|
# Read in headers from each infile and create a new filler for each |
|
1178
|
3
|
|
|
|
|
8
|
my ($nshares, $header_info, $header_size)=(0,undef,undef); |
|
1179
|
3
|
|
|
|
|
7
|
my ($chunk_start,$chunk_next)=(undef,undef); |
|
1180
|
3
|
|
|
|
|
9
|
foreach my $infile (@$infiles) { |
|
1181
|
|
|
|
|
|
|
|
|
1182
|
9
|
|
|
|
|
25
|
my $istream=sf_mk_file_istream($infile,1); |
|
1183
|
9
|
50
|
|
|
|
30
|
unless (defined($istream)) { |
|
1184
|
0
|
|
|
|
|
0
|
carp "Problem opening input file $infile: $!"; |
|
1185
|
0
|
|
|
|
|
0
|
return undef; |
|
1186
|
|
|
|
|
|
|
} |
|
1187
|
|
|
|
|
|
|
|
|
1188
|
|
|
|
|
|
|
# It's fine for some of these values to be undefined the first |
|
1189
|
|
|
|
|
|
|
# time around. However if they were specified as options and |
|
1190
|
|
|
|
|
|
|
# the first header read in doesn't match, or if shares have |
|
1191
|
|
|
|
|
|
|
# inconsistent values then read_ida_header will detect this. |
|
1192
|
9
|
|
|
|
|
23
|
$header_info=sf_read_ida_header($istream,$k,$w,$chunk_start, |
|
1193
|
|
|
|
|
|
|
$chunk_next,$header_size); |
|
1194
|
|
|
|
|
|
|
|
|
1195
|
9
|
50
|
|
|
|
36
|
if ($header_info->{error}) { |
|
1196
|
0
|
|
|
|
|
0
|
carp $header_info->{error_message}; |
|
1197
|
0
|
|
|
|
|
0
|
return undef; |
|
1198
|
|
|
|
|
|
|
} |
|
1199
|
|
|
|
|
|
|
|
|
1200
|
|
|
|
|
|
|
# Store values to check for consistency across all shares |
|
1201
|
9
|
|
|
|
|
16
|
$k = $header_info->{k}; |
|
1202
|
9
|
|
|
|
|
12
|
$w = $header_info->{w}; |
|
1203
|
9
|
|
|
|
|
12
|
$header_size = $header_info->{header_size}; |
|
1204
|
9
|
|
|
|
|
11
|
$chunk_start = $header_info->{chunk_start}; |
|
1205
|
9
|
|
|
|
|
11
|
$chunk_next = $header_info->{chunk_next}; |
|
1206
|
|
|
|
|
|
|
|
|
1207
|
9
|
50
|
|
|
|
17
|
if (++$nshares <= $k) { |
|
1208
|
9
|
50
|
|
|
|
17
|
if ($header_info->{opt_transform}) { |
|
1209
|
9
|
50
|
|
|
|
22
|
if (defined($mat)) { |
|
|
|
50
|
|
|
|
|
|
|
1210
|
0
|
|
|
|
|
0
|
carp "Ignoring file transform data (overriden by matrix option)"; |
|
1211
|
|
|
|
|
|
|
} elsif (defined($key)) { |
|
1212
|
0
|
|
|
|
|
0
|
carp "Ignoring file transform data (overriden by key option)"; |
|
1213
|
|
|
|
|
|
|
} else { |
|
1214
|
|
|
|
|
|
|
#warn "Adding new transform row: [" . (join ", ", map |
|
1215
|
|
|
|
|
|
|
# {sprintf("%02x",$_) } @{ |
|
1216
|
|
|
|
|
|
|
# $header_info->{transform} |
|
1217
|
|
|
|
|
|
|
# }) . "]\n"; |
|
1218
|
9
|
|
|
|
|
16
|
push @matrix, $header_info->{transform}; |
|
1219
|
|
|
|
|
|
|
} |
|
1220
|
|
|
|
|
|
|
} else { |
|
1221
|
0
|
0
|
0
|
|
|
0
|
unless (defined ($mat) or defined($key)) { |
|
1222
|
0
|
|
|
|
|
0
|
carp "Share file contains no transform data and no " . |
|
1223
|
|
|
|
|
|
|
"key/matrix options were supplied."; |
|
1224
|
0
|
|
|
|
|
0
|
return undef; |
|
1225
|
|
|
|
|
|
|
} |
|
1226
|
|
|
|
|
|
|
} |
|
1227
|
|
|
|
|
|
|
} else { |
|
1228
|
0
|
|
|
|
|
0
|
carp "Redundant share(s) detected and ignored"; |
|
1229
|
0
|
|
|
|
|
0
|
last; |
|
1230
|
|
|
|
|
|
|
} |
|
1231
|
|
|
|
|
|
|
|
|
1232
|
|
|
|
|
|
|
#warn "Filler to skip $header_size bytes\n"; |
|
1233
|
9
|
|
|
|
|
38
|
push @$fillers, fill_from_file($infile,$k * $w, $header_size); |
|
1234
|
|
|
|
|
|
|
} |
|
1235
|
|
|
|
|
|
|
|
|
1236
|
|
|
|
|
|
|
# Now that the header has been read in and all the streams agree on |
|
1237
|
|
|
|
|
|
|
# $k and $w, we proceed to build the inverse matrix unless we've |
|
1238
|
|
|
|
|
|
|
# been supplied with a key or (pre-inverted) matrix. |
|
1239
|
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
# first make sure we have k valid shares to combine |
|
1241
|
3
|
50
|
|
|
|
11
|
unless ($nshares >= $k) { |
|
1242
|
0
|
|
|
|
|
0
|
carp "Wrong number of shares to combine (have $nshares, want $k)"; |
|
1243
|
0
|
|
|
|
|
0
|
return undef; |
|
1244
|
|
|
|
|
|
|
} |
|
1245
|
|
|
|
|
|
|
|
|
1246
|
3
|
50
|
33
|
|
|
17
|
unless (defined($key) or defined($mat)) { |
|
1247
|
|
|
|
|
|
|
#warn "Trying to create combine matrix with k=$k, w=$w\n"; |
|
1248
|
3
|
|
|
|
|
26
|
$mat=Math::FastGF2::Matrix->new( |
|
1249
|
|
|
|
|
|
|
rows => $k, |
|
1250
|
|
|
|
|
|
|
cols => $k, |
|
1251
|
|
|
|
|
|
|
width => $w, |
|
1252
|
|
|
|
|
|
|
org => "rowwise", |
|
1253
|
|
|
|
|
|
|
); |
|
1254
|
3
|
|
|
|
|
99
|
my @vals=(); |
|
1255
|
3
|
|
|
|
|
7
|
map { push @vals, @$_ } @matrix; |
|
|
9
|
|
|
|
|
18
|
|
|
1256
|
|
|
|
|
|
|
#warn "matrix is [" . (join ", ", map |
|
1257
|
|
|
|
|
|
|
# {sprintf("%02x",$_) } @vals) . "] (" . |
|
1258
|
|
|
|
|
|
|
# scalar(@vals) . " values)\n"; |
|
1259
|
3
|
|
|
|
|
16
|
$mat->setvals(0,0, \@vals, $inorder); |
|
1260
|
3
|
|
|
|
|
115
|
$mat=$mat->invert(); |
|
1261
|
3
|
50
|
|
|
|
819
|
unless (defined($mat)) { |
|
1262
|
0
|
|
|
|
|
0
|
carp "Failed to invert matrix!"; |
|
1263
|
0
|
|
|
|
|
0
|
return undef; |
|
1264
|
|
|
|
|
|
|
} |
|
1265
|
|
|
|
|
|
|
#@vals=$mat->getvals(0,0,$k * $k); |
|
1266
|
|
|
|
|
|
|
#warn "inverse is [" . (join ", ", map |
|
1267
|
|
|
|
|
|
|
# {sprintf("%02x",$_) } @vals) . "] (" . |
|
1268
|
|
|
|
|
|
|
# scalar(@vals) . " values)\n"; |
|
1269
|
|
|
|
|
|
|
|
|
1270
|
|
|
|
|
|
|
} |
|
1271
|
|
|
|
|
|
|
|
|
1272
|
3
|
|
|
|
|
6
|
$bytes=$chunk_next - $chunk_start; |
|
1273
|
3
|
50
|
|
|
|
11
|
if ($bytes % ($k * $w)) { |
|
1274
|
0
|
0
|
|
|
|
0
|
unless ($header_info->{"opt_final"}) { |
|
1275
|
0
|
|
|
|
|
0
|
carp "Invalid: non-final share is not a multiple of quorum x width"; |
|
1276
|
0
|
|
|
|
|
0
|
return undef; |
|
1277
|
|
|
|
|
|
|
} |
|
1278
|
0
|
|
|
|
|
0
|
$bytes += (($k * $w) - $bytes % ($k * $w)); |
|
1279
|
|
|
|
|
|
|
} |
|
1280
|
|
|
|
|
|
|
|
|
1281
|
|
|
|
|
|
|
# we leave creating/opening the output file until relatively late |
|
1282
|
|
|
|
|
|
|
# since we need to know what offset to seek to in it, and we only |
|
1283
|
|
|
|
|
|
|
# know that when we've examined the sharefile headers |
|
1284
|
3
|
|
|
|
|
15
|
my $emptier=empty_to_file($outfile,undef,$chunk_start); |
|
1285
|
|
|
|
|
|
|
|
|
1286
|
|
|
|
|
|
|
# Need to update %o before calling ida_combine |
|
1287
|
3
|
|
|
|
|
10
|
$o{"emptier"} = $emptier; # leave error-checking to ida_combine |
|
1288
|
3
|
|
|
|
|
6
|
$o{"fillers"} = $fillers; |
|
1289
|
3
|
50
|
|
|
|
11
|
$o{"matrix"} = $mat unless (defined($key)); |
|
1290
|
3
|
|
|
|
|
6
|
$o{"quorum"} = $k; |
|
1291
|
3
|
|
|
|
|
5
|
$o{"width"} = $w; |
|
1292
|
3
|
|
|
|
|
5
|
$o{"bytes"} = $bytes; |
|
1293
|
|
|
|
|
|
|
|
|
1294
|
3
|
|
|
|
|
24
|
my $output_bytes=ida_combine(%o); |
|
1295
|
|
|
|
|
|
|
|
|
1296
|
3
|
50
|
|
|
|
15
|
return undef unless defined($output_bytes); |
|
1297
|
|
|
|
|
|
|
|
|
1298
|
3
|
50
|
|
|
|
10
|
if ($header_info->{opt_final}) { |
|
1299
|
|
|
|
|
|
|
#warn "Truncating output file to $header_info->{chunk_next} bytes\n"; |
|
1300
|
3
|
|
|
|
|
128
|
truncate $outfile, $header_info->{chunk_next}; |
|
1301
|
|
|
|
|
|
|
} |
|
1302
|
|
|
|
|
|
|
|
|
1303
|
3
|
|
|
|
|
185
|
return $output_bytes; |
|
1304
|
|
|
|
|
|
|
} |
|
1305
|
|
|
|
|
|
|
|
|
1306
|
|
|
|
|
|
|
1; |
|
1307
|
|
|
|
|
|
|
|
|
1308
|
|
|
|
|
|
|
__END__ |