naev 0.11.5
physfs_archiver_blacklist.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
11#include "naev.h"
12#define PCRE2_CODE_UNIT_WIDTH 8
13#include <pcre2.h>
14#include "physfs.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 = NULL;
31static char **blk_blacklists_re = NULL;
32static char **blk_blacklists = NULL;
33static char **blk_dirnames = NULL;
34static BlkFile *blk_fs = NULL;
36/*
37 * Prototypes.
38 */
39static PHYSFS_Io *blk_unsupportedIO( void *opaque, const char *filename );
40static int blk_unsupported( void *opaque, const char *name );
41static void *blk_openArchive( PHYSFS_Io *io, const char *name, int forWrite, int *claimed );
42static PHYSFS_EnumerateCallbackResult blk_enumerate( void *opaque, const char *dirname, PHYSFS_EnumerateCallback cb, const char *origdir, void *callbackdata );
43static PHYSFS_Io *blk_openRead(void *opaque, const char *fnm);
44static int blk_stat( void *opaque, const char *fn, PHYSFS_Stat *stat );
45static void blk_closeArchive( void *opaque );
46
50static const PHYSFS_Archiver blk_archiver = {
51 .version = 0,
52 .info = {
53 .extension = "BLACKLIST",
54 .description = "Naev blacklist archiver.",
55 .author = "Naev DevTeam",
56 .url = "https://naev.org",
57 .supportsSymlinks = 0,
58 },
59 .openArchive = blk_openArchive,
60 .enumerate = blk_enumerate,
61 .openRead = blk_openRead,
62 .openWrite = blk_unsupportedIO,
63 .openAppend = blk_unsupportedIO,
64 .remove = blk_unsupported,
65 .mkdir = blk_unsupported,
66 .stat = blk_stat,
67 .closeArchive = blk_closeArchive,
68};
69
70static PHYSFS_sint64 blk_read( struct PHYSFS_Io *io, void *buf, PHYSFS_uint64 len );
71static PHYSFS_sint64 blk_write( struct PHYSFS_Io *io, const void *buffer, PHYSFS_uint64 len );
72static int blk_seek( struct PHYSFS_Io *io, PHYSFS_uint64 offset );
73static PHYSFS_sint64 blk_tell( struct PHYSFS_Io *io );
74static PHYSFS_sint64 blk_length( struct PHYSFS_Io *io );
75static struct PHYSFS_Io *blk_duplicate( struct PHYSFS_Io *io );
76static int blk_flush( struct PHYSFS_Io *io );
77static void blk_destroy( struct PHYSFS_Io *io );
78
82static const PHYSFS_Io blk_emptyio = {
83 .version = 0,
84 .opaque = NULL,
85 .read = blk_read,
86 .write = blk_write,
87 .seek = blk_seek,
88 .tell = blk_tell,
89 .length = blk_length,
90 .duplicate = blk_duplicate,
91 .flush = blk_flush,
92 .destroy = blk_destroy,
93};
94
98static const PHYSFS_Stat blk_emptystat = {
99 .filesize = 0,
100 .modtime = 0,
101 .createtime = 0,
102 .accesstime = 0,
103 .filetype = PHYSFS_FILETYPE_REGULAR,
104 .readonly = 1,
105};
106
110static const PHYSFS_Stat blk_emptystatdir = {
111 .filesize = 0,
112 .modtime = 0,
113 .createtime = 0,
114 .accesstime = 0,
115 .filetype = PHYSFS_FILETYPE_DIRECTORY,
116 .readonly = 1,
117};
118
122static int blk_enumerateCallback( void* data, const char* origdir, const char* fname )
123{
124 char *path;
125 const char *fmt;
126 size_t dir_len;
127 PHYSFS_Stat stat;
128
129 dir_len = strlen( origdir );
130 fmt = ((dir_len && origdir[dir_len-1]=='/') || dir_len==0) ? "%s%s" : "%s/%s";
131 SDL_asprintf( &path, fmt, origdir, fname );
132 if (!PHYSFS_stat( path, &stat )) {
133 PHYSFS_ErrorCode err = PHYSFS_getLastErrorCode();
134 if (err!=PHYSFS_ERR_BAD_FILENAME)
135 WARN( _("PhysicsFS: Cannot stat %s: %s"), path,
136 _(PHYSFS_getErrorByCode( err ) ) );
137 free( path );
138 }
139 else if (stat.filetype == PHYSFS_FILETYPE_REGULAR) {
140 /* Iterate and build up matches. */
141 int rc = pcre2_match( blk_re, (PCRE2_SPTR)path, strlen(path), 0, 0, blk_match, NULL );
142 if (rc < 0) {
143 switch (rc) {
144 case PCRE2_ERROR_NOMATCH:
145 free( path );
146 break;
147 default:
148 WARN(_("Matching error %d"), rc );
149 free( path );
150 break;
151 }
152 }
153 else if (rc == 0)
154 free( path );
155 else {
156 int *added = data;
157 int f = -1;
158 BlkFile bf = {
159 .filename= strdup(fname),
160 .dirname = strdup(origdir),
161 };
162 array_push_back( &blk_fs, bf );
164
165 for (int i=0; i<array_size(blk_dirnames); i++) {
166 if (strcmp(blk_dirnames[i],origdir)==0) {
167 f = i;
168 break;
169 }
170 }
171 if (f<0)
172 array_push_back( &blk_dirnames, strdup(origdir) );
173
174 if (added)
175 *added = 1;
176 }
177 }
178 else if (stat.filetype == PHYSFS_FILETYPE_DIRECTORY ) {
179 int added;
180 PHYSFS_enumerate( path, blk_enumerateCallback, &added );
181 if (added) {
182 BlkFile bf = {
183 .filename = strdup(fname),
184 .dirname = strdup(origdir),
185 };
186 array_push_back( &blk_fs, bf );
187 array_push_back( &blk_dirnames, strdup(origdir) );
188 }
189 free( path );
190 }
191 else
192 free( path );
193 return PHYSFS_ENUM_OK;
194}
195
202{
203 char buf[STRMAX];
204 int errornumber, l;
205 PCRE2_SIZE erroroffset;
206
207 /* No blacklist, ignore. */
209 return 0;
210
211 /* Set up the string. */
212 l = 0;
213 for (int i=0; i<array_size(blk_blacklists_re); i++)
214 l += scnprintf( &buf[l], sizeof(buf)-l-1, "%s%s", (i==0) ? "" : "|", blk_blacklists_re[i] );
215
216 /* Try to compile the regex. */
217 blk_re = pcre2_compile( (PCRE2_SPTR)buf, PCRE2_ZERO_TERMINATED, 0, &errornumber, &erroroffset, NULL );
218 if (blk_re == NULL) {
219 PCRE2_UCHAR buffer[256];
220 pcre2_get_error_message( errornumber, buffer, sizeof(buffer) );
221 WARN(_("Blacklist PCRE2 compilation failed at offset %d: %s"), (int)erroroffset, buffer );
222 return -1;;
223 }
224
225 /* Prepare the match data. */
226 blk_match = pcre2_match_data_create_from_pattern( blk_re, NULL );
227
228 /* Find the files and match. */
229 blk_blacklists = array_create( char * );
230 blk_dirnames = array_create( char * );
232 PHYSFS_enumerate( "", blk_enumerateCallback, NULL );
233 qsort( blk_blacklists, array_size(blk_blacklists), sizeof(char*), strsort );
234 qsort( blk_dirnames, array_size(blk_dirnames), sizeof(char*), strsort );
235
236 /* Free stuff up. */
237 pcre2_code_free( blk_re );
238 pcre2_match_data_free( blk_match );
239
240 /* Check to see we actually have stuff to blacklist. */
241 if (array_size(blk_blacklists) <= 0)
242 return 0;
243
244 /* Register archiver and load it from memory. */
245 PHYSFS_registerArchiver( &blk_archiver );
246 int ret = PHYSFS_mountMemory( &blk_archiver, 0, NULL, BLACKLIST_FILENAME, NULL, 0 );
247 if (!ret)
248 WARN( _("PhysicsFS: %s"), _(PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
249 return !ret;
250}
251
255int blacklist_append( const char *path )
256{
257 if (blk_blacklists_re == NULL)
259
260 for (int i=0; i<array_size(blk_blacklists_re); i++)
261 if (strcmp( blk_blacklists_re[i], path )==0)
262 return 0;
263 array_push_back( &blk_blacklists_re, strdup(path) );
264 return 0;
265}
266
270void blacklist_exit (void)
271{
272 for (int i=0; i<array_size(blk_blacklists_re); i++)
273 free( blk_blacklists_re[i] );
275 blk_blacklists_re = NULL;
276
277 for (int i=0; i<array_size(blk_fs); i++) {
278 free( blk_fs[i].filename );
279 free( blk_fs[i].dirname );
280 }
282 blk_fs = NULL;
283
284 for (int i=0; i<array_size(blk_blacklists); i++)
285 free( blk_blacklists[i] );
287 blk_blacklists = NULL;
288
289 for (int i=0; i<array_size(blk_dirnames); i++)
290 free( blk_dirnames[i] );
292 blk_dirnames = NULL;
293}
294
298static int blk_matches( char **lst, const char *filename )
299{
300 const char *str = bsearch( &filename, lst, array_size(lst), sizeof(const char*), strsort );
301 return (str!=NULL);
302}
303
304static PHYSFS_Io *blk_unsupportedIO( void *opaque, const char *filename )
305{
306 (void) opaque;
307 (void) filename;
308 return NULL;
309}
310
311static int blk_unsupported( void *opaque, const char *filename )
312{
313 (void) opaque;
314 (void) filename;
315 return 0;
316}
317
318static void *blk_openArchive( PHYSFS_Io *io, const char *name, int forWrite, int *claimed )
319{
320 (void) io;
321 (void) forWrite;
322 if (strcmp(name,BLACKLIST_FILENAME)==0) {
323 *claimed = 1;
324 return &blk_re; /* Has to be non-NULL. */
325 }
326 return NULL;
327}
328
329static PHYSFS_EnumerateCallbackResult blk_enumerate( void *opaque, const char *dirname, PHYSFS_EnumerateCallback cb, const char *origdir, void *callbackdata )
330{
331 (void) dirname;
332 (void) opaque;
333 PHYSFS_EnumerateCallbackResult retval = PHYSFS_ENUM_OK;
334
335 for (int i=0; i<array_size(blk_fs); i++) {
336 if (strcmp( blk_fs[i].dirname, origdir )!=0)
337 continue;
338
339 retval = cb( callbackdata, origdir, blk_fs[i].filename );
340 if (retval == PHYSFS_ENUM_ERROR)
341 PHYSFS_setErrorCode(PHYSFS_ERR_APP_CALLBACK);
342 if (retval != PHYSFS_ENUM_OK)
343 break;
344 }
345
346 return retval;
347}
348
349static PHYSFS_Io *blk_openRead( void *opaque, const char *fnm )
350{
351 (void) opaque;
352 if (blk_matches( blk_blacklists, fnm )) {
353 PHYSFS_Io *io = malloc( sizeof(PHYSFS_Io) );
354 *io = blk_emptyio;
355 return io;
356 }
357 return NULL;
358}
359
360static int blk_stat( void *opaque, const char *fn, PHYSFS_Stat *stat )
361{
362 (void) opaque;
363 if (blk_matches( blk_dirnames, fn )) {
364 *stat = blk_emptystatdir;
365 return 1;
366 }
367 if (blk_matches( blk_blacklists, fn )) {
368 *stat = blk_emptystat;
369 return 1;
370 }
371 return 0;
372}
373
374static void blk_closeArchive( void *opaque )
375{
376 (void) opaque;
377}
378
379static PHYSFS_sint64 blk_read( struct PHYSFS_Io *io, void *buf, PHYSFS_uint64 len )
380{
381 (void) io;
382 (void) buf;
383 (void) len;
384 return 0;
385}
386
387static PHYSFS_sint64 blk_write( struct PHYSFS_Io *io, const void *buffer, PHYSFS_uint64 len )
388{
389 (void) io;
390 (void) buffer;
391 (void) len;
392 return -1;
393}
394
395static int blk_seek( struct PHYSFS_Io *io, PHYSFS_uint64 offset )
396{
397 (void) io;
398 (void) offset;
399 return -1;
400}
401
402static PHYSFS_sint64 blk_tell( struct PHYSFS_Io *io )
403{
404 (void) io;
405 return 0;
406}
407
408static PHYSFS_sint64 blk_length( struct PHYSFS_Io *io )
409{
410 (void) io;
411 return 0;
412}
413
414static struct PHYSFS_Io *blk_duplicate( struct PHYSFS_Io *io )
415{
416 return io;
417}
418
419static int blk_flush( struct PHYSFS_Io *io )
420{
421 (void) io;
422 return 0;
423}
424
425static void blk_destroy( struct PHYSFS_Io *io )
426{
427 free( io );
428}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:158
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:168
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:129
#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:81
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:99
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.
static char ** blk_dirnames
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 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.