1#**************************************************************
2#
3#  Licensed to the Apache Software Foundation (ASF) under one
4#  or more contributor license agreements.  See the NOTICE file
5#  distributed with this work for additional information
6#  regarding copyright ownership.  The ASF licenses this file
7#  to you under the Apache License, Version 2.0 (the
8#  "License"); you may not use this file except in compliance
9#  with the License.  You may obtain a copy of the License at
10#
11#    http://www.apache.org/licenses/LICENSE-2.0
12#
13#  Unless required by applicable law or agreed to in writing,
14#  software distributed under the License is distributed on an
15#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16#  KIND, either express or implied.  See the License for the
17#  specific language governing permissions and limitations
18#  under the License.
19#
20#**************************************************************
21
22package installer::patch::FileOperations;
23
24use File::Basename;
25use File::Copy;
26use IO::Compress::Bzip2;
27use IO::Uncompress::Bunzip2;
28
29my $CompressionMethod = "bzip2";
30
31
32=head1 NAME
33
34    package installer::patch::FileOperations - Class for collecting, checking and executing file operations.
35
36=cut
37
38
39sub new ($)
40{
41    my ($class) = (@_);
42
43    my $self = {
44        'operations' => []
45    };
46    bless($self, $class);
47
48    return $self;
49}
50
51
52
53
54sub AddCopyOperation ($$$)
55{
56    my ($self, $source_name, $target_name) = @_;
57
58    push
59        @{$self->{'operations'}},
60        [
61            'copy',
62            $source_name,
63            $target_name
64        ];
65}
66
67
68
69
70sub AddMakeDirectoryOperation ($$)
71{
72    my ($self, $path) = @_;
73
74    push
75        @{$self->{'operations'}},
76        [
77            'mkdir',
78            $path
79        ];
80}
81
82
83
84
85sub AddCompressOperation ($$)
86{
87    my ($self, $filename) = @_;
88
89    push
90        @{$self->{'operations'}},
91        [
92            'compress',
93            $filename
94        ];
95}
96
97
98
99
100sub AddUncompressOperation ($$$)
101{
102    my ($self, $source_name, $target_name) = @_;
103
104    push
105        @{$self->{'operations'}},
106        [
107            'uncompress',
108            $source_name,
109            $target_name
110        ];
111}
112
113
114
115
116sub Check ($)
117{
118    my ($self) = @_;
119
120    # Keep track of which directories or files would be created to check if
121    # operations that depend on these files will succeed.
122    my %files = ();
123    my %directories = ();
124
125    my @error_messages = ();
126    foreach my $operation (@{$self->{'operations'}})
127    {
128        my $command = $operation->[0];
129
130        if ($command eq "copy")
131        {
132            my ($source_name, $destination_name) = ($operation->[1], $operation->[2]);
133            if ( ! -f $source_name)
134            {
135                push @error_messages, sprintf("%s is not a regular file and can not be copied", $source_name);
136            }
137            my $destination_path = dirname($destination_name);
138            if ( ! -d $destination_path && ! defined $directories{$destination_path})
139            {
140                push @error_messages, sprintf("destination path %s does not exist", $destination_path);
141            }
142            if ( -f $destination_name)
143            {
144                # The destination file already exists. We have to overwrite it.
145                if ( ! -w $destination_name)
146                {
147                    push @error_messges, sprintf("destination file %s exists but can not be overwritten", $destination_name);
148                }
149            }
150            $files{$destination_name} = 1;
151        }
152        elsif ($command eq "mkdir")
153        {
154            my $path = $operation->[1];
155            if ( -d $path)
156            {
157                # Directory already exists.  That is OK, the mkdir command will be silently ignored.
158            }
159            else
160            {
161                $directories{$path} = 1;
162            }
163        }
164        elsif ($command eq "compress")
165        {
166            my $filename = $operation->[1];
167            if ( ! -f $filename && ! defined $files{$filename})
168            {
169                # File does not exist and will not be created by an earlier operation.
170                push @error_messages, sprintf("file %s does not exist and can not be compressed", $filename);
171            }
172        }
173        elsif ($command eq "uncompress")
174        {
175            my ($source_filename, $destination_filename) = ($operation->[1], $operation->[2]);
176            if ($CompressionMethod eq "bzip2")
177            {
178                $source_filename .= ".bz2";
179            }
180            if ( ! -f $source_filename && ! defined $files{$source_filename})
181            {
182                # File does not exist and will not be created by an earlier operation.
183                push @error_messages, sprintf("file %s does not exist and can not be decompressed", $source_filename);
184            }
185            if ( -f $destination_filename && ! -w $destination_filename)
186            {
187                # Destination file already exists but can not be replaced.
188                push @error_messages, sprintf("compress destination file %s exists but can not be replaced", $destination_filename);
189            }
190        }
191        else
192        {
193            push @error_messages, sprintf("unknown operation %s", $command);
194        }
195    }
196
197    return @error_messages;
198}
199
200
201
202
203sub CheckAndExecute ($)
204{
205    my ($self) = @_;
206
207    my @error_messages = $self->Check();
208    if (scalar @error_messages > 0)
209    {
210        $installer::logger::Lang->printf("can not execute all operations:\n");
211        for my $message (@error_messages)
212        {
213            $installer::logger::Lang->printf("ERROR: %s\n", $message);
214        }
215        return 0;
216    }
217    else
218    {
219        return $self->Execute();
220    }
221}
222
223
224
225
226sub Execute ($)
227{
228    my ($self) = @_;
229
230    foreach my $operation (@{$self->{'operations'}})
231    {
232        my $command = $operation->[0];
233
234        if ($command eq "copy")
235        {
236            my ($source_name, $destination_name) = ($operation->[1], $operation->[2]);
237            $installer::logger::Lang->printf("copy from %s\n    to %s\n", $source_name, $destination_name);
238            if ( ! $DryRun)
239            {
240                my $result = copy($source_name, $destination_name);
241                if ( ! $result)
242                {
243                    $installer::logger::Lang->printf("ERROR: copying from %s to %s failed",
244                        $source_name, $destination_name);
245                }
246            }
247        }
248        elsif ($command eq "mkdir")
249        {
250            my $path = $operation->[1];
251            if ( -d $path)
252            {
253                # Path exists already. Do nothing.
254            }
255            else
256            {
257                $installer::logger::Lang->printf("creating directory %s\n", $path);
258                if ( ! $DryRun)
259                {
260                    if (File::Path::make_path($path, {'mode' => 0775}) == 0)
261                    {
262                        $installer::logger::Lang->printf("could not create directory %s\n", $path);
263                    }
264                }
265            }
266        }
267        elsif ($command eq "compress")
268        {
269            my $filename = $operation->[1];
270            $installer::logger::Lang->printf("compressing %s\n", $filename);
271            if ( ! $DryRun)
272            {
273                my $result = 0;
274                if ($CompressionMethod eq "bzip2")
275                {
276                    $result = IO::Compress::Bzip2::bzip2($filename => $filename.".bz2");
277                }
278                if ($result == 0)
279                {
280                    $installer::logger::Lang->printf("ERROR: could not compress %s\n", $filename);
281                }
282                else
283                {
284                    unlink($filename);
285                }
286            }
287        }
288        elsif ($command eq "uncompress")
289        {
290            my ($source_name, $destination_name) = ($operation->[1], $operation->[2]);
291            if ($CompressionMethod eq "bzip2")
292            {
293                $source_name .= ".bz2";
294            }
295            $installer::logger::Lang->printf("uncompressing %s to %s\n", $source_name, $destination_name);
296
297            my $destination_base_name = basename($destination_name);
298
299            if ( ! $DryRun)
300            {
301                my $result = 0;
302                if ($CompressionMethod eq "bzip2")
303                {
304                    $result = IO::Uncompress::Bunzip2::bunzip2($source_name => $destination_name);
305                }
306                if ($result == 0)
307                {
308                    $installer::logger::Lang->printf("ERROR: failed to extract content of '%s' from '%s'\n",
309                        $destination_name, $source_name);
310                    return 0;
311                }
312            }
313        }
314
315        else
316        {
317            die "unknown operation $command\n";
318        }
319    }
320
321    return 1;
322}
323
324
325
326sub GetOperationCount ($)
327{
328    my ($self) = @_;
329    return scalar @{$self->{'operations'}};
330}
331
332
3331;
334