1 /*****************************************************************************
2 * cache.c: Plugins cache
3 *****************************************************************************
4 * Copyright (C) 2001-2007 VLC authors and VideoLAN
5 * $Id: a9ce8efa9fc06ac1a333a10af9a5f00571afbaca $
6 *
7 * Authors: Sam Hocevar <sam@zoy.org>
8 * Ethan C. Baldridge <BaldridgeE@cadmus.com>
9 * Hans-Peter Jansen <hpj@urpla.net>
10 * Gildas Bazin <gbazin@videolan.org>
11 *
12 * This program is free software; you can redistribute it and/or modify it
13 * under the terms of the GNU Lesser General Public License as published by
14 * the Free Software Foundation; either version 2.1 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public License
23 * along with this program; if not, write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
26
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <stdalign.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <unistd.h>
38 #include <assert.h>
39
40 #include <vlc_common.h>
41 #include <vlc_block.h>
42 #include "libvlc.h"
43
44 #include <vlc_plugin.h>
45 #include <errno.h>
46
47 #include "config/configuration.h"
48
49 #include <vlc_fs.h>
50
51 #include "modules/modules.h"
52
53
54 /*****************************************************************************
55 * Local prototypes
56 *****************************************************************************/
57 #ifdef HAVE_DYNAMIC_PLUGINS
58 /* Sub-version number
59 * (only used to avoid breakage in dev version when cache structure changes) */
60 #define CACHE_SUBVERSION_NUM 34
61
62 /* Cache filename */
63 #define CACHE_NAME "plugins.dat"
64 /* Magic for the cache filename */
65 #define CACHE_STRING "cache "PACKAGE_NAME" "PACKAGE_VERSION
66
67
vlc_cache_load_immediate(void * out,block_t * in,size_t size)68 static int vlc_cache_load_immediate(void *out, block_t *in, size_t size)
69 {
70 if (in->i_buffer < size)
71 return -1;
72
73 memcpy(out, in->p_buffer, size);
74 in->p_buffer += size;
75 in->i_buffer -= size;
76 return 0;
77 }
78
vlc_cache_load_bool(bool * out,block_t * in)79 static int vlc_cache_load_bool(bool *out, block_t *in)
80 {
81 unsigned char b;
82
83 if (vlc_cache_load_immediate(&b, in, 1) || b > 1)
84 return -1;
85
86 *out = b;
87 return 0;
88 }
89
vlc_cache_load_array(const void ** p,size_t size,size_t n,block_t * file)90 static int vlc_cache_load_array(const void **p, size_t size, size_t n,
91 block_t *file)
92 {
93 if (n == 0)
94 {
95 *p = NULL;
96 return 0;
97 }
98
99 if (unlikely(size * n < size))
100 return -1;
101
102 size *= n;
103
104 if (file->i_buffer < size)
105 return -1;
106
107 *p = file->p_buffer;
108 file->p_buffer += size;
109 file->i_buffer -= size;
110 return 0;
111 }
112
vlc_cache_load_string(const char ** restrict p,block_t * file)113 static int vlc_cache_load_string(const char **restrict p, block_t *file)
114 {
115 uint16_t size;
116
117 if (vlc_cache_load_immediate(&size, file, sizeof (size)) || size > 16384)
118 return -1;
119
120 if (size == 0)
121 {
122 *p = NULL;
123 return 0;
124 }
125
126 const char *str = (char *)file->p_buffer;
127
128 if (file->i_buffer < size || str[size - 1] != '\0')
129 return -1;
130
131 file->p_buffer += size;
132 file->i_buffer -= size;
133 *p = str;
134 return 0;
135 }
136
vlc_cache_load_align(size_t align,block_t * file)137 static int vlc_cache_load_align(size_t align, block_t *file)
138 {
139 assert(align > 0);
140
141 size_t skip = (-(uintptr_t)file->p_buffer) % align;
142 if (skip == 0)
143 return 0;
144
145 assert(skip < align);
146
147 if (file->i_buffer < skip)
148 return -1;
149
150 file->p_buffer += skip;
151 file->i_buffer -= skip;
152 assert((((uintptr_t)file->p_buffer) % align) == 0);
153 return 0;
154 }
155
156 #define LOAD_IMMEDIATE(a) \
157 if (vlc_cache_load_immediate(&(a), file, sizeof (a))) \
158 goto error
159 #define LOAD_FLAG(a) \
160 do \
161 { \
162 bool b; \
163 if (vlc_cache_load_bool(&b, file)) \
164 goto error; \
165 (a) = b; \
166 } while (0)
167 #define LOAD_ARRAY(a,n) \
168 do \
169 { \
170 const void *base; \
171 if (vlc_cache_load_array(&base, sizeof (*(a)), (n), file)) \
172 goto error; \
173 (a) = base; \
174 } while (0)
175 #define LOAD_STRING(a) \
176 if (vlc_cache_load_string(&(a), file)) \
177 goto error
178 #define LOAD_ALIGNOF(t) \
179 if (vlc_cache_load_align(alignof(t), file)) \
180 goto error
181
vlc_cache_load_config(module_config_t * cfg,block_t * file)182 static int vlc_cache_load_config(module_config_t *cfg, block_t *file)
183 {
184 LOAD_IMMEDIATE (cfg->i_type);
185 LOAD_IMMEDIATE (cfg->i_short);
186 LOAD_FLAG (cfg->b_advanced);
187 LOAD_FLAG (cfg->b_internal);
188 LOAD_FLAG (cfg->b_unsaveable);
189 LOAD_FLAG (cfg->b_safe);
190 LOAD_FLAG (cfg->b_removed);
191 LOAD_STRING (cfg->psz_type);
192 LOAD_STRING (cfg->psz_name);
193 LOAD_STRING (cfg->psz_text);
194 LOAD_STRING (cfg->psz_longtext);
195 LOAD_IMMEDIATE (cfg->list_count);
196
197 if (IsConfigStringType (cfg->i_type))
198 {
199 const char *psz;
200 LOAD_STRING(psz);
201 cfg->orig.psz = (char *)psz;
202 cfg->value.psz = (psz != NULL) ? strdup (cfg->orig.psz) : NULL;
203
204 if (cfg->list_count)
205 cfg->list.psz = xmalloc (cfg->list_count * sizeof (char *));
206 else
207 LOAD_STRING(cfg->list_cb_name);
208 for (unsigned i = 0; i < cfg->list_count; i++)
209 {
210 LOAD_STRING (cfg->list.psz[i]);
211 if (cfg->list.psz[i] == NULL /* NULL -> empty string */
212 && (cfg->list.psz[i] = calloc (1, 1)) == NULL)
213 goto error;
214 }
215 }
216 else
217 {
218 LOAD_IMMEDIATE (cfg->orig);
219 LOAD_IMMEDIATE (cfg->min);
220 LOAD_IMMEDIATE (cfg->max);
221 cfg->value = cfg->orig;
222
223 if (cfg->list_count)
224 {
225 LOAD_ALIGNOF(*cfg->list.i);
226 }
227 else
228 LOAD_STRING(cfg->list_cb_name);
229
230 LOAD_ARRAY(cfg->list.i, cfg->list_count);
231 }
232
233 cfg->list_text = xmalloc (cfg->list_count * sizeof (char *));
234 for (unsigned i = 0; i < cfg->list_count; i++)
235 {
236 LOAD_STRING (cfg->list_text[i]);
237 if (cfg->list_text[i] == NULL /* NULL -> empty string */
238 && (cfg->list_text[i] = calloc (1, 1)) == NULL)
239 goto error;
240 }
241
242 return 0;
243 error:
244 return -1; /* FIXME: leaks */
245 }
246
vlc_cache_load_plugin_config(vlc_plugin_t * plugin,block_t * file)247 static int vlc_cache_load_plugin_config(vlc_plugin_t *plugin, block_t *file)
248 {
249 uint16_t lines;
250
251 /* Calculate the structure length */
252 LOAD_IMMEDIATE (lines);
253
254 /* Allocate memory */
255 if (lines)
256 {
257 plugin->conf.items = calloc(sizeof (module_config_t), lines);
258 if (unlikely(plugin->conf.items == NULL))
259 {
260 plugin->conf.size = 0;
261 return -1;
262 }
263 }
264 else
265 plugin->conf.items = NULL;
266
267 plugin->conf.size = lines;
268
269 /* Do the duplication job */
270 for (size_t i = 0; i < lines; i++)
271 {
272 module_config_t *item = plugin->conf.items + i;
273
274 if (vlc_cache_load_config(item, file))
275 return -1;
276
277 if (CONFIG_ITEM(item->i_type))
278 {
279 plugin->conf.count++;
280 if (item->i_type == CONFIG_ITEM_BOOL)
281 plugin->conf.booleans++;
282 }
283 item->owner = plugin;
284 }
285
286 return 0;
287 error:
288 return -1; /* FIXME: leaks */
289 }
290
vlc_cache_load_module(vlc_plugin_t * plugin,block_t * file)291 static int vlc_cache_load_module(vlc_plugin_t *plugin, block_t *file)
292 {
293 module_t *module = vlc_module_create(plugin);
294 if (unlikely(module == NULL))
295 return -1;
296
297 LOAD_STRING(module->psz_shortname);
298 LOAD_STRING(module->psz_longname);
299 LOAD_STRING(module->psz_help);
300
301 LOAD_IMMEDIATE(module->i_shortcuts);
302 if (module->i_shortcuts > MODULE_SHORTCUT_MAX)
303 goto error;
304 else
305 {
306 module->pp_shortcuts =
307 xmalloc (sizeof (*module->pp_shortcuts) * module->i_shortcuts);
308 for (unsigned j = 0; j < module->i_shortcuts; j++)
309 LOAD_STRING(module->pp_shortcuts[j]);
310 }
311
312 LOAD_STRING(module->activate_name);
313 LOAD_STRING(module->deactivate_name);
314 LOAD_STRING(module->psz_capability);
315 LOAD_IMMEDIATE(module->i_score);
316 return 0;
317 error:
318 return -1;
319 }
320
vlc_cache_load_plugin(block_t * file)321 static vlc_plugin_t *vlc_cache_load_plugin(block_t *file)
322 {
323 vlc_plugin_t *plugin = vlc_plugin_create();
324 if (unlikely(plugin == NULL))
325 return NULL;
326
327 uint32_t modules;
328 LOAD_IMMEDIATE(modules);
329
330 for (size_t i = 0; i < modules; i++)
331 if (vlc_cache_load_module(plugin, file))
332 goto error;
333
334 if (vlc_cache_load_plugin_config(plugin, file))
335 goto error;
336
337 LOAD_STRING(plugin->textdomain);
338
339 const char *path;
340 LOAD_STRING(path);
341 if (path == NULL)
342 goto error;
343
344 plugin->path = strdup(path);
345 if (unlikely(plugin->path == NULL))
346 goto error;
347
348 LOAD_FLAG(plugin->unloadable);
349 LOAD_IMMEDIATE(plugin->mtime);
350 LOAD_IMMEDIATE(plugin->size);
351
352 if (plugin->textdomain != NULL)
353 vlc_bindtextdomain(plugin->textdomain);
354
355 return plugin;
356
357 error:
358 vlc_plugin_destroy(plugin);
359 return NULL;
360 }
361
362 /**
363 * Loads a plugins cache file.
364 *
365 * This function will load the plugin cache if present and valid. This cache
366 * will in turn be queried by AllocateAllPlugins() to see if it needs to
367 * actually load the dynamically loadable module.
368 * This allows us to only fully load plugins when they are actually used.
369 */
vlc_cache_load(vlc_object_t * p_this,const char * dir,block_t ** backingp)370 vlc_plugin_t *vlc_cache_load(vlc_object_t *p_this, const char *dir,
371 block_t **backingp)
372 {
373 char *psz_filename;
374
375 assert( dir != NULL );
376
377 if( asprintf( &psz_filename, "%s"DIR_SEP CACHE_NAME, dir ) == -1 )
378 return 0;
379
380 msg_Dbg( p_this, "loading plugins cache file %s", psz_filename );
381
382 block_t *file = block_FilePath(psz_filename, false);
383 if (file == NULL)
384 msg_Warn(p_this, "cannot read %s: %s", psz_filename,
385 vlc_strerror_c(errno));
386 free(psz_filename);
387 if (file == NULL)
388 return 0;
389
390 /* Check the file is a plugins cache */
391 char cachestr[sizeof (CACHE_STRING) - 1];
392
393 if (vlc_cache_load_immediate(cachestr, file, sizeof (cachestr))
394 || memcmp(cachestr, CACHE_STRING, sizeof (cachestr)))
395 {
396 msg_Warn( p_this, "This doesn't look like a valid plugins cache" );
397 block_Release(file);
398 return 0;
399 }
400
401 #ifdef DISTRO_VERSION
402 /* Check for distribution specific version */
403 char distrostr[sizeof (DISTRO_VERSION) - 1];
404
405 if (vlc_cache_load_immediate(distrostr, file, sizeof (distrostr))
406 || memcmp(distrostr, DISTRO_VERSION, sizeof (distrostr)))
407 {
408 msg_Warn( p_this, "This doesn't look like a valid plugins cache" );
409 block_Release(file);
410 return 0;
411 }
412 #endif
413
414 /* Check sub-version number */
415 uint32_t marker;
416
417 if (vlc_cache_load_immediate(&marker, file, sizeof (marker))
418 || marker != CACHE_SUBVERSION_NUM)
419 {
420 msg_Warn( p_this, "This doesn't look like a valid plugins cache "
421 "(corrupted header)" );
422 block_Release(file);
423 return 0;
424 }
425
426 /* Check header marker */
427 if (vlc_cache_load_immediate(&marker, file, sizeof (marker))
428 #ifdef DISTRO_VERSION
429 || marker != (sizeof (cachestr) + sizeof (distrostr) + sizeof (marker))
430 #else
431 || marker != (sizeof (cachestr) + sizeof (marker))
432 #endif
433 )
434 {
435 msg_Warn( p_this, "This doesn't look like a valid plugins cache "
436 "(corrupted header)" );
437 block_Release(file);
438 return 0;
439 }
440
441 vlc_plugin_t *cache = NULL;
442
443 while (file->i_buffer > 0)
444 {
445 vlc_plugin_t *plugin = vlc_cache_load_plugin(file);
446 if (plugin == NULL)
447 goto error;
448
449 if (unlikely(asprintf(&plugin->abspath, "%s" DIR_SEP "%s", dir,
450 plugin->path) == -1))
451 {
452 plugin->abspath = NULL;
453 vlc_plugin_destroy(plugin);
454 goto error;
455 }
456
457 plugin->next = cache;
458 cache = plugin;
459 }
460
461 file->p_next = *backingp;
462 *backingp = file;
463 return cache;
464
465 error:
466 msg_Warn( p_this, "plugins cache not loaded (corrupted)" );
467
468 /* TODO: cleanup */
469 block_Release(file);
470 return NULL;
471 }
472
473 #define SAVE_IMMEDIATE( a ) \
474 if (fwrite (&(a), sizeof(a), 1, file) != 1) \
475 goto error
476 #define SAVE_FLAG(a) \
477 do { \
478 char b = (a); \
479 SAVE_IMMEDIATE(b); \
480 } while (0)
481
CacheSaveString(FILE * file,const char * str)482 static int CacheSaveString (FILE *file, const char *str)
483 {
484 uint16_t size = (str != NULL) ? (strlen (str) + 1) : 0;
485
486 SAVE_IMMEDIATE (size);
487 if (size != 0 && fwrite(str, 1, size, file) != size)
488 {
489 error:
490 return -1;
491 }
492 return 0;
493 }
494
495 #define SAVE_STRING( a ) \
496 if (CacheSaveString (file, (a))) \
497 goto error
498
CacheSaveAlign(FILE * file,size_t align)499 static int CacheSaveAlign(FILE *file, size_t align)
500 {
501 assert(align > 0);
502
503 size_t skip = (-ftell(file)) % align;
504 if (skip == 0)
505 return 0;
506
507 assert(((ftell(file) + skip) % align) == 0);
508 return fseek(file, skip, SEEK_CUR);
509 }
510
511 #define SAVE_ALIGNOF(t) \
512 if (CacheSaveAlign(file, alignof (t))) \
513 goto error
514
CacheSaveConfig(FILE * file,const module_config_t * cfg)515 static int CacheSaveConfig (FILE *file, const module_config_t *cfg)
516 {
517 SAVE_IMMEDIATE (cfg->i_type);
518 SAVE_IMMEDIATE (cfg->i_short);
519 SAVE_FLAG (cfg->b_advanced);
520 SAVE_FLAG (cfg->b_internal);
521 SAVE_FLAG (cfg->b_unsaveable);
522 SAVE_FLAG (cfg->b_safe);
523 SAVE_FLAG (cfg->b_removed);
524 SAVE_STRING (cfg->psz_type);
525 SAVE_STRING (cfg->psz_name);
526 SAVE_STRING (cfg->psz_text);
527 SAVE_STRING (cfg->psz_longtext);
528 SAVE_IMMEDIATE (cfg->list_count);
529
530 if (IsConfigStringType (cfg->i_type))
531 {
532 SAVE_STRING (cfg->orig.psz);
533 if (cfg->list_count == 0)
534 SAVE_STRING(cfg->list_cb_name);
535
536 for (unsigned i = 0; i < cfg->list_count; i++)
537 SAVE_STRING (cfg->list.psz[i]);
538 }
539 else
540 {
541 SAVE_IMMEDIATE (cfg->orig);
542 SAVE_IMMEDIATE (cfg->min);
543 SAVE_IMMEDIATE (cfg->max);
544
545 if (cfg->list_count > 0)
546 {
547 SAVE_ALIGNOF(*cfg->list.i);
548 }
549 else
550 SAVE_STRING(cfg->list_cb_name);
551
552 for (unsigned i = 0; i < cfg->list_count; i++)
553 SAVE_IMMEDIATE (cfg->list.i[i]);
554 }
555 for (unsigned i = 0; i < cfg->list_count; i++)
556 SAVE_STRING (cfg->list_text[i]);
557
558 return 0;
559 error:
560 return -1;
561 }
562
CacheSaveModuleConfig(FILE * file,const vlc_plugin_t * plugin)563 static int CacheSaveModuleConfig(FILE *file, const vlc_plugin_t *plugin)
564 {
565 uint16_t lines = plugin->conf.size;
566
567 SAVE_IMMEDIATE (lines);
568
569 for (size_t i = 0; i < lines; i++)
570 if (CacheSaveConfig(file, plugin->conf.items + i))
571 goto error;
572
573 return 0;
574 error:
575 return -1;
576 }
577
CacheSaveModule(FILE * file,const module_t * module)578 static int CacheSaveModule(FILE *file, const module_t *module)
579 {
580 SAVE_STRING(module->psz_shortname);
581 SAVE_STRING(module->psz_longname);
582 SAVE_STRING(module->psz_help);
583 SAVE_IMMEDIATE(module->i_shortcuts);
584
585 for (size_t j = 0; j < module->i_shortcuts; j++)
586 SAVE_STRING(module->pp_shortcuts[j]);
587
588 SAVE_STRING(module->activate_name);
589 SAVE_STRING(module->deactivate_name);
590 SAVE_STRING(module->psz_capability);
591 SAVE_IMMEDIATE(module->i_score);
592 return 0;
593 error:
594 return -1;
595 }
596
CacheSaveBank(FILE * file,vlc_plugin_t * const * cache,size_t n)597 static int CacheSaveBank(FILE *file, vlc_plugin_t *const *cache, size_t n)
598 {
599 uint32_t i_file_size = 0;
600
601 /* Contains version number */
602 if (fputs (CACHE_STRING, file) == EOF)
603 goto error;
604 #ifdef DISTRO_VERSION
605 /* Allow binary maintaner to pass a string to detect new binary version*/
606 if (fputs( DISTRO_VERSION, file ) == EOF)
607 goto error;
608 #endif
609 /* Sub-version number (to avoid breakage in the dev version when cache
610 * structure changes) */
611 i_file_size = CACHE_SUBVERSION_NUM;
612 if (fwrite (&i_file_size, sizeof (i_file_size), 1, file) != 1 )
613 goto error;
614
615 /* Header marker */
616 i_file_size = ftell( file );
617 if (fwrite (&i_file_size, sizeof (i_file_size), 1, file) != 1)
618 goto error;
619
620 for (size_t i = 0; i < n; i++)
621 {
622 const vlc_plugin_t *plugin = cache[i];
623 uint32_t count = plugin->modules_count;
624
625 SAVE_IMMEDIATE(count);
626
627 for (module_t *module = plugin->module;
628 module != NULL;
629 module = module->next)
630 if (CacheSaveModule(file, module))
631 goto error;
632
633 /* Config stuff */
634 if (CacheSaveModuleConfig(file, plugin))
635 goto error;
636
637 /* Save common info */
638 SAVE_STRING(plugin->textdomain);
639 SAVE_STRING(plugin->path);
640 SAVE_FLAG(plugin->unloadable);
641 SAVE_IMMEDIATE(plugin->mtime);
642 SAVE_IMMEDIATE(plugin->size);
643 }
644
645 if (fflush (file)) /* flush libc buffers */
646 goto error;
647 return 0; /* success! */
648
649 error:
650 return -1;
651 }
652
653 /**
654 * Saves a module cache to disk, and release cache data from memory.
655 */
CacheSave(vlc_object_t * p_this,const char * dir,vlc_plugin_t * const * entries,size_t n)656 void CacheSave(vlc_object_t *p_this, const char *dir,
657 vlc_plugin_t *const *entries, size_t n)
658 {
659 char *filename = NULL, *tmpname = NULL;
660
661 if (asprintf (&filename, "%s"DIR_SEP CACHE_NAME, dir ) == -1)
662 goto out;
663
664 if (asprintf (&tmpname, "%s.%"PRIu32, filename, (uint32_t)getpid ()) == -1)
665 goto out;
666 msg_Dbg (p_this, "saving plugins cache %s", filename);
667
668 FILE *file = vlc_fopen (tmpname, "wb");
669 if (file == NULL)
670 {
671 if (errno != EACCES && errno != ENOENT)
672 msg_Warn (p_this, "cannot create %s: %s", tmpname,
673 vlc_strerror_c(errno));
674 goto out;
675 }
676
677 if (CacheSaveBank(file, entries, n))
678 {
679 msg_Warn (p_this, "cannot write %s: %s", tmpname,
680 vlc_strerror_c(errno));
681 clearerr (file);
682 fclose (file);
683 vlc_unlink (tmpname);
684 goto out;
685 }
686
687 #if !defined( _WIN32 ) && !defined( __OS2__ )
688 vlc_rename (tmpname, filename); /* atomically replace old cache */
689 fclose (file);
690 #else
691 vlc_unlink (filename);
692 fclose (file);
693 vlc_rename (tmpname, filename);
694 #endif
695 out:
696 free (filename);
697 free (tmpname);
698 }
699
700 /**
701 * Looks up a plugin file in a table of cached plugins.
702 */
vlc_cache_lookup(vlc_plugin_t ** cache,const char * path)703 vlc_plugin_t *vlc_cache_lookup(vlc_plugin_t **cache, const char *path)
704 {
705 vlc_plugin_t **pp = cache, *plugin;
706
707 while ((plugin = *pp) != NULL)
708 {
709 if (plugin->path != NULL && !strcmp(plugin->path, path))
710 {
711 *pp = plugin->next;
712 plugin->next = NULL;
713 return plugin;
714 }
715
716 pp = &plugin->next;
717 }
718
719 return NULL;
720 }
721 #endif /* HAVE_DYNAMIC_PLUGINS */
722