"Fossies" - the Fresh Open Source Software Archive

Member "libisofs-1.5.4/libisofs/filters/external.c" (8 Jul 2020, 21708 Bytes) of package /linux/misc/libisofs-1.5.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "external.c" see the Fossies "Dox" file reference documentation.

    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
   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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  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
  583 int extf_update_size(IsoStream *stream)
  584 {
  585     /* By principle size is determined only once */
  586     return 1;
  587 }
  588 
  589 
  590 static
  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
  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
  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
  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
  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
  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 
  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 
  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