1#!/usr/bin/perl -w
2# This Source Code Form is subject to the terms of the Mozilla Public
3# License, v. 2.0. If a copy of the MPL was not distributed with this
4# file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
6#
7# This tool extracts a mar file, changes the compression of the files contained
8# by the mar file either from bzip2 to lzma or lzma to bzip2, and then recreates
9# the mar file. The script determines whether the files are compressed with
10# bzip2 or lzma to determine what compression should be used. The permissions
11# of the files will be retained as long as the script is run on a system such
12# as Linux or Mac OS X but not on Windows. The mar file will be created with the
13# same MAR channel and Product version information as the original mar file.
14# If a mar signature is required the converted mar files will need to be
15# re-signed afterwards.
16# Author: Robert Strong
17#
18
19# -----------------------------------------------------------------------------
20# By default just assume that these tools exist in our path
21
22use Getopt::Std;
23use Cwd 'abs_path';
24use File::Basename;
25
26$|++;
27
28my ($MAR, $XZ, $BZIP2, $MAR_OLD_FORMAT, $FILES, $CHANNEL, $VERSION, $REPLACE, $archive, $tmparchive, @marentries, @marfiles);
29
30if (defined($ENV{"MAR"})) {
31    $MAR = $ENV{"MAR"};
32}
33else {
34    $MAR = "mar";
35}
36
37if (defined($ENV{"BZIP2"})) {
38    $BZIP2 = $ENV{"BZIP2"};
39}
40else {
41    $BZIP2 = "bzip2";
42}
43
44if (defined($ENV{"XZ"})) {
45    $XZ = $ENV{"XZ"};
46}
47else {
48    $XZ = "xz";
49}
50
51sub print_usage
52{
53    print "Usage: change_mar_compression.pl [OPTIONS] ARCHIVE\n";
54	print "\n";
55    print "The ARCHIVE will be recreated using either bzip2 for mar file contents that\n";
56	print "are compressed with lzma or lzma for mar file contents that are compressed\n";
57	print "with bzip2. The new mar file that is created will not be signed but all other\n";
58	print "attributes that should be retained will be retained.\n";
59	print "\n";
60    print "Options:\n";
61    print "  -h show this help text\n";
62    print "  -r replace the original mar file with the new mar file\n";
63}
64
65my %opts;
66getopts("hr", \%opts);
67
68if (defined($opts{'h'}) || scalar(@ARGV) != 1) {
69    print_usage();
70    exit 1;
71}
72
73if ($opts{'r'}) {
74    $REPLACE = 1;
75}
76
77$archive = $ARGV[0];
78@marentries = `"$MAR" -T "$archive"`;
79$? && die("Couldn't run \"$MAR\" -t");
80
81system("$MAR -x \"$archive\"") == 0 ||
82  die "Couldn't run $MAR -x";
83
84open(my $testfilename, "updatev3.manifest") or die $!;
85binmode($testfilename);
86read($testfilename, my $bytes, 3);
87if ($bytes eq "BZh") {
88    $MAR_OLD_TO_NEW = 1;
89    print "Converting mar file from bzip2 to lzma compression\n";
90} else {
91    undef $MAR_OLD_TO_NEW;
92    print "Converting mar file from lzma to bzip2 compression\n";
93}
94close $testfilename;
95
96print "\n";
97
98# The channel is the 4th line of the output
99shift @marentries;
100shift @marentries;
101shift @marentries;
102$CHANNEL = substr($marentries[0], 24, -1);
103print "MAR channel name: " . $CHANNEL . "\n";
104
105# The version is the 5th line of the output
106shift @marentries;
107$VERSION = substr($marentries[0], 23, -1);
108print "Product version: " . $VERSION . "\n";
109
110# The file entries start on the 8th line of the output
111shift @marentries;
112shift @marentries;
113shift @marentries;
114
115print "\n";
116# Decompress the extracted files
117foreach (@marentries) {
118    tr/\n\r//d;
119    my @splits = split(/\t/,$_);
120    my $file = $splits[2];
121
122    print "Decompressing: " . $file . "\n";
123    if ($MAR_OLD_TO_NEW) {
124        system("mv \"$file\" \"$file.bz2\"") == 0 ||
125          print "\n" && die "Couldn't mv \"$file\"";
126        system("\"$BZIP2\" -d \"$file.bz2\"") == 0 ||
127          print "\n" && die "Couldn't decompress \"$file\"";
128    }
129    else {
130        system("mv \"$file\" \"$file.xz\"") == 0 ||
131          print "\n" && die "Couldn't mv \"$file\"";
132        system("\"$XZ\" -d \"$file.xz\"") == 0 ||
133          print "\n" && die "Couldn't decompress \"$file\"";
134    }
135}
136print "All files decompressed\n";
137
138print "\n";
139# Compress the files in the requested format
140$FILES = "";
141foreach (@marentries) {
142    tr/\n\r//d;
143    my @splits = split(/\t/,$_);
144    my $mod = $splits[1];
145    my $file = $splits[2];
146
147    print "Compressing: " . $file . "\n";
148    if ($MAR_OLD_TO_NEW) {
149        system("\"$XZ\" --compress --x86 --lzma2 --format=xz --check=crc64 --force --stdout \"$file\" > \"$file.xz\"") == 0 ||
150          die "Couldn't compress \"$file\"";
151        system("mv \"$file.xz\" \"$file\"") == 0 ||
152          die "Couldn't mv \"$file.xz\"";
153    }
154    else {
155        system("\"$BZIP2\" -z9 \"$file\"") == 0 ||
156          die "Couldn't compress \"$file\"";
157        system("mv \"$file.bz2\" \"$file\"") == 0 ||
158          die "Couldn't mv \"$file.bz2\"";
159    }
160    $FILES = $FILES . "\"$file\" ";
161    chmod oct($mod), $file;
162}
163print "All files compressed\n";
164
165my $filesuffix = ".bz";
166if ($MAR_OLD_TO_NEW) {
167    $filesuffix = ".xz";
168}
169$tmparchive = $archive . $filesuffix;
170
171system("$MAR -H $CHANNEL -V $VERSION -c \"$tmparchive\" $FILES") == 0 ||
172  die "Couldn't run $MAR -c";
173
174if ($REPLACE) {
175    print "\n";
176    print "Replacing mar file with the converted mar file\n";
177    unlink $archive;
178    system("mv \"$tmparchive\" \"$archive\"") == 0 ||
179      die "Couldn't mv \"$tmparchive\"";
180}
181
182print "\n";
183print "Removing extracted files\n";
184foreach (@marentries) {
185    tr/\n\r//d;
186    my @splits = split(/\t/,$_);
187    my $file = $splits[2];
188
189    unlink $file;
190    my $dirpath = $file;
191    while (1) {
192        if (index($dirpath, '/') < 0) {
193            last;
194        }
195        $dirpath = substr($dirpath, 0, rindex($dirpath, '/'));
196        rmdir($dirpath);
197        if (-d $dirpath) {
198            last;
199        }
200    }
201}
202
203print "\n";
204if ($MAR_OLD_TO_NEW) {
205    print "Finished converting mar file from bzip2 to lzma compression\n";
206} else {
207    print "Finished converting mar file from lzma to bzip2 compression\n";
208}
209