1 /*
2 * Copyright (c) 2009 - 2011 Thomas Schmitt
3 *
4 * This file is part of the libisofs project; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License version 2
6 * or later as published by the Free Software Foundation.
7 * See COPYING file for details.
8 *
9 * It implements a filter facility which can pipe a IsoStream into an external
10 * process, read its output and forward it as IsoStream output to an IsoFile.
11 * The external processes get started according to an IsoExternalFilterCommand
12 * which is described in libisofs.h.
13 *
14 */
15
16 #ifdef HAVE_CONFIG_H
17 #include "../config.h"
18 #endif
19
20 #include "../libisofs.h"
21 #include "../filter.h"
22 #include "../fsource.h"
23 #include "../stream.h"
24
25 #include <sys/types.h>
26 #include <sys/time.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29 #include <signal.h>
30 #include <stdio.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <string.h>
34
35 #ifdef Libisofs_external_filters_selecT
36 #include <sys/select.h>
37 #endif
38
39 /*
40 * A filter that starts an external process and uses its stdin and stdout
41 * for classical pipe filtering.
42 */
43
44 /* IMPORTANT: Any change must be reflected by extf_clone_stream() */
45 /*
46 * Individual runtime properties exist only as long as the stream is opened.
47 */
48 typedef struct
49 {
50 int send_fd;
51 int recv_fd;
52 pid_t pid;
53 off_t in_counter;
54 int in_eof;
55 off_t out_counter;
56 int out_eof;
57 uint8_t pipebuf[2048]; /* buffers in case of EAGAIN on write() */
58 int pipebuf_fill;
59 } ExternalFilterRuntime;
60
61
62 static
extf_running_new(ExternalFilterRuntime ** running,int send_fd,int recv_fd,pid_t child_pid,int flag)63 int extf_running_new(ExternalFilterRuntime **running, int send_fd, int recv_fd,
64 pid_t child_pid, int flag)
65 {
66 ExternalFilterRuntime *o;
67 *running = o = calloc(sizeof(ExternalFilterRuntime), 1);
68 if (o == NULL) {
69 return ISO_OUT_OF_MEM;
70 }
71 o->send_fd = send_fd;
72 o->recv_fd = recv_fd;
73 o->pid = child_pid;
74 o->in_counter = 0;
75 o->in_eof = 0;
76 o->out_counter = 0;
77 o->out_eof = 0;
78 memset(o->pipebuf, 0, sizeof(o->pipebuf));
79 o->pipebuf_fill = 0;
80 return 1;
81 }
82
83
84 /*
85 * The data payload of an individual IsoStream from External Filter
86 */
87 typedef struct
88 {
89 ino_t id;
90
91 IsoStream *orig;
92
93 IsoExternalFilterCommand *cmd;
94
95 off_t size; /* -1 means that the size is unknown yet */
96
97 ExternalFilterRuntime *running; /* is non-NULL when open */
98
99 } ExternalFilterStreamData;
100
101
102 /* Each individual ExternalFilterStreamData needs a unique id number. */
103 /* >>> This is very suboptimal:
104 The counter can rollover.
105 */
106 static ino_t extf_ino_id = 0;
107
108
109 /* <<< */
110 static int print_fd= 0;
111
112
113 /*
114 * Methods for the IsoStreamIface of an External Filter object.
115 */
116
117
118 /*
119 * @param flag bit0= original stream is not open
120 */
121 static
extf_stream_close_flag(IsoStream * stream,int flag)122 int extf_stream_close_flag(IsoStream *stream, int flag)
123 {
124 int ret, status;
125 ExternalFilterStreamData *data;
126
127 if (stream == NULL) {
128 return ISO_NULL_POINTER;
129 }
130 data = stream->data;
131
132 if (data->running == NULL) {
133 return 1;
134 }
135
136 /* <<< */
137 if (print_fd) {
138 fprintf(stderr, "libisofs_DEBUG: filter close in = %d , ic= %.f\n",
139 data->running->recv_fd, (double) data->running->in_counter);
140 fprintf(stderr, "libisofs_DEBUG: filter close out = %d , oc= %.f\n",
141 data->running->send_fd, (double) data->running->out_counter);
142 }
143
144 if(data->running->recv_fd != -1)
145 close(data->running->recv_fd);
146 if(data->running->send_fd != -1)
147 close(data->running->send_fd);
148
149 ret = waitpid(data->running->pid, &status, WNOHANG);
150 if (ret == 0 && data->running->pid != 0) {
151 kill(data->running->pid, SIGKILL);
152 waitpid(data->running->pid, &status, 0);
153 }
154 free(data->running);
155 data->running = NULL;
156 if (flag & 1)
157 return 1;
158 return iso_stream_close(data->orig);
159 }
160
161
162 static
extf_stream_close(IsoStream * stream)163 int extf_stream_close(IsoStream *stream)
164 {
165 return extf_stream_close_flag(stream, 0);
166 }
167
168
169 /*
170 * @param flag bit0= do not run .get_size() if size is < 0
171 */
172 static
extf_stream_open_flag(IsoStream * stream,int flag)173 int extf_stream_open_flag(IsoStream *stream, int flag)
174 {
175 ExternalFilterStreamData *data;
176 ExternalFilterRuntime *running = NULL;
177 pid_t child_pid;
178 int send_pipe[2], recv_pipe[2], ret, stream_open = 0;
179
180 send_pipe[0] = send_pipe[1] = recv_pipe[0] = recv_pipe[1] = -1;
181
182 if (stream == NULL) {
183 return ISO_NULL_POINTER;
184 }
185 data = (ExternalFilterStreamData*)stream->data;
186 if (data->running != NULL) {
187 return ISO_FILE_ALREADY_OPENED;
188 }
189 if (data->size < 0 && !(flag & 1)) {
190 /* Do the size determination run now, so that the size gets cached
191 and .get_size() will not fail on an opened stream.
192 */
193 stream->class->get_size(stream);
194 }
195
196 ret = pipe(send_pipe);
197 if (ret == -1) {
198 ret = ISO_OUT_OF_MEM;
199 goto parent_failed;
200 }
201 ret = pipe(recv_pipe);
202 if (ret == -1) {
203 ret = ISO_OUT_OF_MEM;
204 goto parent_failed;
205 }
206
207 child_pid= fork();
208 if (child_pid == -1) {
209 ret = ISO_DATA_SOURCE_FATAL;
210 goto parent_failed;
211 }
212
213 if (child_pid != 0) {
214 /* parent */
215 ret = extf_running_new(&running, send_pipe[1], recv_pipe[0], child_pid,
216 0);
217 if (ret < 0) {
218 goto parent_failed;
219 }
220 data->running = running;
221
222 /* <<< */
223 if (print_fd) {
224 fprintf(stderr, "libisofs_DEBUG: filter parent in = %d\n",
225 data->running->recv_fd);
226 fprintf(stderr, "libisofs_DEBUG: filter parent out = %d\n",
227 data->running->send_fd);
228 }
229
230 /* Give up the child-side pipe ends */
231 close(send_pipe[0]);
232 close(recv_pipe[1]);
233
234 /* Open stream only after forking so that the child does not know
235 the pipe inlets of eventually underlying other filter streams.
236 They would stay open and prevent those underlying filter children
237 from seeing EOF at their input.
238 */
239 ret = iso_stream_open(data->orig);
240
241
242 /* <<< TEST <<<
243 ret= ISO_FILE_READ_ERROR;
244 */
245
246 if (ret < 0) {
247 /* Dispose pipes and child */
248 extf_stream_close_flag(stream, 1);
249 return ret;
250 }
251 stream_open = 1;
252 /* Make filter outlet non-blocking */
253 ret = fcntl(recv_pipe[0], F_GETFL);
254 if (ret != -1) {
255 ret |= O_NONBLOCK;
256 fcntl(recv_pipe[0], F_SETFL, ret);
257 }
258 /* Make filter sink non-blocking */
259 ret = fcntl(send_pipe[1], F_GETFL);
260 if (ret != -1) {
261 ret |= O_NONBLOCK;
262 fcntl(send_pipe[1], F_SETFL, ret);
263 }
264 return 1;
265 }
266
267 /* child */
268
269 /* Give up the parent-side pipe ends */
270 close(send_pipe[1]);
271 close(recv_pipe[0]);
272
273 /* attach pipe ends to stdin and stdout */;
274 close(0);
275 ret = dup2(send_pipe[0], 0);
276 if (ret == -1) {
277 goto child_failed;
278 }
279 close(1);
280 ret = dup2(recv_pipe[1], 1);
281 if (ret == -1) {
282 goto child_failed;
283 }
284
285 /* <<< */
286 if (print_fd) {
287 fprintf(stderr, "libisofs_DEBUG: filter child in = %d\n",
288 send_pipe[0]);
289 fprintf(stderr, "libisofs_DEBUG: filter child out = %d\n",
290 recv_pipe[1]);
291 }
292
293 /* Self conversion into external program */
294 execv(data->cmd->path, data->cmd->argv); /* should never come back */
295
296 child_failed:;
297 fprintf(stderr,"--- execution of external filter command failed:\n");
298 fprintf(stderr," %s\n", data->cmd->path);
299 exit(127);
300
301 parent_failed:;
302
303 /* <<< */
304 if (print_fd) {
305 fprintf(stderr, "libisofs_DEBUG: FAILED : filter parent in = %d\n",
306 recv_pipe[0]);
307 fprintf(stderr, "libisofs_DEBUG: FAILED : filter parent out = %d\n",
308 send_pipe[1]);
309 }
310
311 if (stream_open)
312 iso_stream_close(data->orig);
313 if(send_pipe[0] != -1)
314 close(send_pipe[0]);
315 if(send_pipe[1] != -1)
316 close(send_pipe[1]);
317 if(recv_pipe[0] != -1)
318 close(recv_pipe[0]);
319 if(recv_pipe[1] != -1)
320 close(recv_pipe[1]);
321 return ret;
322 }
323
324
325 static
extf_stream_open(IsoStream * stream)326 int extf_stream_open(IsoStream *stream)
327 {
328 return extf_stream_open_flag(stream, 0);
329 }
330
331
332 #ifdef Libisofs_external_filters_selecT
333
334 /* Performance is weaker than with non-blocking i/o and usleep(). */
335
336 static
extf_wait_for_io(int fd_in,int fd_out,int microsec,int flag)337 int extf_wait_for_io(int fd_in, int fd_out, int microsec, int flag)
338 {
339 struct timeval wt;
340 fd_set rds,wts,exs;
341 int ready, fd_max;
342
343 fd_max = fd_out;
344 if (fd_in > fd_out)
345 fd_max = fd_in;
346
347 FD_ZERO(&rds);
348 FD_ZERO(&wts);
349 FD_ZERO(&exs);
350 if (fd_in >= 0) {
351 FD_SET(fd_in,&rds);
352 FD_SET(fd_in,&exs);
353 }
354 if (fd_out >= 0) {
355 FD_SET(fd_out,&rds);
356 FD_SET(fd_in,&exs);
357 }
358 wt.tv_sec = microsec/1000000;
359 wt.tv_usec = microsec%1000000;
360 ready = select(fd_max + 1, &rds, &wts, &exs, &wt);
361 if (ready <= 0)
362 return 0;
363 if (fd_in >= 0) {
364 if (FD_ISSET(fd_in, &rds))
365 return 1;
366 }
367 if (fd_out >= 0) {
368 if (FD_ISSET(fd_out, &rds))
369 return 2;
370 }
371 if (fd_in >= 0) {
372 if (FD_ISSET(fd_in, &exs))
373 return -1;
374 }
375 if (fd_out >= 0) {
376 if (FD_ISSET(fd_out, &exs))
377 return -2;
378 }
379 return(0);
380 }
381
382 #endif /* Libisofs_external_filters_selecT */
383
384
385 static
extf_stream_read(IsoStream * stream,void * buf,size_t desired)386 int extf_stream_read(IsoStream *stream, void *buf, size_t desired)
387 {
388 int ret, blocking = 0;
389 ExternalFilterStreamData *data;
390 ExternalFilterRuntime *running;
391 size_t fill = 0;
392
393 if (stream == NULL) {
394 return ISO_NULL_POINTER;
395 }
396 data = stream->data;
397 running= data->running;
398 if (running == NULL) {
399 return ISO_FILE_NOT_OPENED;
400 }
401 if (running->out_eof) {
402 return 0;
403 }
404
405 while (1) {
406 if (running->in_eof && !blocking) {
407 /* Make filter outlet blocking */
408 ret = fcntl(running->recv_fd, F_GETFL);
409 if (ret != -1) {
410 ret &= ~O_NONBLOCK;
411 fcntl(running->recv_fd, F_SETFL, ret);
412 }
413 blocking = 1;
414 }
415
416 /* Try to read desired amount from filter */;
417 while (1) {
418 ret = read(running->recv_fd, ((char *) buf) + fill,
419 desired - fill);
420 if (ret < 0) {
421 if (errno == EAGAIN)
422 break;
423 return ISO_FILE_READ_ERROR;
424 }
425 fill += ret;
426 if (ret == 0) {
427 running->out_eof = 1;
428 }
429 if (ret == 0 || fill >= desired) {
430 running->out_counter += fill;
431 return fill;
432 }
433 }
434
435 if (running->in_eof) {
436 usleep(1000); /* just in case it is still non-blocking */
437 continue;
438 }
439 if (running->pipebuf_fill) {
440 ret = running->pipebuf_fill;
441 running->pipebuf_fill = 0;
442 } else {
443 ret = iso_stream_read(data->orig, running->pipebuf,
444 sizeof(running->pipebuf));
445 if (ret > 0)
446 running->in_counter += ret;
447 }
448 if (ret < 0) {
449 running->in_eof = 1;
450 return ret;
451 }
452 if (ret == 0) {
453
454 /* <<< */
455 if (print_fd) {
456 fprintf(stderr,
457 "libisofs_DEBUG: filter close out = %d , ic= %.f\n",
458 running->send_fd, (double) running->in_counter);
459 }
460
461 running->in_eof = 1;
462 close(running->send_fd); /* Tell the filter: it is over */
463 running->send_fd = -1;
464 } else {
465 running->pipebuf_fill = ret;
466 ret = write(running->send_fd, running->pipebuf,
467 running->pipebuf_fill);
468 if (ret == -1) {
469 if (errno == EAGAIN) {
470
471 #ifdef Libisofs_external_filters_selecT
472
473 /* This select() based waiting saves 10 % CPU load but
474 needs 50 % more real time */
475
476 ret = extf_wait_for_io(running->recv_fd, running->send_fd,
477 100000, 0);
478 if (ret < 0)
479 usleep(1000); /* To make sure sufficient laziness */
480
481 #else
482
483 /* No sleeping needs 90 % more CPU and saves 6 % time */
484 usleep(1000); /* go lazy because the filter is slow */
485
486 #endif /* ! Libisofs_external_filters_selecT */
487
488 continue;
489 }
490
491 /* From the view of the caller it _is_ a read error */
492 running->in_eof = 1;
493 return ISO_FILE_READ_ERROR;
494 }
495 running->pipebuf_fill = 0;
496 }
497 }
498 return ISO_FILE_READ_ERROR; /* should never be hit */
499 }
500
501
502 static
extf_stream_get_size(IsoStream * stream)503 off_t extf_stream_get_size(IsoStream *stream)
504 {
505 int ret, ret_close;
506 off_t count = 0;
507 ExternalFilterStreamData *data;
508 char buf[64 * 1024];
509 size_t bufsize = 64 * 1024;
510
511 if (stream == NULL) {
512 return ISO_NULL_POINTER;
513 }
514 data = stream->data;
515
516 if (data->size >= 0) {
517 return data->size;
518 }
519
520 /* Run filter command and count output bytes */
521 ret = extf_stream_open_flag(stream, 1);
522 if (ret < 0) {
523 return ret;
524 }
525 while (1) {
526 ret = extf_stream_read(stream, buf, bufsize);
527 if (ret <= 0)
528 break;
529 count += ret;
530 }
531 ret_close = extf_stream_close(stream);
532 if (ret < 0)
533 return ret;
534 if (ret_close < 0)
535 return ret_close;
536
537 data->size = count;
538 return count;
539 }
540
541
542 static
extf_stream_is_repeatable(IsoStream * stream)543 int extf_stream_is_repeatable(IsoStream *stream)
544 {
545 /* Only repeatable streams are accepted as orig */
546 return 1;
547 }
548
549
550 static
extf_stream_get_id(IsoStream * stream,unsigned int * fs_id,dev_t * dev_id,ino_t * ino_id)551 void extf_stream_get_id(IsoStream *stream, unsigned int *fs_id,
552 dev_t *dev_id, ino_t *ino_id)
553 {
554 ExternalFilterStreamData *data;
555
556 data = stream->data;
557 *fs_id = ISO_FILTER_FS_ID;
558 *dev_id = ISO_FILTER_EXTERNAL_DEV_ID;
559 *ino_id = data->id;
560 }
561
562
563 static
extf_stream_free(IsoStream * stream)564 void extf_stream_free(IsoStream *stream)
565 {
566 ExternalFilterStreamData *data;
567
568 if (stream == NULL) {
569 return;
570 }
571 data = stream->data;
572 if (data->running != NULL) {
573 extf_stream_close(stream);
574 }
575 iso_stream_unref(data->orig);
576 if (data->cmd->refcount > 0)
577 data->cmd->refcount--;
578 free(data);
579 }
580
581
582 static
extf_update_size(IsoStream * stream)583 int extf_update_size(IsoStream *stream)
584 {
585 /* By principle size is determined only once */
586 return 1;
587 }
588
589
590 static
extf_get_input_stream(IsoStream * stream,int flag)591 IsoStream *extf_get_input_stream(IsoStream *stream, int flag)
592 {
593 ExternalFilterStreamData *data;
594
595 if (stream == NULL) {
596 return NULL;
597 }
598 data = stream->data;
599 return data->orig;
600 }
601
602 static
extf_clone_stream(IsoStream * old_stream,IsoStream ** new_stream,int flag)603 int extf_clone_stream(IsoStream *old_stream, IsoStream **new_stream, int flag)
604 {
605 int ret;
606 IsoStream *new_input_stream, *stream;
607 ExternalFilterStreamData *stream_data, *old_stream_data;
608
609 if (flag)
610 return ISO_STREAM_NO_CLONE; /* unknown option required */
611
612 stream_data = calloc(1, sizeof(ExternalFilterStreamData));
613 if (stream_data == NULL)
614 return ISO_OUT_OF_MEM;
615 ret = iso_stream_clone_filter_common(old_stream, &stream,
616 &new_input_stream, 0);
617 if (ret < 0) {
618 free((char *) stream_data);
619 return ret;
620 }
621 old_stream_data = (ExternalFilterStreamData *) old_stream->data;
622 stream_data->id = ++extf_ino_id;
623 stream_data->orig = new_input_stream;
624 stream_data->cmd = old_stream_data->cmd;
625 stream_data->cmd->refcount++;
626 stream_data->size = old_stream_data->size;
627 stream_data->running = NULL;
628 stream->data = stream_data;
629 *new_stream = stream;
630 return ISO_SUCCESS;
631 }
632
633 static
634 int extf_cmp_ino(IsoStream *s1, IsoStream *s2);
635 /* Function is defined after definition of extf_stream_class */
636
637
638 IsoStreamIface extf_stream_class = {
639 4,
640 "extf",
641 extf_stream_open,
642 extf_stream_close,
643 extf_stream_get_size,
644 extf_stream_read,
645 extf_stream_is_repeatable,
646 extf_stream_get_id,
647 extf_stream_free,
648 extf_update_size,
649 extf_get_input_stream,
650 extf_cmp_ino,
651 extf_clone_stream
652 };
653
654
655 static
extf_cmp_ino(IsoStream * s1,IsoStream * s2)656 int extf_cmp_ino(IsoStream *s1, IsoStream *s2)
657 {
658 int i;
659 ExternalFilterStreamData *data1, *data2;
660 IsoExternalFilterCommand *cmd1, *cmd2;
661
662 /* This function may rely on being called by iso_stream_cmp_ino()
663 only with s1, s2 which both point to it as their .cmp_ino() function.
664 It would be a programming error to let any other than extf_stream_class
665 point to extf_cmp_ino(). This fallback endangers transitivity of
666 iso_stream_cmp_ino().
667 */
668 if (s1->class != &extf_stream_class || s2->class != &extf_stream_class)
669 return iso_stream_cmp_ino(s1, s2, 1);
670
671 data1 = (ExternalFilterStreamData*) s1->data;
672 data2 = (ExternalFilterStreamData*) s2->data;
673 cmd1 = data1->cmd;
674 cmd2 = data2->cmd;
675 if (cmd1 != cmd2) {
676 if (strcmp(cmd1->name, cmd2->name) != 0)
677 return strcmp(cmd1->name, cmd2->name);
678 if (strcmp(cmd1->path, cmd2->path) != 0)
679 return strcmp(cmd1->path, cmd2->path);
680 if (cmd1->argc != cmd2->argc)
681 return cmd1->argc < cmd2->argc ? -1 : 1;
682 for (i = 0; i < cmd1->argc; i++) {
683 if (strcmp(cmd1->argv[i], cmd2->argv[i]) != 0)
684 return strcmp(cmd1->argv[i], cmd2->argv[i]);
685 }
686 if (cmd1->behavior != cmd2->behavior)
687 return cmd1->behavior < cmd2->behavior ? -1 : 1;
688 if (strcmp(cmd1->suffix, cmd2->suffix) != 0)
689 return strcmp(cmd1->suffix, cmd2->suffix);
690 }
691
692 /* Both streams apply the same treatment to their input streams */
693 return iso_stream_cmp_ino(data1->orig, data2->orig, 0);
694 }
695
696
697 /* ------------------------------------------------------------------------- */
698
699 static
extf_filter_free(FilterContext * filter)700 void extf_filter_free(FilterContext *filter)
701 {
702 /* no data are allocated */;
703 }
704
705
706 /* To be called by iso_file_add_filter().
707 * The FilterContext input parameter is not furtherly needed for the
708 * emerging IsoStream.
709 */
710 static
extf_filter_get_filter(FilterContext * filter,IsoStream * original,IsoStream ** filtered)711 int extf_filter_get_filter(FilterContext *filter, IsoStream *original,
712 IsoStream **filtered)
713 {
714 IsoStream *str;
715 ExternalFilterStreamData *data;
716 IsoExternalFilterCommand *cmd;
717
718 if (filter == NULL || original == NULL || filtered == NULL) {
719 return ISO_NULL_POINTER;
720 }
721 cmd = (IsoExternalFilterCommand *) filter->data;
722 if (cmd->refcount + 1 <= 0) {
723 return ISO_EXTF_TOO_OFTEN;
724 }
725
726 str = malloc(sizeof(IsoStream));
727 if (str == NULL) {
728 return ISO_OUT_OF_MEM;
729 }
730 data = malloc(sizeof(ExternalFilterStreamData));
731 if (data == NULL) {
732 free(str);
733 return ISO_OUT_OF_MEM;
734 }
735
736
737 /* These data items are not owned by this filter object */
738 data->id = ++extf_ino_id;
739 data->orig = original;
740 data->cmd = cmd;
741 data->size = -1;
742 data->running = NULL;
743
744 /* get reference to the source */
745 iso_stream_ref(data->orig);
746
747 str->refcount = 1;
748 str->data = data;
749 str->class = &extf_stream_class;
750
751 *filtered = str;
752
753 cmd->refcount++;
754 return ISO_SUCCESS;
755 }
756
757
758 /* Produce a parameter object suitable for iso_file_add_filter().
759 * It may be disposed by free() after all those calls are made.
760 *
761 * This is an internal call of libisofs to be used by an API call that
762 * attaches an IsoExternalFilterCommand to one or more IsoFile objects.
763 * See libisofs.h for IsoExternalFilterCommand.
764 */
765 static
extf_create_context(IsoExternalFilterCommand * cmd,FilterContext ** filter,int flag)766 int extf_create_context(IsoExternalFilterCommand *cmd,
767 FilterContext **filter, int flag)
768 {
769 FilterContext *f;
770
771 *filter = f = calloc(1, sizeof(FilterContext));
772 if (f == NULL) {
773 return ISO_OUT_OF_MEM;
774 }
775 f->refcount = 1;
776 f->version = 0;
777 f->data = cmd;
778 f->free = extf_filter_free;
779 f->get_filter = extf_filter_get_filter;
780 return ISO_SUCCESS;
781 }
782
783
784 /*
785 * A function which adds a filter to an IsoFile shall create a temporary
786 * FilterContext by iso_extf_create_context(), use it in one or more calls
787 * of filter.c:iso_file_add_filter() and finally dispose it by free().
788 */
789
iso_file_add_external_filter(IsoFile * file,IsoExternalFilterCommand * cmd,int flag)790 int iso_file_add_external_filter(IsoFile *file, IsoExternalFilterCommand *cmd,
791 int flag)
792 {
793 int ret;
794 FilterContext *f = NULL;
795 IsoStream *stream;
796 off_t original_size = 0, filtered_size = 0;
797
798 if (cmd->behavior & (1 | 2 | 4)) {
799 original_size = iso_file_get_size(file);
800 if (original_size <= 0 ||
801 ((cmd->behavior & 4) && original_size <= 2048)) {
802 return 2;
803 }
804 }
805 ret = extf_create_context(cmd, &f, 0);
806 if (ret < 0) {
807 return ret;
808 }
809 ret = iso_file_add_filter(file, f, 0);
810 free(f);
811 if (ret < 0) {
812 return ret;
813 }
814 /* Run a full filter process getsize so that the size is cached */
815 stream = iso_file_get_stream(file);
816 filtered_size = iso_stream_get_size(stream);
817 if (filtered_size < 0) {
818 iso_file_remove_filter(file, 0);
819 return filtered_size;
820 }
821 if (((cmd->behavior & 2) && filtered_size >= original_size) ||
822 ((cmd->behavior & 4) && filtered_size / 2048 >= original_size / 2048)){
823 ret = iso_file_remove_filter(file, 0);
824 if (ret < 0) {
825 return ret;
826 }
827 return 2;
828 }
829 return ISO_SUCCESS;
830 }
831
832
iso_stream_get_external_filter(IsoStream * stream,IsoExternalFilterCommand ** cmd,int flag)833 int iso_stream_get_external_filter(IsoStream *stream,
834 IsoExternalFilterCommand **cmd, int flag)
835 {
836 ExternalFilterStreamData *data;
837
838 if (stream->class != &extf_stream_class)
839 return 0;
840 data = stream->data;
841 *cmd = data->cmd;
842 return 1;
843 }
844
845