1 /*****************************************************************************
2  * archive.c: libarchive based stream filter
3  *****************************************************************************
4  * Copyright (C) 2016 VLC authors and VideoLAN
5  * $Id: af69a29094c5c07eb42945959d4fd36669954937 $
6  *
7  * Authors: Filip Roséen <filip@atch.se>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17  * GNU Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23 
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27 
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
30 #include <vlc_stream.h>
31 #include <vlc_stream_extractor.h>
32 #include <vlc_dialog.h>
33 #include <vlc_input_item.h>
34 
35 #include <assert.h>
36 #include <archive.h>
37 #include <archive_entry.h>
38 
39 #if ARCHIVE_VERSION_NUMBER < 3002000
40 typedef __LA_INT64_T la_int64_t;
41 typedef __LA_SSIZE_T la_ssize_t;
42 #endif
43 
44 static  int ExtractorOpen( vlc_object_t* );
45 static void ExtractorClose( vlc_object_t* );
46 
47 static  int DirectoryOpen( vlc_object_t* );
48 static void DirectoryClose( vlc_object_t* );
49 
50 vlc_module_begin()
51     set_category( CAT_INPUT )
52     set_subcategory( SUBCAT_INPUT_STREAM_FILTER )
53     set_capability( "stream_directory", 99 )
54     set_description( N_( "libarchive based stream directory" ) )
55     set_callbacks( DirectoryOpen, DirectoryClose );
56 
57     add_submodule()
58         set_description( N_( "libarchive based stream extractor" ) )
59         set_capability( "stream_extractor", 99 )
60         set_callbacks( ExtractorOpen, ExtractorClose );
61 
62 vlc_module_end()
63 
64 typedef struct libarchive_callback_t libarchive_callback_t;
65 typedef struct private_sys_t private_sys_t;
66 typedef struct archive libarchive_t;
67 
68 struct private_sys_t
69 {
70     libarchive_t* p_archive;
71     vlc_object_t* p_obj;
72     stream_t* source;
73 
74     struct archive_entry* p_entry;
75     bool b_dead;
76     bool b_eof;
77 
78     uint64_t i_offset;
79 
80     uint8_t buffer[ 8192 ];
81     bool b_seekable_source;
82     bool b_seekable_archive;
83 
84     libarchive_callback_t** pp_callback_data;
85     size_t i_callback_data;
86 };
87 
88 struct libarchive_callback_t {
89     private_sys_t* p_sys;
90     stream_t* p_source;
91     char* psz_url;
92 };
93 
94 /* ------------------------------------------------------------------------- */
95 
libarchive_exit_cb(libarchive_t * p_arc,void * p_obj)96 static int libarchive_exit_cb( libarchive_t* p_arc, void* p_obj )
97 {
98     VLC_UNUSED( p_arc );
99 
100     libarchive_callback_t* p_cb = (libarchive_callback_t*)p_obj;
101 
102     if( p_cb->p_sys->source == p_cb->p_source )
103     {  /* DO NOT CLOSE OUR MOTHER STREAM */
104         if( !p_cb->p_sys->b_dead && vlc_stream_Seek( p_cb->p_source, 0 ) )
105             return ARCHIVE_FATAL;
106     }
107     else if( p_cb->p_source )
108     {
109         vlc_stream_Delete( p_cb->p_source );
110         p_cb->p_source = NULL;
111     }
112 
113     return ARCHIVE_OK;
114 }
115 
libarchive_jump_cb(libarchive_t * p_arc,void * p_obj_current,void * p_obj_next)116 static int libarchive_jump_cb( libarchive_t* p_arc, void* p_obj_current,
117   void* p_obj_next )
118 {
119     libarchive_callback_t* p_current = (libarchive_callback_t*)p_obj_current;
120     libarchive_callback_t* p_next    = (libarchive_callback_t*)p_obj_next;
121 
122     if( libarchive_exit_cb( p_arc, p_current ) )
123         return ARCHIVE_FATAL;
124 
125     if( p_next->p_source == NULL )
126         p_next->p_source = vlc_stream_NewURL( p_next->p_sys->p_obj,
127                                               p_next->psz_url );
128 
129     return p_next->p_source ? ARCHIVE_OK : ARCHIVE_FATAL;
130 }
131 
132 
libarchive_skip_cb(libarchive_t * p_arc,void * p_obj,off_t i_request)133 static la_int64_t libarchive_skip_cb( libarchive_t* p_arc, void* p_obj,
134   off_t i_request )
135 {
136     VLC_UNUSED( p_arc );
137 
138     libarchive_callback_t* p_cb = (libarchive_callback_t*)p_obj;
139 
140     stream_t*  p_source = p_cb->p_source;
141     private_sys_t* p_sys = p_cb->p_sys;
142 
143     /* TODO: fix b_seekable_source on libarchive_callback_t */
144 
145     if( p_sys->b_seekable_source )
146     {
147         if( vlc_stream_Seek( p_source, vlc_stream_Tell( p_source ) + i_request ) )
148             return ARCHIVE_FATAL;
149 
150         return i_request;
151     }
152 
153     ssize_t i_read = vlc_stream_Read( p_source, NULL, i_request );
154     return  i_read >= 0 ? i_read : ARCHIVE_FATAL;
155 }
156 
libarchive_seek_cb(libarchive_t * p_arc,void * p_obj,la_int64_t offset,int whence)157 static la_int64_t libarchive_seek_cb( libarchive_t* p_arc, void* p_obj,
158   la_int64_t offset, int whence )
159 {
160     VLC_UNUSED( p_arc );
161 
162     libarchive_callback_t* p_cb = (libarchive_callback_t*)p_obj;
163     stream_t* p_source = p_cb->p_source;
164 
165     ssize_t whence_pos;
166 
167     switch( whence )
168     {
169         case SEEK_SET: whence_pos = 0;                           break;
170         case SEEK_CUR: whence_pos = vlc_stream_Tell( p_source ); break;
171         case SEEK_END: whence_pos = stream_Size( p_source ); break;
172               default: vlc_assert_unreachable();
173 
174     }
175 
176     if( whence_pos < 0 || vlc_stream_Seek( p_source, whence_pos + offset ) )
177         return ARCHIVE_FATAL;
178 
179     return vlc_stream_Tell( p_source );
180 }
181 
libarchive_read_cb(libarchive_t * p_arc,void * p_obj,const void ** pp_dst)182 static la_ssize_t libarchive_read_cb( libarchive_t* p_arc, void* p_obj,
183   const void** pp_dst )
184 {
185     VLC_UNUSED( p_arc );
186 
187     libarchive_callback_t* p_cb = (libarchive_callback_t*)p_obj;
188 
189     stream_t*  p_source = p_cb->p_source;
190     private_sys_t* p_sys = p_cb->p_sys;
191 
192     ssize_t i_ret = vlc_stream_Read( p_source, &p_sys->buffer,
193       sizeof( p_sys->buffer ) );
194 
195     if( i_ret < 0 )
196     {
197         archive_set_error( p_sys->p_archive, ARCHIVE_FATAL,
198           "libarchive_read_cb failed = %zd", i_ret );
199 
200         return ARCHIVE_FATAL;
201     }
202 
203     *pp_dst = &p_sys->buffer;
204     return i_ret;
205 }
206 
207 /* ------------------------------------------------------------------------- */
208 
archive_push_resource(private_sys_t * p_sys,stream_t * p_source,char const * psz_url)209 static int archive_push_resource( private_sys_t* p_sys,
210   stream_t* p_source, char const* psz_url )
211 {
212     libarchive_callback_t** pp_callback_data;
213     libarchive_callback_t*   p_callback_data;
214 
215     /* INCREASE BUFFER SIZE */
216 
217     pp_callback_data = realloc( p_sys->pp_callback_data,
218       sizeof( *p_sys->pp_callback_data ) * ( p_sys->i_callback_data + 1 ) );
219 
220     if( unlikely( !pp_callback_data ) )
221         goto error;
222 
223     /* CREATE NEW NODE */
224 
225     p_callback_data = malloc( sizeof( *p_callback_data ) );
226 
227     if( unlikely( !p_callback_data ) )
228         goto error;
229 
230     /* INITIALIZE AND APPEND */
231 
232     p_callback_data->psz_url  = psz_url ? strdup( psz_url ) : NULL;
233     p_callback_data->p_source = p_source;
234     p_callback_data->p_sys    = p_sys;
235 
236     if( unlikely( !p_callback_data->psz_url && psz_url ) )
237     {
238         free( p_callback_data );
239         goto error;
240     }
241 
242     pp_callback_data[ p_sys->i_callback_data++ ] = p_callback_data;
243     p_sys->pp_callback_data = pp_callback_data;
244 
245     return VLC_SUCCESS;
246 
247 error:
248     free( pp_callback_data );
249     return VLC_ENOMEM;
250 }
251 
archive_init(private_sys_t * p_sys,stream_t * source)252 static int archive_init( private_sys_t* p_sys, stream_t* source )
253 {
254     /* CREATE ARCHIVE HANDLE */
255 
256     p_sys->p_archive = archive_read_new();
257 
258     if( unlikely( !p_sys->p_archive ) )
259     {
260         msg_Dbg( p_sys->p_obj, "unable to create libarchive handle" );
261         return VLC_EGENERIC;
262     }
263 
264     /* SETUP SEEKING */
265 
266     p_sys->b_seekable_archive = false;
267 
268     if( vlc_stream_Control( source, STREAM_CAN_SEEK,
269         &p_sys->b_seekable_source ) )
270     {
271         msg_Warn( p_sys->p_obj, "unable to query whether source stream can seek" );
272         p_sys->b_seekable_source = false;
273     }
274 
275     if( p_sys->b_seekable_source )
276     {
277         if( archive_read_set_seek_callback( p_sys->p_archive,
278             libarchive_seek_cb ) )
279         {
280             msg_Err( p_sys->p_obj, "archive_read_set_callback failed, aborting." );
281             return VLC_EGENERIC;
282         }
283     }
284 
285     /* ENABLE ALL FORMATS/FILTERS */
286 
287     archive_read_support_filter_all( p_sys->p_archive );
288     archive_read_support_format_all( p_sys->p_archive );
289 
290     /* REGISTER CALLBACK DATA */
291 
292     if( archive_read_set_switch_callback( p_sys->p_archive,
293         libarchive_jump_cb ) )
294     {
295         msg_Err( p_sys->p_obj, "archive_read_set_switch_callback failed, aborting." );
296         return VLC_EGENERIC;
297     }
298 
299     for( size_t i = 0; i < p_sys->i_callback_data; ++i )
300     {
301         if( archive_read_append_callback_data( p_sys->p_archive,
302             p_sys->pp_callback_data[i] ) )
303         {
304             return VLC_EGENERIC;
305         }
306     }
307 
308     /* OPEN THE ARCHIVE */
309 
310     if( archive_read_open2( p_sys->p_archive, p_sys->pp_callback_data[0], NULL,
311         libarchive_read_cb, libarchive_skip_cb, libarchive_exit_cb ) )
312     {
313         msg_Dbg( p_sys->p_obj, "libarchive: %s",
314           archive_error_string( p_sys->p_archive ) );
315 
316         return VLC_EGENERIC;
317     }
318 
319     return VLC_SUCCESS;
320 }
321 
archive_clean(private_sys_t * p_sys)322 static int archive_clean( private_sys_t* p_sys )
323 {
324     libarchive_t* p_arc = p_sys->p_archive;
325 
326     if( p_sys->p_entry )
327         archive_entry_free( p_sys->p_entry );
328 
329     if( p_arc )
330         archive_read_free( p_arc );
331 
332     p_sys->p_entry   = NULL;
333     p_sys->p_archive = NULL;
334 
335     return VLC_SUCCESS;
336 }
337 
archive_seek_subentry(private_sys_t * p_sys,char const * psz_subentry)338 static int archive_seek_subentry( private_sys_t* p_sys, char const* psz_subentry )
339 {
340     libarchive_t* p_arc = p_sys->p_archive;
341 
342     struct archive_entry* entry;
343     int archive_status;
344 
345     while( !( archive_status = archive_read_next_header( p_arc, &entry ) ) )
346     {
347         char const* entry_path = archive_entry_pathname( entry );
348 
349         if( strcmp( entry_path, psz_subentry ) == 0 )
350         {
351             p_sys->p_entry = archive_entry_clone( entry );
352 
353             if( unlikely( !p_sys->p_entry ) )
354                 return VLC_ENOMEM;
355 
356             break;
357         }
358 
359         archive_read_data_skip( p_arc );
360     }
361 
362     switch( archive_status )
363     {
364         case ARCHIVE_WARN:
365             msg_Warn( p_sys->p_obj,
366               "libarchive: %s", archive_error_string( p_arc ) );
367 
368         case ARCHIVE_EOF:
369         case ARCHIVE_FATAL:
370         case ARCHIVE_RETRY:
371             archive_set_error( p_arc, ARCHIVE_FATAL,
372                 "archive does not contain >>> %s <<<", psz_subentry );
373 
374             return VLC_EGENERIC;
375     }
376 
377     /* check if seeking is supported */
378 
379     if( p_sys->b_seekable_source )
380     {
381         if( archive_seek_data( p_sys->p_archive, 0, SEEK_CUR ) >= 0 )
382             p_sys->b_seekable_archive = true;
383     }
384 
385     return VLC_SUCCESS;
386 }
387 
archive_extractor_reset(stream_extractor_t * p_extractor)388 static int archive_extractor_reset( stream_extractor_t* p_extractor )
389 {
390     private_sys_t* p_sys = p_extractor->p_sys;
391 
392     if( vlc_stream_Seek( p_extractor->source, 0 )
393         || archive_clean( p_sys )
394         || archive_init( p_sys, p_extractor->source )
395         || archive_seek_subentry( p_sys, p_extractor->identifier ) )
396     {
397         p_sys->b_dead = true;
398         return VLC_EGENERIC;
399     }
400 
401     p_sys->i_offset = 0;
402     p_sys->b_eof = false;
403     p_sys->b_dead = false;
404     return VLC_SUCCESS;
405 }
406 
407 /* ------------------------------------------------------------------------- */
408 
setup(vlc_object_t * obj,stream_t * source)409 static private_sys_t* setup( vlc_object_t* obj, stream_t* source )
410 {
411     private_sys_t* p_sys  = calloc( 1, sizeof( *p_sys ) );
412     char* psz_files = var_InheritString( obj, "concat-list" );
413 
414     if( unlikely( !p_sys ) )
415         goto error;
416 
417     if( archive_push_resource( p_sys, source, NULL ) )
418         goto error;
419 
420     if( psz_files )
421     {
422         for( char* state,
423                  * path = strtok_r( psz_files, ",", &state );
424              path; path = strtok_r(     NULL, ",", &state ) )
425         {
426             if( path == psz_files )
427                 continue;
428 
429             if( archive_push_resource( p_sys, NULL, path ) )
430                 goto error;
431         }
432 
433         free( psz_files );
434     }
435 
436     p_sys->source = source;
437     p_sys->p_obj = obj;
438 
439     return p_sys;
440 
441 error:
442     free( psz_files );
443     free( p_sys );
444     return NULL;
445 }
446 
probe(stream_t * source)447 static int probe( stream_t* source )
448 {
449     struct
450     {
451         uint16_t i_offset;
452         uint8_t  i_length;
453         char const * p_bytes;
454     } const magicbytes[] = {
455         /* keep heaviest at top */
456         { 257, 5, "ustar" },              //TAR
457         { 0,   7, "Rar!\x1A\x07" },       //RAR
458         { 0,   6, "7z\xBC\xAF\x27\x1C" }, //7z
459         { 0,   4, "xar!" },               //XAR
460         { 0,   4, "PK\x03\x04" },         //ZIP
461         { 0,   4, "PK\x05\x06" },         //ZIP
462         { 0,   4, "PK\x07\x08" },         //ZIP
463         { 2,   3, "-lh" },                //LHA/LHZ
464         { 0,   3, "\x1f\x8b\x08" },       // Gzip
465         { 0,   3, "PAX" },                //PAX
466         { 0,   6, "070707" },             //CPIO
467         { 0,   6, "070701" },             //CPIO
468         { 0,   6, "070702" },             //CPIO
469         { 0,   4, "MSCH" },               //CAB
470     };
471 
472     const uint8_t *p_peek;
473 
474     int i_peek = vlc_stream_Peek( source, &p_peek,
475       magicbytes[0].i_offset + magicbytes[0].i_length);
476 
477     for(unsigned i=0; i < ARRAY_SIZE( magicbytes ); i++)
478     {
479         if (i_peek < magicbytes[i].i_offset + magicbytes[i].i_length)
480             continue;
481 
482         if ( !memcmp(p_peek + magicbytes[i].i_offset,
483             magicbytes[i].p_bytes, magicbytes[i].i_length) )
484             return VLC_SUCCESS;
485     }
486 
487     return VLC_EGENERIC;
488 }
489 
490 /* ------------------------------------------------------------------------- */
491 
Control(stream_extractor_t * p_extractor,int i_query,va_list args)492 static int Control( stream_extractor_t* p_extractor, int i_query, va_list args )
493 {
494     private_sys_t* p_sys = p_extractor->p_sys;
495 
496     switch( i_query )
497     {
498         case STREAM_CAN_FASTSEEK:
499             *va_arg( args, bool* ) = false;
500             break;
501 
502         case STREAM_CAN_SEEK:
503             *va_arg( args, bool* ) = p_sys->b_seekable_source;
504             break;
505 
506         case STREAM_GET_SIZE:
507             if( p_sys->p_entry == NULL )
508                 return VLC_EGENERIC;
509 
510             if( !archive_entry_size_is_set( p_sys->p_entry ) )
511                 return VLC_EGENERIC;
512 
513             *va_arg( args, uint64_t* ) = archive_entry_size( p_sys->p_entry );
514             break;
515 
516         default:
517             return vlc_stream_vaControl( p_extractor->source, i_query, args );
518     }
519 
520     return VLC_SUCCESS;
521 }
522 
ReadDir(stream_directory_t * p_directory,input_item_node_t * p_node)523 static int ReadDir( stream_directory_t* p_directory, input_item_node_t* p_node )
524 {
525     private_sys_t* p_sys = p_directory->p_sys;
526     libarchive_t* p_arc = p_sys->p_archive;
527 
528     struct vlc_readdir_helper rdh;
529     vlc_readdir_helper_init( &rdh, p_directory, p_node);
530     struct archive_entry* entry;
531     int archive_status;
532 
533     while( !( archive_status = archive_read_next_header( p_arc, &entry ) ) )
534     {
535         if( archive_entry_filetype( entry ) == AE_IFDIR )
536             continue;
537 
538         char const* path = archive_entry_pathname( entry );
539 
540         if( unlikely( !path ) )
541             break;
542 
543         char*       mrl  = vlc_stream_extractor_CreateMRL( p_directory, path );
544 
545         if( unlikely( !mrl ) )
546             break;
547 
548         if( vlc_readdir_helper_additem( &rdh, mrl, path, NULL, ITEM_TYPE_FILE,
549                                         ITEM_LOCAL ) )
550         {
551             free( mrl );
552             break;
553         }
554         free( mrl );
555 
556         if( archive_read_data_skip( p_arc ) )
557             break;
558     }
559 
560     vlc_readdir_helper_finish( &rdh, archive_status == ARCHIVE_EOF );
561     return archive_status == ARCHIVE_EOF ? VLC_SUCCESS : VLC_EGENERIC;
562 }
563 
Read(stream_extractor_t * p_extractor,void * p_data,size_t i_size)564 static ssize_t Read( stream_extractor_t *p_extractor, void* p_data, size_t i_size )
565 {
566     char dummy_buffer[ 8192 ];
567 
568     private_sys_t* p_sys = p_extractor->p_sys;
569     libarchive_t* p_arc = p_sys->p_archive;
570     ssize_t       i_ret;
571 
572     if( p_sys->b_dead || p_sys->p_entry == NULL )
573         return 0;
574 
575     if( p_sys->b_eof )
576         return 0;
577 
578     i_ret = archive_read_data( p_arc,
579       p_data ? p_data :                        dummy_buffer,
580       p_data ? i_size : __MIN( i_size, sizeof( dummy_buffer ) ) );
581 
582     switch( i_ret )
583     {
584         case ARCHIVE_RETRY:
585         case ARCHIVE_FAILED:
586             msg_Dbg( p_extractor, "libarchive: %s", archive_error_string( p_arc ) );
587             goto eof;
588 
589         case ARCHIVE_WARN:
590             msg_Warn( p_extractor, "libarchive: %s", archive_error_string( p_arc ) );
591             goto eof;
592 
593         case ARCHIVE_FATAL:
594             msg_Err( p_extractor, "libarchive: %s", archive_error_string( p_arc ) );
595             goto fatal_error;
596     }
597 
598     p_sys->i_offset += i_ret;
599     return i_ret;
600 
601 fatal_error:
602     p_sys->b_dead = true;
603 
604 eof:
605     p_sys->b_eof = true;
606     return 0;
607 }
608 
archive_skip_decompressed(stream_extractor_t * p_extractor,uint64_t i_skip)609 static int archive_skip_decompressed( stream_extractor_t* p_extractor, uint64_t i_skip )
610 {
611     while( i_skip )
612     {
613         ssize_t i_read = Read( p_extractor, NULL, i_skip );
614 
615         if( i_read < 1 )
616             return VLC_EGENERIC;
617 
618         i_skip -= i_read;
619     }
620 
621     return VLC_SUCCESS;
622 }
623 
Seek(stream_extractor_t * p_extractor,uint64_t i_req)624 static int Seek( stream_extractor_t* p_extractor, uint64_t i_req )
625 {
626     private_sys_t* p_sys = p_extractor->p_sys;
627 
628     if( !p_sys->p_entry || !p_sys->b_seekable_source )
629         return VLC_EGENERIC;
630 
631     if( archive_entry_size_is_set( p_sys->p_entry ) &&
632         (uint64_t)archive_entry_size( p_sys->p_entry ) <= i_req )
633     {
634         p_sys->b_eof = true;
635         return VLC_SUCCESS;
636     }
637 
638     p_sys->b_eof = false;
639 
640     if( !p_sys->b_seekable_archive || p_sys->b_dead
641       || archive_seek_data( p_sys->p_archive, i_req, SEEK_SET ) < 0 )
642     {
643         msg_Dbg( p_extractor,
644             "intrinsic seek failed: '%s' (falling back to dumb seek)",
645             archive_error_string( p_sys->p_archive ) );
646 
647         uint64_t i_offset = p_sys->i_offset;
648         uint64_t i_skip   = i_req - i_offset;
649 
650         /* RECREATE LIBARCHIE HANDLE IF WE ARE SEEKING BACKWARDS */
651 
652         if( i_req < i_offset )
653         {
654             if( archive_extractor_reset( p_extractor ) )
655             {
656                 msg_Err( p_extractor, "unable to reset libarchive handle" );
657                 return VLC_EGENERIC;
658             }
659 
660             i_skip = i_req;
661             i_offset = 0;
662         }
663 
664         if( archive_skip_decompressed( p_extractor, i_skip ) )
665             msg_Dbg( p_extractor, "failed to skip to seek position" );
666     }
667 
668     p_sys->i_offset = i_req;
669     return VLC_SUCCESS;
670 }
671 
672 
CommonClose(private_sys_t * p_sys)673 static void CommonClose( private_sys_t* p_sys )
674 {
675     p_sys->b_dead = true;
676     archive_clean( p_sys );
677 
678     for( size_t i = 0; i < p_sys->i_callback_data; ++i )
679     {
680         free( p_sys->pp_callback_data[i]->psz_url );
681         free( p_sys->pp_callback_data[i] );
682     }
683 
684     free( p_sys->pp_callback_data );
685     free( p_sys );
686 }
687 
DirectoryClose(vlc_object_t * p_obj)688 static void DirectoryClose( vlc_object_t* p_obj )
689 {
690     stream_directory_t* p_directory = (void*)p_obj;
691     return CommonClose( p_directory->p_sys );
692 }
693 
ExtractorClose(vlc_object_t * p_obj)694 static void ExtractorClose( vlc_object_t* p_obj )
695 {
696     stream_extractor_t* p_extractor = (void*)p_obj;
697     return CommonClose( p_extractor->p_sys );
698 }
699 
CommonOpen(vlc_object_t * p_obj,stream_t * source)700 static private_sys_t* CommonOpen( vlc_object_t* p_obj, stream_t* source  )
701 {
702     if( probe( source ) )
703         return NULL;
704 
705     private_sys_t* p_sys = setup( p_obj, source );
706 
707     if( p_sys == NULL )
708         return NULL;
709 
710     if( archive_init( p_sys, source ) )
711     {
712         CommonClose( p_sys );
713         return NULL;
714     }
715 
716     return p_sys;
717 }
718 
DirectoryOpen(vlc_object_t * p_obj)719 static int DirectoryOpen( vlc_object_t* p_obj )
720 {
721     stream_directory_t* p_directory = (void*)p_obj;
722     private_sys_t* p_sys = CommonOpen( p_obj, p_directory->source );
723 
724     if( p_sys == NULL )
725         return VLC_EGENERIC;
726 
727     p_directory->p_sys = p_sys;
728     p_directory->pf_readdir = ReadDir;
729 
730     return VLC_SUCCESS;
731 }
732 
ExtractorOpen(vlc_object_t * p_obj)733 static int ExtractorOpen( vlc_object_t* p_obj )
734 {
735     stream_extractor_t* p_extractor = (void*)p_obj;
736     private_sys_t* p_sys = CommonOpen( p_obj, p_extractor->source );
737 
738     if( p_sys == NULL )
739         return VLC_EGENERIC;
740 
741     if( archive_seek_subentry( p_sys, p_extractor->identifier ) )
742     {
743         CommonClose( p_sys );
744         return VLC_EGENERIC;
745     }
746 
747     p_extractor->p_sys = p_sys;
748     p_extractor->pf_read = Read;
749     p_extractor->pf_control = Control;
750     p_extractor->pf_seek = Seek;
751 
752     return VLC_SUCCESS;
753 }
754