3 # Proof-of-concept/reference decoder for LBS-format backup snapshots.
5 # This decoder aims to decompress an LBS snapshot. It is not meant to be
6 # particularly efficient, but should be a small and portable tool for doing so
7 # (important for recovering from data loss). It is also meant to serve as a
8 # check on the snapshot tool and data format itself, and serve as documentation
11 # This decoder does not understand TAR archives; it assumes that all segments
12 # in the snapshot have already been decompressed, and that objects are
13 # available simply as files in the filesystem. This simplifies the design.
15 # Copyright (C) 2007 Michael Vrable
20 my $OBJECT_DIR = "."; # Directory where objects are unpacked
22 ############################ CHECKSUM VERIFICATION ############################
23 # A very simple later for verifying checksums. Checksums may be used on object
24 # references directly, and can also be used to verify entire reconstructed
27 # A checksum to verify is given in the form "algorithm=hexdigest". Given such
28 # a string, we can construct a "verifier" object. Bytes can be incrementally
29 # added to the verifier, and at the end a test can be made to see if the
30 # checksum matches. The caller need not know what algorithm is used. However,
31 # at the moment we only support SHA-1 for computing digest (algorith name
36 if ($checksum !~ m/^(\w+)=([0-9a-f]+)$/) {
37 die "Malformed checksum: $checksum";
39 my ($algorithm, $hash) = ($1, $2);
40 if ($algorithm ne 'sha1') {
41 die "Unsupported checksum algorithm: $algorithm";
45 ALGORITHM => $algorithm,
47 DIGESTER => new Digest::SHA1
53 sub verifier_add_bytes {
55 my $digester = $verifier->{DIGESTER};
58 $digester->add($data);
63 my $digester = $verifier->{DIGESTER};
65 my $newhash = $digester->hexdigest();
66 return ($verifier->{HASH} eq $newhash);
69 ################################ OBJECT ACCESS ################################
70 # The base of the decompressor is the object reference layer. See ref.h for a
71 # description of the format for object references. These functions will parse
72 # an object reference, locate the object data from the filesystem, perform any
73 # necessary integrity checks (if a checksum is included), and return the object
76 # First, try to parse the object reference string into constituent pieces.
77 # The format is segment/object(checksum)[range]. Both the checksum and
81 if ($ref_str !~ m/^([-0-9a-f]+)\/([0-9a-f]+)(\(\S+\))?(\[\S+\])?$/) {
82 die "Malformed object reference: $ref_str";
85 my ($segment, $object, $checksum, $range) = ($1, $2, $3, $4);
87 # Next, use the segment/object components to locate and read the object
89 open OBJECT, "<", "$OBJECT_DIR/$segment/$object"
90 or die "Unable to open object: $OBJECT_DIR/$segment/$object";
91 my $contents = join '', <OBJECT>;
94 # If a checksum was specified in the object reference, verify the object
95 # integrity by computing a checksum of the read data and comparing.
97 $checksum =~ m/^\((\S+)\)$/;
98 my $verifier = verifier_create($1);
99 verifier_add_bytes($verifier, $contents);
100 if (!verifier_check($verifier)) {
101 die "Integrity check for object $ref_str failed";
105 # If a range was specified, then only a subset of the bytes of the object
106 # are desired. Extract just the desired bytes.
108 if ($range !~ m/^\[(\d+)\+(\d+)\]$/) {
109 die "Malformed object range: $range";
112 my $object_size = length $contents;
113 my ($start, $length) = ($1 + 0, $2 + 0);
114 if ($start >= $object_size || $start + $length > $object_size) {
115 die "Object range $range falls outside object bounds "
116 . "(actual size $object_size)";
119 $contents = substr $contents, $start, $length;
125 ############################### MAIN ENTRY POINT ##############################
126 my $object = $ARGV[0];
128 #print "Object: $object\n\n";
130 my $contents = load_ref($object);