2 * Copyright (c) 2007 Joerg Sonnenberger
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 #include "archive_platform.h"
28 __FBSDID("$FreeBSD: src/lib/libarchive/archive_write_set_compression_program.c,v 1.1 2007/05/29 01:00:19 kientzle Exp $");
30 /* This capability is only available on POSIX systems. */
31 #if !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL)
34 * On non-Posix systems, allow the program to build, but choke if
35 * this function is actually invoked.
38 archive_write_set_compression_program(struct archive
*_a
, const char *cmd
)
40 archive_set_error(_a
, -1,
41 "External compression programs not supported on this platform");
42 return (ARCHIVE_FATAL
);
47 #ifdef HAVE_SYS_WAIT_H
48 # include <sys/wait.h>
64 #include "archive_private.h"
65 #include "archive_write_private.h"
67 #include "filter_fork.h"
72 int child_stdin
, child_stdout
;
75 size_t child_buf_len
, child_buf_avail
;
78 static int archive_compressor_program_finish(struct archive_write
*);
79 static int archive_compressor_program_init(struct archive_write
*);
80 static int archive_compressor_program_write(struct archive_write
*,
81 const void *, size_t);
84 * Allocate, initialize and return a archive object.
87 archive_write_set_compression_program(struct archive
*_a
, const char *cmd
)
89 struct archive_write
*a
= (struct archive_write
*)_a
;
90 __archive_check_magic(&a
->archive
, ARCHIVE_WRITE_MAGIC
,
91 ARCHIVE_STATE_NEW
, "archive_write_set_compression_program");
92 a
->compressor
.init
= &archive_compressor_program_init
;
93 a
->compressor
.config
= strdup(cmd
);
101 archive_compressor_program_init(struct archive_write
*a
)
104 struct private_data
*state
;
105 static const char *prefix
= "Program: ";
106 char *cmd
= a
->compressor
.config
;
108 if (a
->client_opener
!= NULL
) {
109 ret
= (a
->client_opener
)(&a
->archive
, a
->client_data
);
110 if (ret
!= ARCHIVE_OK
)
114 state
= (struct private_data
*)malloc(sizeof(*state
));
116 archive_set_error(&a
->archive
, ENOMEM
,
117 "Can't allocate data for compression");
118 return (ARCHIVE_FATAL
);
120 memset(state
, 0, sizeof(*state
));
122 a
->archive
.compression_code
= ARCHIVE_COMPRESSION_PROGRAM
;
123 state
->description
= (char *)malloc(strlen(prefix
) + strlen(cmd
) + 1);
124 strcpy(state
->description
, prefix
);
125 strcat(state
->description
, cmd
);
126 a
->archive
.compression_name
= state
->description
;
128 state
->child_buf_len
= a
->bytes_per_block
;
129 state
->child_buf_avail
= 0;
130 state
->child_buf
= malloc(state
->child_buf_len
);
132 if (state
->child_buf
== NULL
) {
133 archive_set_error(&a
->archive
, ENOMEM
,
134 "Can't allocate data for compression buffer");
136 return (ARCHIVE_FATAL
);
139 if ((state
->child
= __archive_create_child(cmd
,
140 &state
->child_stdin
, &state
->child_stdout
)) == -1) {
141 archive_set_error(&a
->archive
, EINVAL
,
142 "Can't initialise filter");
143 free(state
->child_buf
);
145 return (ARCHIVE_FATAL
);
148 a
->compressor
.write
= archive_compressor_program_write
;
149 a
->compressor
.finish
= archive_compressor_program_finish
;
151 a
->compressor
.data
= state
;
156 child_write(struct archive_write
*a
, const char *buf
, size_t buf_len
)
158 struct private_data
*state
= a
->compressor
.data
;
161 if (state
->child_stdin
== -1)
169 ret
= write(state
->child_stdin
, buf
, buf_len
);
170 } while (ret
== -1 && errno
== EINTR
);
175 close(state
->child_stdin
);
176 state
->child_stdin
= -1;
177 fcntl(state
->child_stdout
, F_SETFL
, 0);
180 if (ret
== -1 && errno
!= EAGAIN
)
184 ret
= read(state
->child_stdout
,
185 state
->child_buf
+ state
->child_buf_avail
,
186 state
->child_buf_len
- state
->child_buf_avail
);
187 } while (ret
== -1 && errno
== EINTR
);
189 if (ret
== 0 || (ret
== -1 && errno
== EPIPE
)) {
190 close(state
->child_stdout
);
191 state
->child_stdout
= -1;
192 fcntl(state
->child_stdin
, F_SETFL
, 0);
195 if (ret
== -1 && errno
== EAGAIN
) {
196 __archive_check_child(state
->child_stdin
, state
->child_stdout
);
202 state
->child_buf_avail
+= ret
;
204 ret
= (a
->client_writer
)(&a
->archive
, a
->client_data
,
205 state
->child_buf
, state
->child_buf_avail
);
209 if ((size_t)ret
< state
->child_buf_avail
) {
210 memmove(state
->child_buf
, state
->child_buf
+ ret
,
211 state
->child_buf_avail
- ret
);
213 state
->child_buf_avail
-= ret
;
214 a
->archive
.raw_position
+= ret
;
219 * Write data to the compressed stream.
222 archive_compressor_program_write(struct archive_write
*a
, const void *buff
,
225 struct private_data
*state
;
229 state
= (struct private_data
*)a
->compressor
.data
;
230 if (a
->client_writer
== NULL
) {
231 archive_set_error(&a
->archive
, ARCHIVE_ERRNO_PROGRAMMER
,
232 "No write callback is registered? "
233 "This is probably an internal programming error.");
234 return (ARCHIVE_FATAL
);
239 ret
= child_write(a
, buf
, length
);
240 if (ret
== -1 || ret
== 0) {
241 archive_set_error(&a
->archive
, EIO
,
242 "Can't write to filter");
243 return (ARCHIVE_FATAL
);
249 a
->archive
.file_position
+= length
;
255 * Finish the compression...
258 archive_compressor_program_finish(struct archive_write
*a
)
261 ssize_t bytes_read
, bytes_written
;
262 struct private_data
*state
;
264 state
= (struct private_data
*)a
->compressor
.data
;
266 if (a
->client_writer
== NULL
) {
267 archive_set_error(&a
->archive
, ARCHIVE_ERRNO_PROGRAMMER
,
268 "No write callback is registered? "
269 "This is probably an internal programming error.");
274 /* XXX pad compressed data. */
276 close(state
->child_stdin
);
277 state
->child_stdin
= -1;
278 fcntl(state
->child_stdout
, F_SETFL
, 0);
282 bytes_read
= read(state
->child_stdout
,
283 state
->child_buf
+ state
->child_buf_avail
,
284 state
->child_buf_len
- state
->child_buf_avail
);
285 } while (bytes_read
== -1 && errno
== EINTR
);
287 if (bytes_read
== 0 || (bytes_read
== -1 && errno
== EPIPE
))
290 if (bytes_read
== -1) {
291 archive_set_error(&a
->archive
, errno
,
292 "Read from filter failed unexpectedly.");
296 state
->child_buf_avail
+= bytes_read
;
298 bytes_written
= (a
->client_writer
)(&a
->archive
, a
->client_data
,
299 state
->child_buf
, state
->child_buf_avail
);
300 if (bytes_written
<= 0) {
304 if ((size_t)bytes_written
< state
->child_buf_avail
) {
305 memmove(state
->child_buf
,
306 state
->child_buf
+ bytes_written
,
307 state
->child_buf_avail
- bytes_written
);
309 state
->child_buf_avail
-= bytes_written
;
310 a
->archive
.raw_position
+= bytes_written
;
313 /* XXX pad final compressed block. */
316 /* Shut down the child. */
317 if (state
->child_stdin
!= -1)
318 close(state
->child_stdin
);
319 if (state
->child_stdout
!= -1)
320 close(state
->child_stdout
);
321 while (waitpid(state
->child
, &status
, 0) == -1 && errno
== EINTR
)
325 archive_set_error(&a
->archive
, EIO
,
326 "Filter exited with failure.");
330 /* Release our configuration data. */
331 free(a
->compressor
.config
);
332 a
->compressor
.config
= NULL
;
334 /* Release our private state data. */
335 free(state
->child_buf
);
336 free(state
->description
);
341 #endif /* !defined(HAVE_PIPE) || !defined(HAVE_VFORK) || !defined(HAVE_FCNTL) */