"Fossies" - the Fresh Open Source Software Archive 
Member "zutils-1.10/zutils.cc" (5 Jan 2021, 8896 Bytes) of package /linux/privat/zutils-1.10.tar.lz:
1 /* Zutils - Utilities dealing with compressed files
2 Copyright (C) 2009-2021 Antonio Diaz Diaz.
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 #define _FILE_OFFSET_BITS 64
19
20 #include <cerrno>
21 #include <csignal>
22 #include <cstdio>
23 #include <cstdlib>
24 #include <cstring>
25 #include <string>
26 #include <vector>
27 #include <stdint.h>
28 #include <unistd.h>
29 #include <sys/wait.h>
30
31 #include "rc.h"
32 #include "zutils.h"
33
34
35 namespace {
36
37 inline bool isvalid_ds( const uint8_t ds ) // lzip valid dictionary_size
38 {
39 enum { min_dictionary_size = 1 << 12,
40 max_dictionary_size = 1 << 29 };
41 unsigned dictionary_size = ( 1 << ( ds & 0x1F ) );
42 if( dictionary_size > min_dictionary_size )
43 dictionary_size -= ( dictionary_size / 16 ) * ( ( ds >> 5 ) & 7 );
44 return ( dictionary_size >= min_dictionary_size &&
45 dictionary_size <= max_dictionary_size );
46 }
47
48
49 /* Returns -1 if child not terminated, 2 in case of error, or exit status of
50 child process 'pid'.
51 */
52 int child_status( const pid_t pid, const char * const name )
53 {
54 int status;
55 while( true )
56 {
57 const int tmp = waitpid( pid, &status, WNOHANG );
58 if( tmp == -1 && errno != EINTR )
59 {
60 if( verbosity >= 0 )
61 std::fprintf( stderr, "%s: Error checking status of '%s': %s\n",
62 program_name, name, std::strerror( errno ) );
63 _exit( 2 );
64 }
65 if( tmp == 0 ) return -1; // child not terminated
66 if( tmp == pid ) break; // child terminated
67 }
68 if( WIFEXITED( status ) ) return WEXITSTATUS( status );
69 if( WIFSIGNALED( status ) && WTERMSIG( status ) == SIGPIPE ) return 0;
70 return 2;
71 }
72
73 } // end namespace
74
75
76 /* Returns the number of bytes really read.
77 If (returned value < size) and (errno == 0), means EOF was reached.
78 */
79 int readblock( const int fd, uint8_t * const buf, const int size )
80 {
81 int sz = 0;
82 errno = 0;
83 while( sz < size )
84 {
85 const int n = read( fd, buf + sz, size - sz );
86 if( n > 0 ) sz += n;
87 else if( n == 0 ) break; // EOF
88 else if( errno != EINTR ) break;
89 errno = 0;
90 }
91 return sz;
92 }
93
94
95 /* Returns the number of bytes really written.
96 If (returned value < size), it is always an error.
97 */
98 int writeblock( const int fd, const uint8_t * const buf, const int size )
99 {
100 int sz = 0;
101 errno = 0;
102 while( sz < size )
103 {
104 const int n = write( fd, buf + sz, size - sz );
105 if( n > 0 ) sz += n;
106 else if( n < 0 && errno != EINTR ) break;
107 errno = 0;
108 }
109 return sz;
110 }
111
112
113 // Empty filename means stdin.
114 //
115 bool feed_data( const std::string & filename, const int infd, const int outfd,
116 const uint8_t * magic_data, const int magic_size )
117 {
118 if( magic_size && writeblock( outfd, magic_data, magic_size ) != magic_size )
119 { show_error( "Write error", errno ); return false; }
120 enum { buffer_size = 4096 };
121 uint8_t buffer[buffer_size];
122 while( true )
123 {
124 const int size = readblock( infd, buffer, buffer_size );
125 if( size != buffer_size && errno )
126 { const char * const name = filename.empty() ? "-" : filename.c_str();
127 show_file_error( name, "Read error", errno ); return false; }
128 if( size > 0 && writeblock( outfd, buffer, size ) != size )
129 { show_error( "Write error", errno ); return false; }
130 if( size < buffer_size ) break;
131 }
132 return true;
133 }
134
135
136 bool good_status( const Children & children, const bool finished )
137 {
138 bool error = false;
139 for( int i = 0; i < 2; ++i )
140 {
141 const pid_t pid = children.pid[i];
142 if( pid )
143 {
144 const char * const name =
145 ( i == 0 ) ? "data feeder" : children.compressor_name;
146 // even if compressor finished, trailing data may remain in data feeder
147 if( i == 0 || !finished )
148 {
149 const int tmp = child_status( pid, name );
150 if( tmp < 0 ) // child not terminated
151 { kill( pid, SIGTERM ); wait_for_child( pid, name ); }
152 else if( tmp != 0 ) error = true; // child status != 0
153 }
154 else
155 if( wait_for_child( pid, name ) != 0 ) error = true;
156 }
157 }
158 return !error;
159 }
160
161
162 bool set_data_feeder( const std::string & filename, int * const infdp,
163 Children & children, int format_index )
164 {
165 uint8_t magic_data[magic_buf_size];
166 int magic_size = 0;
167 if( format_index < 0 )
168 format_index = test_format( *infdp, magic_data, &magic_size );
169 children.compressor_name = get_compressor_name( format_index );
170
171 if( children.compressor_name ) // compressed
172 {
173 int fda[2]; // pipe from feeder
174 int fda2[2]; // pipe from compressor
175 if( pipe( fda ) < 0 || pipe( fda2 ) < 0 )
176 { show_error( "Can't create pipe", errno ); return false; }
177 const int old_infd = *infdp;
178 *infdp = fda2[0];
179 const pid_t pid = fork();
180 if( pid == 0 ) // child 1 (compressor feeder)
181 {
182 if( close( fda[0] ) != 0 ||
183 close( fda2[0] ) != 0 || close( fda2[1] ) != 0 ||
184 !feed_data( filename, old_infd, fda[1], magic_data, magic_size ) )
185 _exit( 2 );
186 if( close( fda[1] ) != 0 )
187 { show_close_error(); _exit( 2 ); }
188 _exit( 0 );
189 }
190 if( pid < 0 ) // parent
191 { show_fork_error( "data feeder" ); return false; }
192
193 const pid_t pid2 = fork();
194 if( pid2 == 0 ) // child 2 (compressor)
195 {
196 if( dup2( fda[0], STDIN_FILENO ) >= 0 &&
197 dup2( fda2[1], STDOUT_FILENO ) >= 0 &&
198 close( fda[0] ) == 0 && close( fda[1] ) == 0 &&
199 close( fda2[0] ) == 0 && close( fda2[1] ) == 0 )
200 {
201 const std::vector< std::string > & compressor_args =
202 get_compressor_args( format_index );
203 const int size = compressor_args.size();
204 const char ** const argv = new const char *[size+3];
205 argv[0] = children.compressor_name;
206 for( int i = 0; i < size; ++i )
207 argv[i+1] = compressor_args[i].c_str();
208 argv[size+1] = ( verbosity >= 0 ) ? "-d" : "-dq";
209 argv[size+2] = 0;
210 execvp( argv[0], (char **)argv );
211 }
212 show_exec_error( children.compressor_name );
213 _exit( 2 );
214 }
215 if( pid2 < 0 ) // parent
216 { show_fork_error( children.compressor_name ); return false; }
217
218 close( fda[0] ); close( fda[1] ); close( fda2[1] );
219 children.pid[0] = pid;
220 children.pid[1] = pid2;
221 }
222 else // uncompressed
223 {
224 int fda[2]; // pipe from feeder
225 if( pipe( fda ) < 0 )
226 { show_error( "Can't create pipe", errno ); return false; }
227 const int old_infd = *infdp;
228 *infdp = fda[0];
229 const pid_t pid = fork();
230 if( pid == 0 ) // child (feeder)
231 {
232 if( close( fda[0] ) != 0 ||
233 !feed_data( filename, old_infd, fda[1], magic_data, magic_size ) )
234 _exit( 2 );
235 if( close( fda[1] ) != 0 )
236 { show_close_error(); _exit( 2 ); }
237 _exit( 0 );
238 }
239 if( pid < 0 ) // parent
240 { show_fork_error( "data feeder" ); return false; }
241 close( fda[1] );
242 children.pid[0] = pid;
243 children.pid[1] = 0;
244 }
245 return true;
246 }
247
248
249 // Returns format index or -1 if uncompressed
250 //
251 int test_format( const int infd, uint8_t magic_data[],
252 int * const magic_sizep )
253 {
254 enum { bzip2_magic_size = 3,
255 gzip_magic_size = 2,
256 lzip_magic_size = 5,
257 xz_magic_size = 5 };
258 const uint8_t bzip2_magic[bzip2_magic_size] =
259 { 0x42, 0x5A, 0x68 }; // "BZh"
260 const uint8_t gzip_magic[gzip_magic_size] =
261 { 0x1F, 0x8B };
262 const uint8_t lzip_magic[lzip_magic_size] =
263 { 0x4C, 0x5A, 0x49, 0x50, 0x01 }; // "LZIP\001"
264 const uint8_t xz_magic[xz_magic_size] =
265 { 0xFD, 0x37, 0x7A, 0x58, 0x5A }; // 0xFD, "7zXZ"
266
267 *magic_sizep = readblock( infd, magic_data, magic_buf_size );
268 if( *magic_sizep == magic_buf_size )
269 {
270 if( std::memcmp( magic_data, bzip2_magic, bzip2_magic_size ) == 0 &&
271 magic_data[3] >= '1' && magic_data[3] <= '9' &&
272 std::memcmp( magic_data + 4, "1AY&SY", 6 ) == 0 )
273 return fmt_bz2;
274 if( std::memcmp( magic_data, gzip_magic, gzip_magic_size ) == 0 )
275 return fmt_gz;
276 if( std::memcmp( magic_data, lzip_magic, lzip_magic_size ) == 0 &&
277 isvalid_ds( magic_data[lzip_magic_size] ) )
278 return fmt_lz;
279 if( std::memcmp( magic_data, xz_magic, xz_magic_size ) == 0 )
280 return fmt_xz;
281 }
282 return -1;
283 }