1 /*
2  * Copyright (c) 2007 Vreixo Formoso
3  * Copyright (c) 2009 - 2015 Thomas Schmitt
4  *
5  * This file is part of the libisofs project; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License version 2
7  * or later as published by the Free Software Foundation.
8  * See COPYING file for details.
9  */
10 
11 #ifdef HAVE_CONFIG_H
12 #include "../config.h"
13 #endif
14 
15 #include "libisofs.h"
16 #include "stream.h"
17 #include "fsource.h"
18 #include "util.h"
19 #include "node.h"
20 
21 #include <stdlib.h>
22 #include <string.h>
23 #include <limits.h>
24 #include <stdio.h>
25 
26 
27 #ifndef PATH_MAX
28 #define PATH_MAX Libisofs_default_path_maX
29 #endif
30 
31 
32 ino_t serial_id = (ino_t)1;
33 ino_t mem_serial_id = (ino_t)1;
34 ino_t cut_out_serial_id = (ino_t)1;
35 
36 static
fsrc_open(IsoStream * stream)37 int fsrc_open(IsoStream *stream)
38 {
39     int ret;
40     struct stat info;
41     off_t esize;
42     IsoFileSource *src;
43     if (stream == NULL) {
44         return ISO_NULL_POINTER;
45     }
46     src = ((FSrcStreamData*)stream->data)->src;
47     ret = iso_file_source_stat(src, &info);
48     if (ret < 0) {
49         return ret;
50     }
51     ret = iso_file_source_open(src);
52     if (ret < 0) {
53         return ret;
54     }
55     esize = ((FSrcStreamData*)stream->data)->size;
56     if (info.st_size == esize) {
57         return ISO_SUCCESS;
58     } else {
59         return (esize > info.st_size) ? 3 : 2;
60     }
61 }
62 
63 static
fsrc_close(IsoStream * stream)64 int fsrc_close(IsoStream *stream)
65 {
66     IsoFileSource *src;
67     if (stream == NULL) {
68         return ISO_NULL_POINTER;
69     }
70     src = ((FSrcStreamData*)stream->data)->src;
71     return iso_file_source_close(src);
72 }
73 
74 static
fsrc_get_size(IsoStream * stream)75 off_t fsrc_get_size(IsoStream *stream)
76 {
77     FSrcStreamData *data;
78     data = (FSrcStreamData*)stream->data;
79 
80     return data->size;
81 }
82 
83 static
fsrc_read(IsoStream * stream,void * buf,size_t count)84 int fsrc_read(IsoStream *stream, void *buf, size_t count)
85 {
86     IsoFileSource *src;
87     if (stream == NULL) {
88         return ISO_NULL_POINTER;
89     }
90     src = ((FSrcStreamData*)stream->data)->src;
91     return iso_file_source_read(src, buf, count);
92 }
93 
94 static
fsrc_is_repeatable(IsoStream * stream)95 int fsrc_is_repeatable(IsoStream *stream)
96 {
97     int ret;
98     struct stat info;
99     FSrcStreamData *data;
100     if (stream == NULL) {
101         return ISO_NULL_POINTER;
102     }
103     data = (FSrcStreamData*)stream->data;
104 
105     /* mode is not cached, this function is only useful for filters */
106     ret = iso_file_source_stat(data->src, &info);
107     if (ret < 0) {
108         return ret;
109     }
110     if (S_ISREG(info.st_mode) || S_ISBLK(info.st_mode)) {
111         return 1;
112     } else {
113         return 0;
114     }
115 }
116 
117 static
fsrc_get_id(IsoStream * stream,unsigned int * fs_id,dev_t * dev_id,ino_t * ino_id)118 void fsrc_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
119                 ino_t *ino_id)
120 {
121     FSrcStreamData *data;
122     IsoFilesystem *fs;
123 
124     data = (FSrcStreamData*)stream->data;
125     fs = iso_file_source_get_filesystem(data->src);
126 
127     *fs_id = fs->get_id(fs);
128     *dev_id = data->dev_id;
129     *ino_id = data->ino_id;
130 }
131 
132 static
fsrc_free(IsoStream * stream)133 void fsrc_free(IsoStream *stream)
134 {
135     FSrcStreamData *data;
136     data = (FSrcStreamData*)stream->data;
137     iso_file_source_unref(data->src);
138     free(data);
139 }
140 
141 static
fsrc_update_size(IsoStream * stream)142 int fsrc_update_size(IsoStream *stream)
143 {
144     int ret;
145     struct stat info;
146     IsoFileSource *src;
147 
148     if (stream == NULL) {
149         return ISO_NULL_POINTER;
150     }
151     src = ((FSrcStreamData*)stream->data)->src;
152     ret = iso_file_source_stat(src, &info);
153     if (ret < 0) {
154         return ret;
155     }
156 
157     ((FSrcStreamData*)stream->data)->size = info.st_size;
158     return ISO_SUCCESS;
159 }
160 
161 static
fsrc_get_input_stream(IsoStream * stream,int flag)162 IsoStream *fsrc_get_input_stream(IsoStream *stream, int flag)
163 {
164     return NULL;
165 }
166 
fsrc_clone_stream(IsoStream * old_stream,IsoStream ** new_stream,int flag)167 int fsrc_clone_stream(IsoStream *old_stream, IsoStream **new_stream,
168                       int flag)
169 {
170     FSrcStreamData *data, *new_data;
171     IsoStream *stream;
172     int ret;
173 
174     if (flag)
175         return ISO_STREAM_NO_CLONE; /* unknown option required */
176 
177     data = (FSrcStreamData*) old_stream->data;
178     if (data->src->class->version < 2)
179         return ISO_STREAM_NO_CLONE; /* No clone_src() method available */
180 
181     *new_stream = NULL;
182     stream = calloc(1, sizeof(IsoStream));
183     if (stream == NULL)
184         return ISO_OUT_OF_MEM;
185     new_data = calloc(1, sizeof(FSrcStreamData));
186     if (new_data == NULL) {
187         free((char *) stream);
188         return ISO_OUT_OF_MEM;
189     }
190     *new_stream = stream;
191     stream->class = old_stream->class;
192     stream->refcount = 1;
193     stream->data = new_data;
194 
195     ret = data->src->class->clone_src(data->src, &(new_data->src), 0);
196     if (ret < 0) {
197         free((char *) stream);
198         free((char *) new_data);
199         return ret;
200     }
201     new_data->dev_id = data->dev_id;
202     new_data->ino_id = data->ino_id;
203     new_data->size = data->size;
204 
205     return ISO_SUCCESS;
206 }
207 
208 static
209 IsoStreamIface fsrc_stream_class = {
210     4, /* version */
211     "fsrc",
212     fsrc_open,
213     fsrc_close,
214     fsrc_get_size,
215     fsrc_read,
216     fsrc_is_repeatable,
217     fsrc_get_id,
218     fsrc_free,
219     fsrc_update_size,
220     fsrc_get_input_stream,
221     NULL,
222     fsrc_clone_stream
223 };
224 
iso_file_source_stream_new(IsoFileSource * src,IsoStream ** stream)225 int iso_file_source_stream_new(IsoFileSource *src, IsoStream **stream)
226 {
227     int r;
228     struct stat info;
229     IsoStream *str;
230     FSrcStreamData *data;
231 
232     if (src == NULL || stream == NULL) {
233         return ISO_NULL_POINTER;
234     }
235 
236     r = iso_file_source_stat(src, &info);
237     if (r < 0) {
238         return r;
239     }
240     if (S_ISDIR(info.st_mode)) {
241         return ISO_FILE_IS_DIR;
242     }
243 
244     /* check for read access to contents */
245     r = iso_file_source_access(src);
246     if (r < 0) {
247         return r;
248     }
249 
250     str = malloc(sizeof(IsoStream));
251     if (str == NULL) {
252         return ISO_OUT_OF_MEM;
253     }
254     data = malloc(sizeof(FSrcStreamData));
255     if (data == NULL) {
256         free(str);
257         return ISO_OUT_OF_MEM;
258     }
259 
260     /* take the ref to IsoFileSource */
261     data->src = src;
262     data->size = info.st_size;
263 
264     /* get the id numbers */
265     {
266         IsoFilesystem *fs;
267         unsigned int fs_id;
268         fs = iso_file_source_get_filesystem(data->src);
269 
270         fs_id = fs->get_id(fs);
271         if (fs_id == 0) {
272             /*
273              * the filesystem implementation is unable to provide valid
274              * st_dev and st_ino fields. Use serial_id.
275              */
276             data->dev_id = (dev_t) 0;
277             data->ino_id = serial_id++;
278         } else {
279             data->dev_id = info.st_dev;
280             data->ino_id = info.st_ino;
281         }
282     }
283 
284     str->refcount = 1;
285     str->data = data;
286     str->class = &fsrc_stream_class;
287 
288     *stream = str;
289     return ISO_SUCCESS;
290 }
291 
292 
iso_stream_get_src_zf(IsoStream * stream,uint8_t zisofs_algo[2],int * header_size_div4,int * block_size_log2,uint64_t * uncompressed_size,int flag)293 int iso_stream_get_src_zf(IsoStream *stream, uint8_t zisofs_algo[2],
294                           int *header_size_div4, int *block_size_log2,
295                           uint64_t *uncompressed_size, int flag)
296 {
297     int ret;
298     FSrcStreamData *data;
299     IsoFileSource *src;
300 
301     /* Intimate friendship with libisofs/fs_image.c */
302     int iso_ifs_source_get_zf(IsoFileSource *src, uint8_t zisofs_algo[2],
303                               int *header_size_div4, int *block_size_log2,
304                               uint64_t *uncompressed_size, int flag);
305 
306     if (stream->class != &fsrc_stream_class)
307         return 0;
308     data = stream->data;
309     src = data->src;
310     ret = iso_ifs_source_get_zf(src, zisofs_algo, header_size_div4,
311                                 block_size_log2, uncompressed_size, 0);
312     return ret;
313 }
314 
315 
316 struct cut_out_stream
317 {
318     IsoFileSource *src;
319 
320     /* key for file identification inside filesystem */
321     dev_t dev_id;
322     ino_t ino_id;
323     off_t offset; /**< offset where read begins */
324     off_t size; /**< size of this file */
325     off_t pos; /* position on the file for read */
326 };
327 
328 static
cut_out_open(IsoStream * stream)329 int cut_out_open(IsoStream *stream)
330 {
331     int ret;
332     struct stat info;
333     IsoFileSource *src;
334     struct cut_out_stream *data;
335 
336     if (stream == NULL) {
337         return ISO_NULL_POINTER;
338     }
339 
340     data = stream->data;
341     src = data->src;
342     ret = iso_file_source_stat(data->src, &info);
343     if (ret < 0) {
344         return ret;
345     }
346     ret = iso_file_source_open(src);
347     if (ret < 0) {
348         return ret;
349     }
350 
351     {
352         off_t ret;
353         if (data->offset > info.st_size) {
354             /* file is smaller than expected */
355             ret = iso_file_source_lseek(src, info.st_size, 0);
356         } else {
357             ret = iso_file_source_lseek(src, data->offset, 0);
358         }
359         if (ret < 0) {
360             return (int) ret;
361         }
362     }
363     data->pos = 0;
364     if (data->offset + data->size > info.st_size) {
365         return 3; /* file smaller than expected */
366     } else {
367         return ISO_SUCCESS;
368     }
369 }
370 
371 static
cut_out_close(IsoStream * stream)372 int cut_out_close(IsoStream *stream)
373 {
374     IsoFileSource *src;
375     if (stream == NULL) {
376         return ISO_NULL_POINTER;
377     }
378     src = ((struct cut_out_stream*)stream->data)->src;
379     return iso_file_source_close(src);
380 }
381 
382 static
cut_out_get_size(IsoStream * stream)383 off_t cut_out_get_size(IsoStream *stream)
384 {
385     struct cut_out_stream *data = stream->data;
386     return data->size;
387 }
388 
389 static
cut_out_read(IsoStream * stream,void * buf,size_t count)390 int cut_out_read(IsoStream *stream, void *buf, size_t count)
391 {
392     struct cut_out_stream *data = stream->data;
393     count = (size_t) MIN((size_t) (data->size - data->pos), count);
394     if (count == 0) {
395         return 0;
396     }
397     return iso_file_source_read(data->src, buf, count);
398 }
399 
400 static
cut_out_is_repeatable(IsoStream * stream)401 int cut_out_is_repeatable(IsoStream *stream)
402 {
403     /* reg files are always repeatable */
404     return 1;
405 }
406 
407 static
cut_out_get_id(IsoStream * stream,unsigned int * fs_id,dev_t * dev_id,ino_t * ino_id)408 void cut_out_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
409                 ino_t *ino_id)
410 {
411     FSrcStreamData *data;
412     IsoFilesystem *fs;
413 
414     data = (FSrcStreamData*)stream->data;
415     fs = iso_file_source_get_filesystem(data->src);
416 
417     *fs_id = fs->get_id(fs);
418     *dev_id = data->dev_id;
419     *ino_id = data->ino_id;
420 }
421 
422 static
cut_out_free(IsoStream * stream)423 void cut_out_free(IsoStream *stream)
424 {
425     struct cut_out_stream *data = stream->data;
426     iso_file_source_unref(data->src);
427     free(data);
428 }
429 
430 static
cut_out_update_size(IsoStream * stream)431 int cut_out_update_size(IsoStream *stream)
432 {
433     return ISO_SUCCESS;
434 }
435 
436 static
cut_out_get_input_stream(IsoStream * stream,int flag)437 IsoStream* cut_out_get_input_stream(IsoStream *stream, int flag)
438 {
439     return NULL;
440 }
441 
442 static
cut_out_clone_stream(IsoStream * old_stream,IsoStream ** new_stream,int flag)443 int cut_out_clone_stream(IsoStream *old_stream, IsoStream **new_stream,
444                       int flag)
445 {
446     struct cut_out_stream *data, *new_data;
447     IsoStream *stream;
448     int ret;
449 
450     if (flag)
451         return ISO_STREAM_NO_CLONE; /* unknown option required */
452 
453     data = (struct cut_out_stream *) old_stream->data;
454     if (data->src->class->version < 2)
455         return ISO_STREAM_NO_CLONE; /* No clone_src() method available */
456 
457     *new_stream = NULL;
458     stream = calloc(1, sizeof(IsoStream));
459     if (stream == NULL)
460         return ISO_OUT_OF_MEM;
461     stream->refcount = 1;
462     stream->class = old_stream->class;
463     new_data = calloc(1, sizeof(struct cut_out_stream));
464     if (new_data == NULL) {
465         free((char *) stream);
466         return ISO_OUT_OF_MEM;
467     }
468     ret = data->src->class->clone_src(data->src, &(new_data->src), 0);
469     if (ret < 0) {
470         free((char *) stream);
471         free((char *) new_data);
472         return ret;
473     }
474 
475     new_data->dev_id = (dev_t) 0;
476     new_data->ino_id = cut_out_serial_id++;
477     new_data->offset = data->offset;
478     new_data->size = data->size;
479     new_data->pos = 0;
480 
481     stream->data = new_data;
482     *new_stream = stream;
483     return ISO_SUCCESS;
484 }
485 
486 /*
487  * TODO update cut out streams to deal with update_size(). Seems hard.
488  */
489 static
490 IsoStreamIface cut_out_stream_class = {
491     4, /* version */
492     "cout",
493     cut_out_open,
494     cut_out_close,
495     cut_out_get_size,
496     cut_out_read,
497     cut_out_is_repeatable,
498     cut_out_get_id,
499     cut_out_free,
500     cut_out_update_size,
501     cut_out_get_input_stream,
502     NULL,
503     cut_out_clone_stream
504 
505 };
506 
iso_cut_out_stream_new(IsoFileSource * src,off_t offset,off_t size,IsoStream ** stream)507 int iso_cut_out_stream_new(IsoFileSource *src, off_t offset, off_t size,
508                            IsoStream **stream)
509 {
510     int r;
511     struct stat info;
512     IsoStream *str;
513     struct cut_out_stream *data;
514 
515     if (src == NULL || stream == NULL) {
516         return ISO_NULL_POINTER;
517     }
518     if (size == 0) {
519         return ISO_WRONG_ARG_VALUE;
520     }
521 
522     r = iso_file_source_stat(src, &info);
523     if (r < 0) {
524         return r;
525     }
526     if (!S_ISREG(info.st_mode)) {
527         return ISO_WRONG_ARG_VALUE;
528     }
529     if (offset > info.st_size) {
530         return ISO_FILE_OFFSET_TOO_BIG;
531     }
532 
533     /* check for read access to contents */
534     r = iso_file_source_access(src);
535     if (r < 0) {
536         return r;
537     }
538 
539     str = malloc(sizeof(IsoStream));
540     if (str == NULL) {
541         return ISO_OUT_OF_MEM;
542     }
543     data = malloc(sizeof(struct cut_out_stream));
544     if (data == NULL) {
545         free(str);
546         return ISO_OUT_OF_MEM;
547     }
548 
549     /* take a new ref to IsoFileSource */
550     data->src = src;
551     iso_file_source_ref(src);
552 
553     data->offset = offset;
554     data->size = MIN(info.st_size - offset, size);
555 
556     /* get the id numbers */
557     data->dev_id = (dev_t) 0;
558     data->ino_id = cut_out_serial_id++;
559 
560     str->refcount = 1;
561     str->data = data;
562     str->class = &cut_out_stream_class;
563 
564     *stream = str;
565     return ISO_SUCCESS;
566 }
567 
568 
569 
570 typedef struct
571 {
572     uint8_t *buf;
573     ssize_t offset; /* -1 if stream closed */
574     ino_t ino_id;
575     size_t size;
576 } MemStreamData;
577 
578 static
mem_open(IsoStream * stream)579 int mem_open(IsoStream *stream)
580 {
581     MemStreamData *data;
582     if (stream == NULL) {
583         return ISO_NULL_POINTER;
584     }
585     data = (MemStreamData*)stream->data;
586     if (data->offset != -1) {
587         return ISO_FILE_ALREADY_OPENED;
588     }
589     data->offset = 0;
590     return ISO_SUCCESS;
591 }
592 
593 static
mem_close(IsoStream * stream)594 int mem_close(IsoStream *stream)
595 {
596     MemStreamData *data;
597     if (stream == NULL) {
598         return ISO_NULL_POINTER;
599     }
600     data = (MemStreamData*)stream->data;
601     if (data->offset == -1) {
602         return ISO_FILE_NOT_OPENED;
603     }
604     data->offset = -1;
605     return ISO_SUCCESS;
606 }
607 
608 static
mem_get_size(IsoStream * stream)609 off_t mem_get_size(IsoStream *stream)
610 {
611     MemStreamData *data;
612     data = (MemStreamData*)stream->data;
613 
614     return (off_t)data->size;
615 }
616 
617 static
mem_read(IsoStream * stream,void * buf,size_t count)618 int mem_read(IsoStream *stream, void *buf, size_t count)
619 {
620     size_t len;
621     MemStreamData *data;
622     if (stream == NULL || buf == NULL) {
623         return ISO_NULL_POINTER;
624     }
625     if (count == 0) {
626         return ISO_WRONG_ARG_VALUE;
627     }
628     data = stream->data;
629 
630     if (data->offset == -1) {
631         return ISO_FILE_NOT_OPENED;
632     }
633 
634     if (data->offset >= (ssize_t) data->size) {
635         return 0; /* EOF */
636     }
637 
638     len = MIN(count, data->size - data->offset);
639     memcpy(buf, data->buf + data->offset, len);
640     data->offset += len;
641     return len;
642 }
643 
644 static
mem_is_repeatable(IsoStream * stream)645 int mem_is_repeatable(IsoStream *stream)
646 {
647     return 1;
648 }
649 
650 static
mem_get_id(IsoStream * stream,unsigned int * fs_id,dev_t * dev_id,ino_t * ino_id)651 void mem_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
652                 ino_t *ino_id)
653 {
654     MemStreamData *data;
655     data = (MemStreamData*)stream->data;
656     *fs_id = ISO_MEM_FS_ID;
657     *dev_id = 0;
658     *ino_id = data->ino_id;
659 }
660 
661 static
mem_free(IsoStream * stream)662 void mem_free(IsoStream *stream)
663 {
664     MemStreamData *data;
665     data = (MemStreamData*)stream->data;
666     if (data->buf != NULL)
667         free(data->buf);
668     free(data);
669 }
670 
671 static
mem_update_size(IsoStream * stream)672 int mem_update_size(IsoStream *stream)
673 {
674     return ISO_SUCCESS;
675 }
676 
677 static
mem_get_input_stream(IsoStream * stream,int flag)678 IsoStream* mem_get_input_stream(IsoStream *stream, int flag)
679 {
680     return NULL;
681 }
682 
683 static
mem_clone_stream(IsoStream * old_stream,IsoStream ** new_stream,int flag)684 int mem_clone_stream(IsoStream *old_stream, IsoStream **new_stream,
685                       int flag)
686 {
687     MemStreamData *data, *new_data;
688     IsoStream *stream;
689     uint8_t *new_buf = NULL;
690 
691     if (flag)
692         return ISO_STREAM_NO_CLONE; /* unknown option required */
693 
694     *new_stream = NULL;
695     stream = calloc(1, sizeof(IsoStream));
696     if (stream == NULL)
697         return ISO_OUT_OF_MEM;
698     stream->refcount = 1;
699     stream->class = old_stream->class;
700     new_data = calloc(1, sizeof(MemStreamData));
701     if (new_data == NULL) {
702         free((char *) stream);
703         return ISO_OUT_OF_MEM;
704     }
705     data = (MemStreamData *) old_stream->data;
706     if (data->size > 0) {
707         new_buf = calloc(1, data->size);
708         if (new_buf == NULL) {
709             free((char *) stream);
710             free((char *) new_data);
711             return ISO_OUT_OF_MEM;
712         }
713         memcpy(new_buf, data->buf, data->size);
714     }
715     new_data->buf = new_buf;
716     new_data->offset = -1;
717     new_data->ino_id = mem_serial_id++;
718     new_data->size = data->size;
719 
720     stream->data = new_data;
721     *new_stream = stream;
722     return ISO_SUCCESS;
723 }
724 
725 
726 static
727 IsoStreamIface mem_stream_class = {
728     4, /* version */
729     "mem ",
730     mem_open,
731     mem_close,
732     mem_get_size,
733     mem_read,
734     mem_is_repeatable,
735     mem_get_id,
736     mem_free,
737     mem_update_size,
738     mem_get_input_stream,
739     NULL,
740     mem_clone_stream
741 
742 };
743 
744 /**
745  * Create a stream for reading from a arbitrary memory buffer.
746  * When the Stream refcount reach 0, the buffer is free(3).
747  *
748  * @return
749  *      1 success, < 0 error
750  */
iso_memory_stream_new(unsigned char * buf,size_t size,IsoStream ** stream)751 int iso_memory_stream_new(unsigned char *buf, size_t size, IsoStream **stream)
752 {
753     IsoStream *str;
754     MemStreamData *data;
755 
756     if (buf == NULL || stream == NULL) {
757         return ISO_NULL_POINTER;
758     }
759 
760     str = malloc(sizeof(IsoStream));
761     if (str == NULL) {
762         return ISO_OUT_OF_MEM;
763     }
764     data = malloc(sizeof(MemStreamData));
765     if (data == NULL) {
766         free(str);
767         return ISO_OUT_OF_MEM;
768     }
769 
770     /* fill data */
771     data->buf = buf;
772     data->size = size;
773     data->offset = -1;
774     data->ino_id = mem_serial_id++;
775 
776     str->refcount = 1;
777     str->data = data;
778     str->class = &mem_stream_class;
779 
780     *stream = str;
781     return ISO_SUCCESS;
782 }
783 
iso_stream_ref(IsoStream * stream)784 void iso_stream_ref(IsoStream *stream)
785 {
786     ++stream->refcount;
787 }
788 
iso_stream_unref(IsoStream * stream)789 void iso_stream_unref(IsoStream *stream)
790 {
791     if (--stream->refcount == 0) {
792         stream->class->free(stream);
793         free(stream);
794     }
795 }
796 
797 inline
iso_stream_open(IsoStream * stream)798 int iso_stream_open(IsoStream *stream)
799 {
800     return stream->class->open(stream);
801 }
802 
803 inline
iso_stream_close(IsoStream * stream)804 int iso_stream_close(IsoStream *stream)
805 {
806     return stream->class->close(stream);
807 }
808 
809 inline
iso_stream_get_size(IsoStream * stream)810 off_t iso_stream_get_size(IsoStream *stream)
811 {
812     return stream->class->get_size(stream);
813 }
814 
815 inline
iso_stream_read(IsoStream * stream,void * buf,size_t count)816 int iso_stream_read(IsoStream *stream, void *buf, size_t count)
817 {
818     return stream->class->read(stream, buf, count);
819 }
820 
821 inline
iso_stream_is_repeatable(IsoStream * stream)822 int iso_stream_is_repeatable(IsoStream *stream)
823 {
824     return stream->class->is_repeatable(stream);
825 }
826 
827 inline
iso_stream_update_size(IsoStream * stream)828 int iso_stream_update_size(IsoStream *stream)
829 {
830     IsoStreamIface* class = stream->class;
831     return (class->version >= 1) ? class->update_size(stream) : 0;
832 }
833 
834 inline
iso_stream_get_id(IsoStream * stream,unsigned int * fs_id,dev_t * dev_id,ino_t * ino_id)835 void iso_stream_get_id(IsoStream *stream, unsigned int *fs_id, dev_t *dev_id,
836                       ino_t *ino_id)
837 {
838     stream->class->get_id(stream, fs_id, dev_id, ino_id);
839 }
840 
iso_stream_get_file_name(IsoStream * stream,char * name)841 void iso_stream_get_file_name(IsoStream *stream, char *name)
842 {
843     char *type = stream->class->type;
844 
845     if (!strncmp(type, "fsrc", 4)) {
846         FSrcStreamData *data = stream->data;
847         char *path = iso_file_source_get_path(data->src);
848         if (path == NULL) {
849             name[0] = 0;
850             return;
851         }
852         strncpy(name, path, PATH_MAX - 1);
853         name[PATH_MAX - 1] = 0;
854         free(path);
855     } else if (!strncmp(type, "boot", 4)) {
856         strcpy(name, "BOOT CATALOG");
857     } else if (!strncmp(type, "mem ", 4)) {
858         strcpy(name, "MEM SOURCE");
859     } else if (!strncmp(type, "extf", 4)) {
860         strcpy(name, "EXTERNAL FILTER");
861     } else {
862         strcpy(name, "UNKNOWN SOURCE");
863     }
864 }
865 
866 /* @param flag  bit0= Obtain most fundamental stream */
iso_stream_get_input_stream(IsoStream * stream,int flag)867 IsoStream *iso_stream_get_input_stream(IsoStream *stream, int flag)
868 {
869     IsoStreamIface* class;
870     IsoStream *result = NULL, *next;
871 
872     if (stream == NULL) {
873         return NULL;
874     }
875     while (1) {
876         class = stream->class;
877         if (class->version < 2)
878             return result;
879         next = class->get_input_stream(stream, 0);
880         if (next == NULL)
881             return result;
882         result = next;
883         if (!(flag & 1))
884             return result;
885         stream = result;
886     }
887 }
888 
iso_stream_get_source_path(IsoStream * stream,int flag)889 char *iso_stream_get_source_path(IsoStream *stream, int flag)
890 {
891     char *path = NULL, ivd[80], *raw_path = NULL;
892 
893     if (stream == NULL) {
894         return NULL;
895     }
896     if (stream->class == &fsrc_stream_class) {
897         FSrcStreamData *fsrc_data = stream->data;
898 
899         path = iso_file_source_get_path(fsrc_data->src);
900     } else if (stream->class == &cut_out_stream_class) {
901         struct cut_out_stream *cout_data = stream->data;
902 
903         raw_path = iso_file_source_get_path(cout_data->src);
904         sprintf(ivd, " %.f %.f",
905                 (double) cout_data->offset, (double) cout_data->size);
906         path= calloc(strlen(raw_path) + strlen(ivd) + 1, 1);
907         if (path == NULL) {
908             goto ex;
909         }
910         strcpy(path, raw_path);
911         strcat(path, ivd);
912     }
913 ex:;
914     if (raw_path != NULL)
915         free(raw_path);
916     return path;
917 }
918 
919 /*
920    @param flag bit0= in case of filter stream do not dig for base stream
921    @return 1 = ok , 0 = not an ISO image stream , <0 = error
922 */
iso_stream_set_image_ino(IsoStream * stream,ino_t ino,int flag)923 int iso_stream_set_image_ino(IsoStream *stream, ino_t ino, int flag)
924 {
925     IsoStream *base_stream;
926 
927     if (stream == NULL) {
928         return ISO_NULL_POINTER;
929     }
930     if (!(flag & 1)) {
931         base_stream = iso_stream_get_input_stream(stream, 1);
932         if (base_stream != NULL)
933             stream = base_stream;
934     }
935     if (stream->class == &fsrc_stream_class) {
936         FSrcStreamData *fsrc_data = stream->data;
937         fsrc_data->ino_id = ino;
938         return 1;
939     }
940    return 0;
941 }
942 
iso_stream_cmp_ifs_sections(IsoStream * s1,IsoStream * s2,int * cmp_ret,int flag)943 int iso_stream_cmp_ifs_sections(IsoStream *s1, IsoStream *s2, int *cmp_ret,
944                                 int flag)
945 {
946     int ret;
947     FSrcStreamData *fssd1, *fssd2;
948     IsoFileSource *src1, *src2;
949 
950     /* Must keep any suspect in the game to preserve transitivity of the
951        calling function by ranking applicable streams lower than
952        non-applicable. ones.
953     */
954     if (s1->class != &fsrc_stream_class && s2->class != &fsrc_stream_class)
955         return 0;
956 
957     /* Compare eventual image data section LBA and sizes */
958     if (s1->class == &fsrc_stream_class) {
959         fssd1= (FSrcStreamData *) s1->data;
960         src1 = fssd1->src;
961     } else {
962         src1 = NULL;
963     }
964     if (s2->class == &fsrc_stream_class) {
965         fssd2= (FSrcStreamData *) s2->data;
966         src2 = fssd2->src;
967     } else {
968         src2 = NULL;
969     }
970     ret = iso_ifs_sections_cmp(src1, src2, cmp_ret, 1);
971     if (ret <= 0)
972         return 0;
973     return 1;
974 }
975 
976 
977 /* Maintain and exploit a list of stream compare functions seen by
978    iso_stream_cmp_ino(). This is needed to separate stream comparison
979    families in order to keep iso_stream_cmp_ino() transitive while
980    alternative stream->class->cmp_ino() decide inside the families.
981 */
982 struct iso_streamcmprank {
983     int (*cmp_func)(IsoStream *s1, IsoStream *s2);
984     struct iso_streamcmprank *next;
985 };
986 
987 static struct iso_streamcmprank *streamcmpranks = NULL;
988 
989 static
iso_get_streamcmprank(int (* cmp_func)(IsoStream * s1,IsoStream * s2),int flag)990 int iso_get_streamcmprank(int (*cmp_func)(IsoStream *s1, IsoStream *s2),
991                           int flag)
992 {
993     int idx;
994     struct iso_streamcmprank *cpr, *last_cpr = NULL;
995 
996     idx = 0;
997     for (cpr = streamcmpranks; cpr != NULL; cpr = cpr->next) {
998         if (cpr->cmp_func == cmp_func)
999     break;
1000         idx++;
1001         last_cpr = cpr;
1002     }
1003     if (cpr != NULL)
1004         return idx;
1005     LIBISO_ALLOC_MEM_VOID(cpr, struct iso_streamcmprank, 1);
1006     cpr->cmp_func = cmp_func;
1007     cpr->next = NULL;
1008     if (last_cpr != NULL)
1009         last_cpr->next = cpr;
1010     if (streamcmpranks == NULL)
1011         streamcmpranks = cpr;
1012     return idx;
1013 ex:;
1014     return -1;
1015 }
1016 
1017 static
iso_cmp_streamcmpranks(int (* cf1)(IsoStream * s1,IsoStream * s2),int (* cf2)(IsoStream * s1,IsoStream * s2))1018 int iso_cmp_streamcmpranks(int (*cf1)(IsoStream *s1, IsoStream *s2),
1019                            int (*cf2)(IsoStream *s1, IsoStream *s2))
1020 {
1021     int rank1, rank2;
1022 
1023     rank1 = iso_get_streamcmprank(cf1, 0);
1024     rank2 = iso_get_streamcmprank(cf2, 0);
1025     return rank1 < rank2 ? -1 : 1;
1026 }
1027 
iso_stream_destroy_cmpranks(int flag)1028 int iso_stream_destroy_cmpranks(int flag)
1029 {
1030     struct iso_streamcmprank *cpr, *next;
1031 
1032     for (cpr = streamcmpranks; cpr != NULL; cpr = next) {
1033         next = cpr->next;
1034         LIBISO_FREE_MEM(cpr);
1035     }
1036     streamcmpranks = NULL;
1037     return ISO_SUCCESS;
1038 }
1039 
1040 
1041 /* API */
iso_stream_cmp_ino(IsoStream * s1,IsoStream * s2,int flag)1042 int iso_stream_cmp_ino(IsoStream *s1, IsoStream *s2, int flag)
1043 {
1044     int ret;
1045     unsigned int fs_id1, fs_id2;
1046     dev_t dev_id1, dev_id2;
1047     ino_t ino_id1, ino_id2;
1048     off_t size1, size2;
1049 
1050 /*
1051    #define Libisofs_stream_cmp_ino_debuG 1
1052 */
1053 #ifdef Libisofs_stream_cmp_ino_debuG
1054     static int report_counter = 0;
1055     static int debug = 1;
1056 #endif /* Libisofs_stream_cmp_ino_debuG */
1057 
1058     if (s1 == s2)
1059         return 0;
1060     if (s1 == NULL)
1061         return -1;
1062     if (s2 == NULL)
1063         return 1;
1064 
1065     /* This stays transitive by the fact that
1066        iso_stream_cmp_ifs_sections() is transitive,
1067        returns > 0 if s1 or s2 are applicable,
1068        ret is -1 if s1 is applicable but s2 is not,
1069        ret is 1 if s1 is not applicable but s2 is.
1070 
1071        Proof:
1072        Be A the set of applicable streams, S and G transitive and
1073        antisymmetric relations in respect to outcome {-1, 0, 1}.
1074        The combined relation R shall be defined by
1075          I.   R(a,b) = S(a,b) if a in A or b in A, else G(a,b)
1076        Further S shall have the property
1077          II.  S(a,b) = -1 if a in A and b not in A
1078        Then R can be proven to be transitive:
1079        By enumerating the 8 combinations of a,b,c being in A or not, we get
1080        5 cases of pure S or pure G. Three cases are mixed:
1081          a,b not in A, c in A : G(a,b) == -1, S(b,c) == -1 -> S(a,c) == -1
1082              Impossible because S(b,c) == -1 contradicts II.
1083          a,c not in A, b in A : S(a,b) == -1, S(b,c) == -1 -> G(a,c) == -1
1084              Impossible because S(a,b) == -1 contradicts II.
1085          b,c not in A, a in A : S(a,b) == -1, G(b,c) == -1 -> S(a,c) == -1
1086              Always true because S(a,c) == -1 by definition II.
1087     */
1088     if (iso_stream_cmp_ifs_sections(s1, s2, &ret, 0) > 0)
1089         return ret; /* Both are unfiltered from loaded ISO filesystem */
1090 
1091     if (!(flag & 1)) {
1092        /* Filters may have smarter methods to compare themselves with others.
1093           Transitivity is ensured by ranking mixed pairs by the rank of their
1094           comparison functions, and by ranking streams with .cmp_ino lower
1095           than streams without.
1096           (One could merge (class->version < 3) and (cmp_ino == NULL).)
1097 
1098           Here we define S for "and" rather than "or"
1099             I.   R(a,b) = S(a,b) if a in A and b in A, else G(a,b)
1100           and the function ranking in case of "exor" makes sure that
1101             II.  G(a,b) = -1 if a in A and b not in A
1102           Again we get three mixed cases:
1103             a not in A, b,c in A : G(a,b) == -1, S(b,c) == -1 -> G(a,c) == -1
1104                 Impossible because G(a,b) == -1 contradicts II.
1105             b not in A, a,c in A : G(a,b) == -1, G(b,c) == -1 -> S(a,c) == -1
1106                 Impossible because G(b,c) == -1 contradicts II.
1107             c not in A, a,b in A : S(a,b) == -1, G(b,c) == -1 -> G(a,c) == -1
1108                 Always true because G(a,c) == -1 by definition II.
1109        */
1110        if ((s1->class->version >= 3) ^ (s2->class->version >= 3)) {
1111            /* One of both has no own com_ino function. Rank it as larger. */
1112            return s1->class->version >= 3 ? -1 : 1;
1113        } else if (s1->class->version >= 3) {
1114            if (s1->class->cmp_ino == s2->class->cmp_ino) {
1115                if (s1->class->cmp_ino == NULL) {
1116                    /* Both are NULL. No decision by .cmp_ino(). */;
1117                } else {
1118                    /* Both are compared by the same function */
1119                    ret = s1->class->cmp_ino(s1, s2);
1120                    return ret;
1121                }
1122            } else {
1123                /* Not the same cmp_ino() function. Decide by list rank of
1124                   function while building the list on the fly.
1125                */
1126                ret = iso_cmp_streamcmpranks(s1->class->cmp_ino,
1127                                             s2->class->cmp_ino);
1128                return ret;
1129            }
1130        }
1131     }
1132 
1133     iso_stream_get_id(s1, &fs_id1, &dev_id1, &ino_id1);
1134     iso_stream_get_id(s2, &fs_id2, &dev_id2, &ino_id2);
1135     if (fs_id1 < fs_id2) {
1136         return -1;
1137     } else if (fs_id1 > fs_id2) {
1138         return 1;
1139     }
1140     /* files belong to the same fs */
1141     if (dev_id1 > dev_id2) {
1142         return -1;
1143     } else if (dev_id1 < dev_id2) {
1144         return 1;
1145     } else if (ino_id1 < ino_id2) {
1146         return -1;
1147     } else if (ino_id1 > ino_id2) {
1148         return 1;
1149     }
1150     size1 = iso_stream_get_size(s1);
1151     size2 = iso_stream_get_size(s2);
1152     if (size1 < size2) {
1153 
1154 #ifdef Libisofs_stream_cmp_ino_debuG
1155         if (debug) {
1156             if (report_counter < 5)
1157                 fprintf(stderr,
1158       "\n\nlibisofs_DEBUG : Program error: same ino but differing size\n\n\n");
1159             else if (report_counter == 5)
1160                 fprintf(stderr,
1161       "\n\nlibisofs_DEBUG : Inode error: more of same ino but differing size\n\n\n");
1162             report_counter++;
1163         }
1164 #endif /* Libisofs_stream_cmp_ino_debuG */
1165 
1166         return -1;
1167     } else if (size1 > size2) {
1168 
1169 #ifdef Libisofs_stream_cmp_ino_debuG
1170         if (debug) {
1171             if (report_counter < 5)
1172                 fprintf(stderr,
1173       "\n\nlibisofs_DEBUG : Inode error: same ino but differing size\n\n\n");
1174             else if (report_counter == 5)
1175                 fprintf(stderr,
1176       "\n\nlibisofs_DEBUG : Program error: more of same ino but differing size\n\n\n");
1177             report_counter++;
1178         }
1179 #endif /* Libisofs_stream_cmp_ino_debuG */
1180 
1181         return 1;
1182     }
1183 
1184     if (s1->class != s2->class)
1185         return (s1->class < s2->class ? -1 : 1);
1186     if (fs_id1 == 0 && dev_id1 == 0 && ino_id1 == 0) {
1187         return (s1 < s2 ? -1 : 1);
1188     }
1189     return 0;
1190 }
1191 
1192 
1193 /**
1194  * @return
1195  *     1 ok, 0 EOF, < 0 error
1196  */
iso_stream_read_buffer(IsoStream * stream,char * buf,size_t count,size_t * got)1197 int iso_stream_read_buffer(IsoStream *stream, char *buf, size_t count,
1198                            size_t *got)
1199 {
1200     ssize_t result;
1201 
1202     *got = 0;
1203     do {
1204         result = iso_stream_read(stream, buf + *got, count - *got);
1205         if (result < 0) {
1206             memset(buf + *got, 0, count - *got);
1207             return result;
1208         }
1209         if (result == 0)
1210             break;
1211         *got += result;
1212     } while (*got < count);
1213 
1214     if (*got < count) {
1215         /* eof */
1216         memset(buf + *got, 0, count - *got);
1217         return 0;
1218     }
1219     return 1;
1220 }
1221 
1222 /* @param flag bit0= dig out most original stream (e.g. because from old image)
1223    @return 1=ok, md5 is valid,
1224            0= not ok,
1225           <0 fatal error, abort
1226 */
iso_stream_make_md5(IsoStream * stream,char md5[16],int flag)1227 int iso_stream_make_md5(IsoStream *stream, char md5[16], int flag)
1228 {
1229     int ret, is_open = 0;
1230     char * buffer = NULL;
1231     void *ctx= NULL;
1232     off_t file_size;
1233     uint32_t b, nblocks;
1234     size_t got_bytes;
1235     IsoStream *input_stream;
1236 
1237     LIBISO_ALLOC_MEM(buffer, char, 2048);
1238     if (flag & 1) {
1239         while(1) {
1240            input_stream = iso_stream_get_input_stream(stream, 0);
1241            if (input_stream == NULL)
1242         break;
1243            stream = input_stream;
1244         }
1245     }
1246 
1247     if (! iso_stream_is_repeatable(stream))
1248         {ret = 0; goto ex;}
1249     ret = iso_md5_start(&ctx);
1250     if (ret < 0)
1251         goto ex;
1252     ret = iso_stream_open(stream);
1253     if (ret < 0)
1254         goto ex;
1255     is_open = 1;
1256     file_size = iso_stream_get_size(stream);
1257     nblocks = DIV_UP(file_size, 2048);
1258     for (b = 0; b < nblocks; ++b) {
1259         ret = iso_stream_read_buffer(stream, buffer, 2048, &got_bytes);
1260         if (ret < 0) {
1261             ret = 0;
1262             goto ex;
1263         }
1264         /* Do not use got_bytes to stay closer to IsoFileSrc processing */
1265         if (file_size - b * 2048 > 2048)
1266             ret = 2048;
1267         else
1268             ret = file_size - b * 2048;
1269         iso_md5_compute(ctx, buffer, ret);
1270     }
1271     ret = 1;
1272 ex:;
1273     if (is_open)
1274         iso_stream_close(stream);
1275     if (ctx != NULL)
1276         iso_md5_end(&ctx, md5);
1277     LIBISO_FREE_MEM(buffer);
1278     return ret;
1279 }
1280 
1281 /* API */
iso_stream_clone(IsoStream * old_stream,IsoStream ** new_stream,int flag)1282 int iso_stream_clone(IsoStream *old_stream, IsoStream **new_stream, int flag)
1283 {
1284     int ret;
1285 
1286     if (old_stream->class->version < 4)
1287         return ISO_STREAM_NO_CLONE;
1288     ret = old_stream->class->clone_stream(old_stream, new_stream, 0);
1289     return ret;
1290 }
1291 
iso_stream_clone_filter_common(IsoStream * old_stream,IsoStream ** new_stream,IsoStream ** new_input,int flag)1292 int iso_stream_clone_filter_common(IsoStream *old_stream,
1293                                    IsoStream **new_stream,
1294                                    IsoStream **new_input, int flag)
1295 {
1296     IsoStream *stream, *input_stream;
1297     int ret;
1298 
1299     *new_stream = NULL;
1300     *new_input = NULL;
1301     input_stream = iso_stream_get_input_stream(old_stream, 0);
1302     if (input_stream == NULL)
1303         return ISO_STREAM_NO_CLONE;
1304     stream = calloc(1, sizeof(IsoStream));
1305     if (stream == NULL)
1306         return ISO_OUT_OF_MEM;
1307     ret = iso_stream_clone(input_stream, new_input, 0);
1308     if (ret < 0) {
1309         free((char *) stream);
1310         return ret;
1311     }
1312     stream->class = old_stream->class;
1313     stream->refcount = 1;
1314     stream->data = NULL;
1315     *new_stream = stream;
1316     return ISO_SUCCESS;
1317 }
1318 
1319