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