naev 0.12.5
physfs_archiver_blacklist.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10
11#include "naev.h"
12#define PCRE2_CODE_UNIT_WIDTH 8
13#include "physfs.h"
14#include <pcre2.h>
15
16#include "array.h"
17#include "log.h"
18
19#define BLACKLIST_FILENAME "naev.BLACKLIST"
20
24typedef struct BlkFile_ {
25 char *dirname;
26 char *filename;
27} BlkFile;
28
29static pcre2_code *blk_re = NULL;
30static pcre2_match_data *blk_match =
31 NULL;
32static char **blk_blacklists_re = NULL;
33static pcre2_code *wht_re = NULL;
34static pcre2_match_data *wht_match =
35 NULL;
36static char **wht_blacklists_re = NULL;
37static char **blk_blacklists =
38 NULL;
39static char **blk_dirnames = NULL;
41static BlkFile *blk_fs =
42 NULL;
43
44/*
45 * Prototypes.
46 */
47static PHYSFS_Io *blk_unsupportedIO( void *opaque, const char *filename );
48static int blk_unsupported( void *opaque, const char *name );
49static void *blk_openArchive( PHYSFS_Io *io, const char *name, int forWrite,
50 int *claimed );
51static PHYSFS_EnumerateCallbackResult
52blk_enumerate( void *opaque, const char *dirname, PHYSFS_EnumerateCallback cb,
53 const char *origdir, void *callbackdata );
54static PHYSFS_Io *blk_openRead( void *opaque, const char *fnm );
55static int blk_stat( void *opaque, const char *fn, PHYSFS_Stat *stat );
56static void blk_closeArchive( void *opaque );
57
61static const PHYSFS_Archiver blk_archiver = {
62 .version = 0,
63 .info =
64 {
65 .extension = "BLACKLIST",
66 .description = "Naev blacklist archiver.",
67 .author = "Naev DevTeam",
68 .url = "https://naev.org",
69 .supportsSymlinks = 0,
70 },
71 .openArchive = blk_openArchive,
72 .enumerate = blk_enumerate,
73 .openRead = blk_openRead,
74 .openWrite = blk_unsupportedIO,
75 .openAppend = blk_unsupportedIO,
76 .remove = blk_unsupported,
77 .mkdir = blk_unsupported,
78 .stat = blk_stat,
79 .closeArchive = blk_closeArchive,
80};
81
82static PHYSFS_sint64 blk_read( struct PHYSFS_Io *io, void *buf,
83 PHYSFS_uint64 len );
84static PHYSFS_sint64 blk_write( struct PHYSFS_Io *io, const void *buffer,
85 PHYSFS_uint64 len );
86static int blk_seek( struct PHYSFS_Io *io, PHYSFS_uint64 offset );
87static PHYSFS_sint64 blk_tell( struct PHYSFS_Io *io );
88static PHYSFS_sint64 blk_length( struct PHYSFS_Io *io );
89static struct PHYSFS_Io *blk_duplicate( struct PHYSFS_Io *io );
90static int blk_flush( struct PHYSFS_Io *io );
91static void blk_destroy( struct PHYSFS_Io *io );
92
96static const PHYSFS_Io blk_emptyio = {
97 .version = 0,
98 .opaque = NULL,
99 .read = blk_read,
100 .write = blk_write,
101 .seek = blk_seek,
102 .tell = blk_tell,
103 .length = blk_length,
104 .duplicate = blk_duplicate,
105 .flush = blk_flush,
106 .destroy = blk_destroy,
107};
108
112static const PHYSFS_Stat blk_emptystat = {
113 .filesize = 0,
114 .modtime = 0,
115 .createtime = 0,
116 .accesstime = 0,
117 .filetype = PHYSFS_FILETYPE_REGULAR,
118 .readonly = 1,
119};
120
124static const PHYSFS_Stat blk_emptystatdir = {
125 .filesize = 0,
126 .modtime = 0,
127 .createtime = 0,
128 .accesstime = 0,
129 .filetype = PHYSFS_FILETYPE_DIRECTORY,
130 .readonly = 1,
131};
132
137static int blk_enumerateCallback( void *data, const char *origdir,
138 const char *fname )
139{
140 char *path;
141 const char *fmt;
142 size_t dir_len;
143 PHYSFS_Stat stat;
144
145 dir_len = strlen( origdir );
146 fmt = ( ( dir_len && origdir[dir_len - 1] == '/' ) || dir_len == 0 )
147 ? "%s%s"
148 : "%s/%s";
149 SDL_asprintf( &path, fmt, origdir, fname );
150 if ( !PHYSFS_stat( path, &stat ) ) {
151 PHYSFS_ErrorCode err = PHYSFS_getLastErrorCode();
152 if ( err != PHYSFS_ERR_BAD_FILENAME )
153 WARN( _( "PhysicsFS: Cannot stat %s: %s" ), path,
154 _( PHYSFS_getErrorByCode( err ) ) );
155 free( path );
156 } else if ( stat.filetype == PHYSFS_FILETYPE_REGULAR ) {
157 int rc;
158
159 /* First try whitelist. */
160 if ( wht_re != NULL ) {
161 rc = pcre2_match( wht_re, (PCRE2_SPTR)path, strlen( path ), 0, 0,
162 wht_match, NULL );
163 if ( rc < 0 ) {
164 switch ( rc ) {
165 case PCRE2_ERROR_NOMATCH:
166 break;
167 default:
168 WARN( _( "Matching error %d" ), rc );
169 break;
170 }
171 } else if ( rc > 0 ) {
172 free( path );
173 return PHYSFS_ENUM_OK;
174 }
175 }
176
177 /* Only run matches if not found in whitelist. */
178 rc = pcre2_match( blk_re, (PCRE2_SPTR)path, strlen( path ), 0, 0,
179 blk_match, NULL );
180 if ( rc < 0 ) {
181 switch ( rc ) {
182 case PCRE2_ERROR_NOMATCH:
183 free( path );
184 break;
185 default:
186 WARN( _( "Matching error %d" ), rc );
187 free( path );
188 break;
189 }
190 } else if ( rc == 0 )
191 free( path );
192 else {
193 int *added = data;
194 int f = -1;
195 BlkFile bf = {
196 .filename = strdup( fname ),
197 .dirname = strdup( origdir ),
198 };
199 array_push_back( &blk_fs, bf );
201
202 for ( int i = 0; i < array_size( blk_dirnames ); i++ ) {
203 if ( strcmp( blk_dirnames[i], origdir ) == 0 ) {
204 f = i;
205 break;
206 }
207 }
208 if ( f < 0 )
209 array_push_back( &blk_dirnames, strdup( origdir ) );
210
211 if ( added )
212 *added = 1;
213 }
214 } else if ( stat.filetype == PHYSFS_FILETYPE_DIRECTORY ) {
215 int added;
216 PHYSFS_enumerate( path, blk_enumerateCallback, &added );
217 if ( added ) {
218 BlkFile bf = {
219 .filename = strdup( fname ),
220 .dirname = strdup( origdir ),
221 };
222 array_push_back( &blk_fs, bf );
223 array_push_back( &blk_dirnames, strdup( origdir ) );
224 }
225 free( path );
226 } else
227 free( path );
228 return PHYSFS_ENUM_OK;
229}
230
231static pcre2_code *regex_make( char *const *lst )
232{
233 char buf[STRMAX];
234 pcre2_code *re;
235 int l = 0;
236 int errornumber;
237 PCRE2_SIZE erroroffset;
238
239 if ( array_size( lst ) == 0 )
240 return NULL;
241
242 /* Set up the string. */
243 for ( int i = 0; i < array_size( lst ); i++ )
244 l += scnprintf( &buf[l], sizeof( buf ) - l - 1, "%s%s",
245 ( i == 0 ) ? "" : "|", lst[i] );
246
247 /* Try to compile the regex. */
248 re = pcre2_compile( (PCRE2_SPTR)buf, PCRE2_ZERO_TERMINATED, 0, &errornumber,
249 &erroroffset, NULL );
250 if ( re == NULL ) {
251 PCRE2_UCHAR buffer[256];
252 pcre2_get_error_message( errornumber, buffer, sizeof( buffer ) );
253 WARN( _( "Blacklist PCRE2 compilation failed at offset %d: %s" ),
254 (int)erroroffset, buffer );
255 return NULL;
256 }
257 return re;
258}
259
266int blacklist_init( void )
267{
268 /* No blacklist, ignore. */
269 if ( array_size( blk_blacklists_re ) <= 0 )
270 return 0;
271
272 /* Set up black list. */
273 blk_re = regex_make( blk_blacklists_re );
274 if ( blk_re == NULL )
275 return -1;
276
277 /* White list can be NULL. */
278 wht_re = regex_make( wht_blacklists_re );
279
280 /* Prepare the match data. */
281 blk_match = pcre2_match_data_create_from_pattern( blk_re, NULL );
282 if ( wht_re != NULL )
283 wht_match = pcre2_match_data_create_from_pattern( wht_re, NULL );
284
285 /* Find the files and match. */
286 blk_blacklists = array_create( char * );
287 blk_dirnames = array_create( char *);
289 PHYSFS_enumerate( "", blk_enumerateCallback, NULL );
290 qsort( blk_blacklists, array_size( blk_blacklists ), sizeof( char * ),
291 strsort );
292 qsort( blk_dirnames, array_size( blk_dirnames ), sizeof( char * ), strsort );
293
294 /* Free stuff up. */
295 pcre2_code_free( blk_re );
296 pcre2_match_data_free( blk_match );
297 if ( wht_re != NULL ) {
298 pcre2_code_free( wht_re );
299 pcre2_match_data_free( wht_match );
300 }
301
302 /* Check to see we actually have stuff to blacklist. */
303 if ( array_size( blk_blacklists ) <= 0 )
304 return 0;
305
306 /* Register archiver and load it from memory. */
307 PHYSFS_registerArchiver( &blk_archiver );
308 int ret =
309 PHYSFS_mountMemory( &blk_archiver, 0, NULL, BLACKLIST_FILENAME, NULL, 0 );
310 if ( !ret )
311 WARN( _( "PhysicsFS: %s" ),
312 _( PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
313 return !ret;
314}
315
319int blacklist_append( const char *path )
320{
321 if ( blk_blacklists_re == NULL )
323
324 for ( int i = 0; i < array_size( blk_blacklists_re ); i++ )
325 if ( strcmp( blk_blacklists_re[i], path ) == 0 )
326 return 0;
327 array_push_back( &blk_blacklists_re, strdup( path ) );
328 return 0;
329}
330
334int whitelist_append( const char *path )
335{
336 if ( wht_blacklists_re == NULL )
338
339 for ( int i = 0; i < array_size( wht_blacklists_re ); i++ )
340 if ( strcmp( wht_blacklists_re[i], path ) == 0 )
341 return 0;
342 array_push_back( &wht_blacklists_re, strdup( path ) );
343 return 0;
344}
345
349void blacklist_exit( void )
350{
351 for ( int i = 0; i < array_size( blk_blacklists_re ); i++ )
352 free( blk_blacklists_re[i] );
354 blk_blacklists_re = NULL;
355
356 for ( int i = 0; i < array_size( wht_blacklists_re ); i++ )
357 free( wht_blacklists_re[i] );
359 wht_blacklists_re = NULL;
360
361 for ( int i = 0; i < array_size( blk_fs ); i++ ) {
362 free( blk_fs[i].filename );
363 free( blk_fs[i].dirname );
364 }
366 blk_fs = NULL;
367
368 for ( int i = 0; i < array_size( blk_blacklists ); i++ )
369 free( blk_blacklists[i] );
371 blk_blacklists = NULL;
372
373 for ( int i = 0; i < array_size( blk_dirnames ); i++ )
374 free( blk_dirnames[i] );
376 blk_dirnames = NULL;
377}
378
382static int blk_matches( char **lst, const char *filename )
383{
384 const char *str = bsearch( &filename, lst, array_size( lst ),
385 sizeof( const char * ), strsort );
386 return ( str != NULL );
387}
388
389static PHYSFS_Io *blk_unsupportedIO( void *opaque, const char *filename )
390{
391 (void)opaque;
392 (void)filename;
393 return NULL;
394}
395
396static int blk_unsupported( void *opaque, const char *filename )
397{
398 (void)opaque;
399 (void)filename;
400 return 0;
401}
402
403static void *blk_openArchive( PHYSFS_Io *io, const char *name, int forWrite,
404 int *claimed )
405{
406 (void)io;
407 (void)forWrite;
408 if ( strcmp( name, BLACKLIST_FILENAME ) == 0 ) {
409 *claimed = 1;
410 return &blk_re; /* Has to be non-NULL. */
411 }
412 return NULL;
413}
414
415static PHYSFS_EnumerateCallbackResult
416blk_enumerate( void *opaque, const char *dirname, PHYSFS_EnumerateCallback cb,
417 const char *origdir, void *callbackdata )
418{
419 (void)dirname;
420 (void)opaque;
421 PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
422
423 for ( int i = 0; i < array_size( blk_fs ); i++ ) {
424 if ( strcmp( blk_fs[i].dirname, origdir ) != 0 )
425 continue;
426
427 retval = cb( callbackdata, origdir, blk_fs[i].filename );
428 if ( retval == PHYSFS_ENUM_ERROR )
429 PHYSFS_setErrorCode( PHYSFS_ERR_APP_CALLBACK );
430 if ( retval != PHYSFS_ENUM_OK )
431 break;
432 }
433
434 return retval;
435}
436
437static PHYSFS_Io *blk_openRead( void *opaque, const char *fnm )
438{
439 (void)opaque;
440 if ( blk_matches( blk_blacklists, fnm ) ) {
441 PHYSFS_Io *io = malloc( sizeof( PHYSFS_Io ) );
442 *io = blk_emptyio;
443 return io;
444 }
445 return NULL;
446}
447
448static int blk_stat( void *opaque, const char *fn, PHYSFS_Stat *stat )
449{
450 (void)opaque;
451 if ( blk_matches( blk_dirnames, fn ) ) {
452 *stat = blk_emptystatdir;
453 return 1;
454 }
455 if ( blk_matches( blk_blacklists, fn ) ) {
456 *stat = blk_emptystat;
457 return 1;
458 }
459 return 0;
460}
461
462static void blk_closeArchive( void *opaque )
463{
464 (void)opaque;
465}
466
467static PHYSFS_sint64 blk_read( struct PHYSFS_Io *io, void *buf,
468 PHYSFS_uint64 len )
469{
470 (void)io;
471 (void)buf;
472 (void)len;
473 return 0;
474}
475
476static PHYSFS_sint64 blk_write( struct PHYSFS_Io *io, const void *buffer,
477 PHYSFS_uint64 len )
478{
479 (void)io;
480 (void)buffer;
481 (void)len;
482 return -1;
483}
484
485static int blk_seek( struct PHYSFS_Io *io, PHYSFS_uint64 offset )
486{
487 (void)io;
488 (void)offset;
489 return -1;
490}
491
492static PHYSFS_sint64 blk_tell( struct PHYSFS_Io *io )
493{
494 (void)io;
495 return 0;
496}
497
498static PHYSFS_sint64 blk_length( struct PHYSFS_Io *io )
499{
500 (void)io;
501 return 0;
502}
503
504static struct PHYSFS_Io *blk_duplicate( struct PHYSFS_Io *io )
505{
506 return io;
507}
508
509static int blk_flush( struct PHYSFS_Io *io )
510{
511 (void)io;
512 return 0;
513}
514
515static void blk_destroy( struct PHYSFS_Io *io )
516{
517 free( io );
518}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
Header file with generic functions and naev-specifics.
int strsort(const void *p1, const void *p2)
Sort function for sorting strings with qsort().
Definition nstring.c:83
int scnprintf(char *text, size_t maxlen, const char *fmt,...)
Like snprintf(), but returns the number of characters ACTUALLY "printed" into the buffer....
Definition nstring.c:102
static pcre2_code * blk_re
static BlkFile * blk_fs
int blacklist_init(void)
Initializes the blacklist system if necessary. If no plugin is blacklisting, it will not do anything.
static const PHYSFS_Stat blk_emptystat
Stat for an empty regular file.
static int blk_matches(char **lst, const char *filename)
Tries to match a string in an array of strings that are sorted.
int blacklist_append(const char *path)
Appends a regex string to be blacklisted.
int whitelist_append(const char *path)
Appends a regex string to be whitelisted.
static char ** blk_dirnames
static pcre2_code * wht_re
static const PHYSFS_Stat blk_emptystatdir
Stat for a fake directory.
static char ** blk_blacklists
static char ** blk_blacklists_re
static const PHYSFS_Io blk_emptyio
Mimicks an empty file.
void blacklist_exit(void)
Exits the blacklist system and cleans up as necessary.
static char ** wht_blacklists_re
static pcre2_match_data * wht_match
static pcre2_match_data * blk_match
static const PHYSFS_Archiver blk_archiver
The archiver for blacklists.
static int blk_enumerateCallback(void *data, const char *origdir, const char *fname)
Used to build the blacklist and pseudo filesystem when iterating over real files.
Represents a file in a directory. Used to enumerate files.