binary registry: Rewrite sanity check to actualy catch something.
[platform/upstream/gstreamer.git] / gst / gstregistrybinary.c
1 /* GStreamer
2  * Copyright (C) 2006 Josep Torra <josep@fluendo.com>
3  *               2006 Mathieu Garcia <matthieu@fluendo.com>
4  *               2006,2007 Stefan Kost <ensonic@users.sf.net>
5  *               2008 Sebastian Dröge <slomo@circular-chaos.org>
6  *
7  * gstregistrybinary.c: GstRegistryBinary object, support routines
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library 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 GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  */
24
25 /* FIXME:
26  * - keep registry binary blob and reference strings
27  *   - don't free/unmmap contents when leaving gst_registry_binary_read_cache()
28  *     - free at gst_deinit() / _priv_gst_registry_cleanup() ?
29  *   - GstPlugin:
30  *     - GST_PLUGIN_FLAG_CONST
31  *   - GstPluginFeature, GstIndexFactory, GstElementFactory
32  *     - needs Flags (GST_PLUGIN_FEATURE_FLAG_CONST)
33  *     - can we turn loaded into flag?
34  * - why do we collect a list of binary chunks and not write immediately
35  *   - because we need to process subchunks, before we can set e.g. nr_of_items
36  *     in parent chunk
37  * - need more robustness
38  *   - don't parse beyond mem-block size
39  */
40
41 #ifdef HAVE_CONFIG_H
42 #  include "config.h"
43 #endif
44
45 #ifdef HAVE_UNISTD_H
46 #include <unistd.h>
47 #endif
48
49 #include <errno.h>
50 #include <stdio.h>
51
52 #if defined (_MSC_VER) && _MSC_VER >= 1400
53 #include <io.h>
54 #endif
55
56 #include <gst/gst_private.h>
57 #include <gst/gstconfig.h>
58 #include <gst/gstelement.h>
59 #include <gst/gsttypefind.h>
60 #include <gst/gsttypefindfactory.h>
61 #include <gst/gsturi.h>
62 #include <gst/gstinfo.h>
63 #include <gst/gstenumtypes.h>
64 #include <gst/gstpadtemplate.h>
65
66 #include <gst/gstregistrybinary.h>
67
68 #include <glib/gstdio.h>        /* for g_stat(), g_mapped_file(), ... */
69
70 #include "glib-compat-private.h"
71
72
73 #define GST_CAT_DEFAULT GST_CAT_REGISTRY
74
75 /* macros */
76
77 #define unpack_element(_inptr, _outptr, _element)  G_STMT_START{ \
78   _outptr = (_element *) _inptr; \
79   _inptr += sizeof (_element); \
80 }G_STMT_END
81
82 #define unpack_const_string(_inptr, _outptr) G_STMT_START{\
83   _outptr = g_intern_string ((const gchar *)_inptr); \
84   _inptr += strlen(_outptr) + 1; \
85 }G_STMT_END
86
87 #define unpack_string(_inptr, _outptr)  G_STMT_START{\
88   _outptr = g_strdup ((gchar *)_inptr); \
89   _inptr += strlen(_outptr) + 1; \
90 }G_STMT_END
91
92 #define ALIGNMENT            (sizeof (void *))
93 #define alignment(_address)  (gsize)_address%ALIGNMENT
94 #define align(_ptr)          _ptr += (( alignment(_ptr) == 0) ? 0 : ALIGNMENT-alignment(_ptr))
95
96 /* Registry saving */
97
98 #ifdef G_OS_WIN32
99 /* On win32, we can't use g_mkstmp(), because of cross-DLL file I/O problems.
100  * So, we just create the entire binary registry in memory, then write it out
101  * with g_file_set_contents(), which creates a temporary file internally
102  */
103
104 typedef struct BinaryRegistryCache
105 {
106   const char *location;
107   guint8 *mem;
108   gssize len;
109 } BinaryRegistryCache;
110
111 static BinaryRegistryCache *
112 gst_registry_binary_cache_init (GstRegistry * registry, const char *location)
113 {
114   BinaryRegistryCache *cache = g_new0 (BinaryRegistryCache, 1);
115   cache->location = location;
116   return cache;
117 }
118
119 static int
120 gst_registry_binary_cache_write (GstRegistry * registry,
121     BinaryRegistryCache * cache, unsigned long offset,
122     const void *data, int length)
123 {
124   cache->len = MAX (offset + length, cache->len);
125   cache->mem = g_realloc (cache->mem, cache->len);
126
127   memcpy (cache->mem + offset, data, length);
128
129   return length;
130 }
131
132 static gboolean
133 gst_registry_binary_cache_finish (GstRegistry * registry,
134     BinaryRegistryCache * cache, gboolean success)
135 {
136   gboolean ret = TRUE;
137   GError *error = NULL;
138   if (!g_file_set_contents (cache->location, (const gchar *) cache->mem,
139           cache->len, &error)) {
140     /* Probably the directory didn't exist; create it */
141     gchar *dir;
142     dir = g_path_get_dirname (cache->location);
143     g_mkdir_with_parents (dir, 0777);
144     g_free (dir);
145
146     g_error_free (error);
147     error = NULL;
148
149     if (!g_file_set_contents (cache->location, (const gchar *) cache->mem,
150             cache->len, &error)) {
151       GST_ERROR ("Failed to write to cache file: %s", error->message);
152       g_error_free (error);
153       ret = FALSE;
154     }
155   }
156
157   g_free (cache->mem);
158   g_free (cache);
159   return ret;
160 }
161
162 #else
163 typedef struct BinaryRegistryCache
164 {
165   const char *location;
166   char *tmp_location;
167   unsigned long currentoffset;
168 } BinaryRegistryCache;
169
170 static BinaryRegistryCache *
171 gst_registry_binary_cache_init (GstRegistry * registry, const char *location)
172 {
173   BinaryRegistryCache *cache = g_new0 (BinaryRegistryCache, 1);
174
175   cache->location = location;
176   cache->tmp_location = g_strconcat (location, ".tmpXXXXXX", NULL);
177   registry->cache_file = g_mkstemp (cache->tmp_location);
178   if (registry->cache_file == -1) {
179     gchar *dir;
180
181     /* oops, I bet the directory doesn't exist */
182     dir = g_path_get_dirname (location);
183     g_mkdir_with_parents (dir, 0777);
184     g_free (dir);
185
186     /* the previous g_mkstemp call overwrote the XXXXXX placeholder ... */
187     g_free (cache->tmp_location);
188     cache->tmp_location = g_strconcat (location, ".tmpXXXXXX", NULL);
189     registry->cache_file = g_mkstemp (cache->tmp_location);
190
191     if (registry->cache_file == -1) {
192       GST_DEBUG ("g_mkstemp() failed: %s", g_strerror (errno));
193       g_free (cache->tmp_location);
194       g_free (cache);
195       return NULL;
196     }
197   }
198
199   return cache;
200 }
201
202 static int
203 gst_registry_binary_cache_write (GstRegistry * registry,
204     BinaryRegistryCache * cache, unsigned long offset,
205     const void *data, int length)
206 {
207   long written;
208   if (offset != cache->currentoffset) {
209     if (lseek (registry->cache_file, offset, SEEK_SET) != 0) {
210       GST_ERROR ("Seeking to new offset failed");
211       return FALSE;
212     }
213     cache->currentoffset = offset;
214   }
215
216   written = write (registry->cache_file, data, length);
217   if (written != length) {
218     GST_ERROR ("Failed to write to cache file");
219   }
220   cache->currentoffset += written;
221
222   return written;
223 }
224
225 static gboolean
226 gst_registry_binary_cache_finish (GstRegistry * registry,
227     BinaryRegistryCache * cache, gboolean success)
228 {
229   if (close (registry->cache_file) < 0)
230     goto close_failed;
231
232   if (success) {
233     /* Only do the rename if we wrote the entire file successfully */
234     if (g_rename (cache->tmp_location, cache->location) < 0)
235       goto rename_failed;
236   }
237
238   g_free (cache->tmp_location);
239   g_free (cache);
240   GST_INFO ("Wrote binary registry cache");
241   return TRUE;
242
243 fail_after_close:
244   {
245     g_unlink (cache->tmp_location);
246     g_free (cache->tmp_location);
247     g_free (cache);
248     return FALSE;
249   }
250 close_failed:
251   {
252     GST_ERROR ("close() failed: %s", g_strerror (errno));
253     goto fail_after_close;
254   }
255 rename_failed:
256   {
257     GST_ERROR ("g_rename() failed: %s", g_strerror (errno));
258     goto fail_after_close;
259   }
260 }
261 #endif
262
263 /*
264  * gst_registry_binary_write_chunk:
265  *
266  * Write from a memory location to the registry cache file
267  *
268  * Returns: %TRUE for success
269  */
270 inline static gboolean
271 gst_registry_binary_write_chunk (GstRegistry * registry,
272     BinaryRegistryCache * cache, const void *mem,
273     const gssize size, unsigned long *file_position, gboolean align)
274 {
275   gchar padder[ALIGNMENT] = { 0, };
276   int padsize = 0;
277
278   /* Padding to insert the struct that requiere word alignment */
279   if ((align) && (alignment (*file_position) != 0)) {
280     padsize = ALIGNMENT - alignment (*file_position);
281     if (gst_registry_binary_cache_write (registry, cache, *file_position,
282             padder, padsize) != padsize) {
283       GST_ERROR ("Failed to write binary registry padder");
284       return FALSE;
285     }
286     *file_position += padsize;
287   }
288
289   if (gst_registry_binary_cache_write (registry, cache, *file_position,
290           mem, size) != size) {
291     GST_ERROR ("Failed to write binary registry element");
292     return FALSE;
293   }
294
295   *file_position += size;
296
297   return TRUE;
298 }
299
300
301 /*
302  * gst_registry_binary_initialize_magic:
303  *
304  * Initialize the GstBinaryRegistryMagic, setting both our magic number and
305  * gstreamer major/minor version
306  */
307 inline static gboolean
308 gst_registry_binary_initialize_magic (GstBinaryRegistryMagic * m)
309 {
310   memset (m, 0, sizeof (GstBinaryRegistryMagic));
311
312   if (!strncpy (m->magic, GST_MAGIC_BINARY_REGISTRY_STR,
313           GST_MAGIC_BINARY_REGISTRY_LEN)
314       || !strncpy (m->version, GST_MAGIC_BINARY_VERSION_STR,
315           GST_MAGIC_BINARY_VERSION_LEN)) {
316     GST_ERROR ("Failed to write magic to the registry magic structure");
317     return FALSE;
318   }
319
320   return TRUE;
321 }
322
323
324 /*
325  * gst_registry_binary_save_const_string:
326  *
327  * Store a const string in a binary chunk.
328  *
329  * Returns: %TRUE for success
330  */
331 inline static gboolean
332 gst_registry_binary_save_const_string (GList ** list, const gchar * str)
333 {
334   GstBinaryChunk *chunk;
335
336   if (G_UNLIKELY (str == NULL)) {
337     GST_ERROR ("unexpected NULL string in plugin or plugin feature data");
338     str = "";
339   }
340
341   chunk = g_malloc (sizeof (GstBinaryChunk));
342   chunk->data = (gpointer) str;
343   chunk->size = strlen ((gchar *) chunk->data) + 1;
344   chunk->flags = GST_BINARY_REGISTRY_FLAG_CONST;
345   chunk->align = FALSE;
346   *list = g_list_prepend (*list, chunk);
347   return TRUE;
348 }
349
350 /*
351  * gst_registry_binary_save_string:
352  *
353  * Store a string in a binary chunk.
354  *
355  * Returns: %TRUE for success
356  */
357 inline static gboolean
358 gst_registry_binary_save_string (GList ** list, gchar * str)
359 {
360   GstBinaryChunk *chunk;
361
362   chunk = g_malloc (sizeof (GstBinaryChunk));
363   chunk->data = str;
364   chunk->size = strlen ((gchar *) chunk->data) + 1;
365   chunk->flags = GST_BINARY_REGISTRY_FLAG_NONE;
366   chunk->align = FALSE;
367   *list = g_list_prepend (*list, chunk);
368   return TRUE;
369 }
370
371
372 /*
373  * gst_registry_binary_save_data:
374  *
375  * Store some data in a binary chunk.
376  *
377  * Returns: the initialized chunk
378  */
379 inline static GstBinaryChunk *
380 gst_registry_binary_make_data (gpointer data, gulong size)
381 {
382   GstBinaryChunk *chunk;
383
384   chunk = g_malloc (sizeof (GstBinaryChunk));
385   chunk->data = data;
386   chunk->size = size;
387   chunk->flags = GST_BINARY_REGISTRY_FLAG_NONE;
388   chunk->align = TRUE;
389   return chunk;
390 }
391
392
393 /*
394  * gst_registry_binary_save_pad_template:
395  *
396  * Store pad_templates in binary chunks.
397  *
398  * Returns: %TRUE for success
399  */
400 static gboolean
401 gst_registry_binary_save_pad_template (GList ** list,
402     GstStaticPadTemplate * template)
403 {
404   GstBinaryPadTemplate *pt;
405   GstBinaryChunk *chk;
406
407   pt = g_malloc0 (sizeof (GstBinaryPadTemplate));
408   chk = gst_registry_binary_make_data (pt, sizeof (GstBinaryPadTemplate));
409
410   pt->presence = template->presence;
411   pt->direction = template->direction;
412
413   /* pack pad template strings */
414   gst_registry_binary_save_const_string (list,
415       (gchar *) (template->static_caps.string));
416   gst_registry_binary_save_const_string (list, template->name_template);
417
418   *list = g_list_prepend (*list, chk);
419
420   return TRUE;
421 }
422
423
424 /*
425  * gst_registry_binary_save_feature:
426  *
427  * Store features in binary chunks.
428  *
429  * Returns: %TRUE for success
430  */
431 static gboolean
432 gst_registry_binary_save_feature (GList ** list, GstPluginFeature * feature)
433 {
434   const gchar *type_name = g_type_name (G_OBJECT_TYPE (feature));
435   GstBinaryPluginFeature *pf = NULL;
436   GstBinaryChunk *chk = NULL;
437   GList *walk;
438
439   if (!type_name) {
440     GST_ERROR ("NULL feature type_name, aborting.");
441     return FALSE;
442   }
443
444   if (GST_IS_ELEMENT_FACTORY (feature)) {
445     GstBinaryElementFactory *ef;
446     GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
447
448     ef = g_malloc0 (sizeof (GstBinaryElementFactory));
449     chk = gst_registry_binary_make_data (ef, sizeof (GstBinaryElementFactory));
450     ef->npadtemplates = ef->ninterfaces = ef->nuriprotocols = 0;
451     pf = (GstBinaryPluginFeature *) ef;
452
453     /* save interfaces */
454     for (walk = factory->interfaces; walk;
455         walk = g_list_next (walk), ef->ninterfaces++) {
456       gst_registry_binary_save_const_string (list, (gchar *) walk->data);
457     }
458     GST_DEBUG ("Saved %d Interfaces", ef->ninterfaces);
459     /* save uritypes */
460     if (GST_URI_TYPE_IS_VALID (factory->uri_type)) {
461       if (factory->uri_protocols && *factory->uri_protocols) {
462         GstBinaryChunk *subchk;
463         gchar **protocol;
464
465         subchk =
466             gst_registry_binary_make_data (&factory->uri_type,
467             sizeof (factory->uri_type));
468         subchk->flags = GST_BINARY_REGISTRY_FLAG_CONST;
469
470         protocol = factory->uri_protocols;
471         while (*protocol) {
472           gst_registry_binary_save_const_string (list, *protocol++);
473           ef->nuriprotocols++;
474         }
475         *list = g_list_prepend (*list, subchk);
476         GST_DEBUG ("Saved %d UriTypes", ef->nuriprotocols);
477       } else {
478         g_warning ("GStreamer feature '%s' is URI handler but does not provide"
479             " any protocols it can handle", feature->name);
480       }
481     }
482
483     /* save pad-templates */
484     for (walk = factory->staticpadtemplates; walk;
485         walk = g_list_next (walk), ef->npadtemplates++) {
486       GstStaticPadTemplate *template = walk->data;
487
488       if (!gst_registry_binary_save_pad_template (list, template)) {
489         GST_ERROR ("Can't fill pad template, aborting.");
490         goto fail;
491       }
492     }
493
494     /* pack element factory strings */
495     gst_registry_binary_save_const_string (list, factory->details.author);
496     gst_registry_binary_save_const_string (list, factory->details.description);
497     gst_registry_binary_save_const_string (list, factory->details.klass);
498     gst_registry_binary_save_const_string (list, factory->details.longname);
499   } else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
500     GstBinaryTypeFindFactory *tff;
501     GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
502     gchar *str;
503
504     /* we copy the caps here so we can simplify them before saving. This is a lot
505      * faster when loading them later on */
506     GstCaps *copy = gst_caps_copy (factory->caps);
507
508     tff = g_malloc0 (sizeof (GstBinaryTypeFindFactory));
509     chk =
510         gst_registry_binary_make_data (tff, sizeof (GstBinaryTypeFindFactory));
511     tff->nextensions = 0;
512     pf = (GstBinaryPluginFeature *) tff;
513
514     /* save extensions */
515     if (factory->extensions) {
516       while (factory->extensions[tff->nextensions]) {
517         gst_registry_binary_save_const_string (list,
518             factory->extensions[tff->nextensions++]);
519       }
520     }
521     /* save caps */
522     gst_caps_do_simplify (copy);
523     str = gst_caps_to_string (copy);
524     gst_caps_unref (copy);
525     gst_registry_binary_save_string (list, str);
526   } else if (GST_IS_INDEX_FACTORY (feature)) {
527     GstIndexFactory *factory = GST_INDEX_FACTORY (feature);
528
529     pf = g_malloc0 (sizeof (GstBinaryPluginFeature));
530     chk = gst_registry_binary_make_data (pf, sizeof (GstBinaryPluginFeature));
531     pf->rank = feature->rank;
532
533     /* pack element factory strings */
534     gst_registry_binary_save_const_string (list, factory->longdesc);
535   } else {
536     GST_WARNING ("unhandled feature type '%s'", type_name);
537   }
538
539   if (pf) {
540     pf->rank = feature->rank;
541     *list = g_list_prepend (*list, chk);
542
543     /* pack plugin feature strings */
544     gst_registry_binary_save_const_string (list, feature->name);
545     gst_registry_binary_save_const_string (list, (gchar *) type_name);
546
547     return TRUE;
548   }
549
550   /* Errors */
551 fail:
552   g_free (chk);
553   g_free (pf);
554   return FALSE;
555 }
556
557 static gboolean
558 gst_registry_binary_save_plugin_dep (GList ** list, GstPluginDep * dep)
559 {
560   GstBinaryDep *ed;
561   GstBinaryChunk *chk;
562   gchar **s;
563
564   ed = g_new0 (GstBinaryDep, 1);
565   chk = gst_registry_binary_make_data (ed, sizeof (GstBinaryDep));
566
567   ed->flags = dep->flags;
568   ed->n_env_vars = 0;
569   ed->n_paths = 0;
570   ed->n_names = 0;
571
572   ed->env_hash = dep->env_hash;
573   ed->stat_hash = dep->stat_hash;
574
575   for (s = dep->env_vars; s != NULL && *s != NULL; ++s, ++ed->n_env_vars)
576     gst_registry_binary_save_string (list, g_strdup (*s));
577
578   for (s = dep->paths; s != NULL && *s != NULL; ++s, ++ed->n_paths)
579     gst_registry_binary_save_string (list, g_strdup (*s));
580
581   for (s = dep->names; s != NULL && *s != NULL; ++s, ++ed->n_names)
582     gst_registry_binary_save_string (list, g_strdup (*s));
583
584   *list = g_list_prepend (*list, chk);
585
586   GST_LOG ("Saved external plugin dependency");
587   return TRUE;
588 }
589
590 /*
591  * gst_registry_binary_save_plugin:
592  *
593  * Adapt a GstPlugin to our GstBinaryPluginElement structure, and write it to
594  * the registry file.
595  */
596 static gboolean
597 gst_registry_binary_save_plugin (GList ** list, GstRegistry * registry,
598     GstPlugin * plugin)
599 {
600   GstBinaryPluginElement *pe;
601   GstBinaryChunk *chk;
602   GList *plugin_features = NULL;
603   GList *walk;
604
605   pe = g_malloc0 (sizeof (GstBinaryPluginElement));
606   chk = gst_registry_binary_make_data (pe, sizeof (GstBinaryPluginElement));
607
608   pe->file_size = plugin->file_size;
609   pe->file_mtime = plugin->file_mtime;
610   pe->n_deps = 0;
611   pe->nfeatures = 0;
612
613   /* pack external deps */
614   for (walk = plugin->priv->deps; walk != NULL; walk = walk->next) {
615     if (!gst_registry_binary_save_plugin_dep (list, walk->data)) {
616       GST_ERROR ("Could not save external plugin dependency, aborting.");
617       goto fail;
618     }
619     ++pe->n_deps;
620   }
621
622   /* pack plugin features */
623   plugin_features =
624       gst_registry_get_feature_list_by_plugin (registry, plugin->desc.name);
625   for (walk = plugin_features; walk; walk = g_list_next (walk), pe->nfeatures++) {
626     GstPluginFeature *feature = GST_PLUGIN_FEATURE (walk->data);
627
628     if (!gst_registry_binary_save_feature (list, feature)) {
629       GST_ERROR ("Can't fill plugin feature, aborting.");
630       goto fail;
631     }
632   }
633   GST_DEBUG ("Save plugin '%s' with %d feature(s)", plugin->desc.name,
634       pe->nfeatures);
635
636   gst_plugin_feature_list_free (plugin_features);
637
638   /* pack plugin element strings */
639   gst_registry_binary_save_const_string (list, plugin->desc.origin);
640   gst_registry_binary_save_const_string (list, plugin->desc.package);
641   gst_registry_binary_save_const_string (list, plugin->desc.source);
642   gst_registry_binary_save_const_string (list, plugin->desc.license);
643   gst_registry_binary_save_const_string (list, plugin->desc.version);
644   gst_registry_binary_save_const_string (list, plugin->filename);
645   gst_registry_binary_save_const_string (list, plugin->desc.description);
646   gst_registry_binary_save_const_string (list, plugin->desc.name);
647
648   *list = g_list_prepend (*list, chk);
649
650   GST_DEBUG ("Found %d features in plugin \"%s\"", pe->nfeatures,
651       plugin->desc.name);
652   return TRUE;
653
654   /* Errors */
655 fail:
656   gst_plugin_feature_list_free (plugin_features);
657   g_free (chk);
658   g_free (pe);
659   return FALSE;
660 }
661
662 /**
663  * gst_registry_binary_write_cache:
664  * @registry: a #GstRegistry
665  * @location: a filename
666  *
667  * Write the @registry to a cache to file at given @location.
668  * 
669  * Returns: %TRUE on success.
670  */
671 gboolean
672 gst_registry_binary_write_cache (GstRegistry * registry, const char *location)
673 {
674   GList *walk;
675   GstBinaryRegistryMagic magic;
676   GList *to_write = NULL;
677   unsigned long file_position = 0;
678   BinaryRegistryCache *cache;
679
680   GST_INFO ("Building binary registry cache image");
681
682   g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
683
684   if (!gst_registry_binary_initialize_magic (&magic))
685     goto fail;
686
687   /* iterate trough the list of plugins and fit them into binary structures */
688   for (walk = registry->plugins; walk; walk = g_list_next (walk)) {
689     GstPlugin *plugin = GST_PLUGIN (walk->data);
690
691     if (!plugin->filename)
692       continue;
693
694     if (plugin->flags & GST_PLUGIN_FLAG_CACHED) {
695       int ret;
696       struct stat statbuf;
697
698       if ((ret = g_stat (plugin->filename, &statbuf)) < 0 ||
699           plugin->file_mtime != statbuf.st_mtime ||
700           plugin->file_size != statbuf.st_size)
701         continue;
702     }
703
704     if (!gst_registry_binary_save_plugin (&to_write, registry, plugin)) {
705       GST_ERROR ("Can't write binary plugin information for \"%s\"",
706           plugin->filename);
707     }
708   }
709
710   GST_INFO ("Writing binary registry cache");
711
712   cache = gst_registry_binary_cache_init (registry, location);
713   if (!cache)
714     goto fail_free_list;
715
716   /* write magic */
717   if (gst_registry_binary_cache_write (registry, cache, file_position,
718           &magic, sizeof (GstBinaryRegistryMagic)) !=
719       sizeof (GstBinaryRegistryMagic)) {
720     GST_ERROR ("Failed to write binary registry magic");
721     goto fail_free_list;
722   }
723   file_position += sizeof (GstBinaryRegistryMagic);
724
725   /* write out data chunks */
726   for (walk = to_write; walk; walk = g_list_next (walk)) {
727     GstBinaryChunk *cur = walk->data;
728
729     if (!gst_registry_binary_write_chunk (registry, cache, cur->data, cur->size,
730             &file_position, cur->align)) {
731       if (!(cur->flags & GST_BINARY_REGISTRY_FLAG_CONST))
732         g_free (cur->data);
733       g_free (cur);
734       walk->data = NULL;
735       goto fail_free_list;
736     }
737     if (!(cur->flags & GST_BINARY_REGISTRY_FLAG_CONST))
738       g_free (cur->data);
739     g_free (cur);
740     walk->data = NULL;
741   }
742   g_list_free (to_write);
743
744   if (!gst_registry_binary_cache_finish (registry, cache, TRUE))
745     return FALSE;
746
747   return TRUE;
748
749   /* Errors */
750 fail_free_list:
751   {
752     for (walk = to_write; walk; walk = g_list_next (walk)) {
753       GstBinaryChunk *cur = walk->data;
754
755       if (!(cur->flags & GST_BINARY_REGISTRY_FLAG_CONST))
756         g_free (cur->data);
757       g_free (cur);
758     }
759     g_list_free (to_write);
760
761     if (cache)
762       (void) gst_registry_binary_cache_finish (registry, cache, FALSE);
763     /* fall through */
764   }
765 fail:
766   {
767     return FALSE;
768   }
769 }
770
771
772 /* Registry loading */
773
774 /*
775  * gst_registry_binary_check_magic:
776  *
777  * Check GstBinaryRegistryMagic validity.
778  * Return < 0 if something is wrong, -2 means
779  * that just the version of the registry is out of
780  * date, -1 is a general failure.
781  */
782 static gint
783 gst_registry_binary_check_magic (gchar ** in, gsize size)
784 {
785   GstBinaryRegistryMagic *m;
786
787   align (*in);
788   GST_DEBUG ("Reading/casting for GstBinaryRegistryMagic at address %p", *in);
789
790   if (size < sizeof (GstBinaryRegistryMagic)) {
791     GST_WARNING ("Not enough data for binary registry magic structure");
792     return -1;
793   }
794   unpack_element (*in, m, GstBinaryRegistryMagic);
795
796   if (strncmp (m->magic, GST_MAGIC_BINARY_REGISTRY_STR,
797           GST_MAGIC_BINARY_REGISTRY_LEN) != 0) {
798     GST_WARNING
799         ("Binary registry magic is different : %02x%02x%02x%02x != %02x%02x%02x%02x",
800         GST_MAGIC_BINARY_REGISTRY_STR[0] & 0xff,
801         GST_MAGIC_BINARY_REGISTRY_STR[1] & 0xff,
802         GST_MAGIC_BINARY_REGISTRY_STR[2] & 0xff,
803         GST_MAGIC_BINARY_REGISTRY_STR[3] & 0xff, m->magic[0] & 0xff,
804         m->magic[1] & 0xff, m->magic[2] & 0xff, m->magic[3] & 0xff);
805     return -1;
806   }
807   if (strncmp (m->version, GST_MAGIC_BINARY_VERSION_STR,
808           GST_MAGIC_BINARY_VERSION_LEN)) {
809     GST_WARNING ("Binary registry magic version is different : %s != %s",
810         GST_MAGIC_BINARY_VERSION_STR, m->version);
811     return -2;
812   }
813
814   return 0;
815 }
816
817
818 /*
819  * gst_registry_binary_load_pad_template:
820  *
821  * Make a new GstStaticPadTemplate from current GstBinaryPadTemplate structure
822  *
823  * Returns: new GstStaticPadTemplate
824  */
825 static gboolean
826 gst_registry_binary_load_pad_template (GstElementFactory * factory, gchar ** in)
827 {
828   GstBinaryPadTemplate *pt;
829   GstStaticPadTemplate *template;
830
831   align (*in);
832   GST_DEBUG ("Reading/casting for GstBinaryPadTemplate at address %p", *in);
833   unpack_element (*in, pt, GstBinaryPadTemplate);
834
835   template = g_new0 (GstStaticPadTemplate, 1);
836   template->presence = pt->presence;
837   template->direction = pt->direction;
838
839   /* unpack pad template strings */
840   unpack_const_string (*in, template->name_template);
841   unpack_string (*in, template->static_caps.string);
842
843   __gst_element_factory_add_static_pad_template (factory, template);
844   GST_DEBUG ("Added pad_template %s", template->name_template);
845
846   return TRUE;
847 }
848
849
850 /*
851  * gst_registry_binary_load_feature:
852  *
853  * Make a new GstPluginFeature from current binary plugin feature structure
854  *
855  * Returns: new GstPluginFeature
856  */
857 static gboolean
858 gst_registry_binary_load_feature (GstRegistry * registry, gchar ** in,
859     const gchar * plugin_name)
860 {
861   GstBinaryPluginFeature *pf = NULL;
862   GstPluginFeature *feature;
863   gchar *type_name = NULL, *str;
864   GType type;
865   guint i;
866
867   /* unpack plugin feature strings */
868   unpack_string (*in, type_name);
869
870   if (!type_name) {
871     GST_ERROR ("No feature type name");
872     return FALSE;
873   }
874
875   GST_DEBUG ("Plugin '%s' feature typename : '%s'", plugin_name, type_name);
876
877   if (!(type = g_type_from_name (type_name))) {
878     GST_ERROR ("Unknown type from typename '%s' for plugin '%s'", type_name,
879         plugin_name);
880     g_free (type_name);
881     return FALSE;
882   }
883   if ((feature = g_object_new (type, NULL)) == NULL) {
884     GST_ERROR ("Can't create feature from type");
885     g_free (type_name);
886     return FALSE;
887   }
888
889   if (!GST_IS_PLUGIN_FEATURE (feature)) {
890     GST_ERROR ("typename : '%s' is not a plugin feature", type_name);
891     goto fail;
892   }
893
894   /* unpack more plugin feature strings */
895   unpack_string (*in, feature->name);
896
897   if (GST_IS_ELEMENT_FACTORY (feature)) {
898     GstBinaryElementFactory *ef;
899     GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
900
901     align (*in);
902     GST_LOG ("Reading/casting for GstBinaryElementFactory at address %p", *in);
903     unpack_element (*in, ef, GstBinaryElementFactory);
904     pf = (GstBinaryPluginFeature *) ef;
905
906     /* unpack element factory strings */
907     unpack_string (*in, factory->details.longname);
908     unpack_string (*in, factory->details.klass);
909     unpack_string (*in, factory->details.description);
910     unpack_string (*in, factory->details.author);
911     GST_DEBUG ("Element factory : '%s' with npadtemplates=%d",
912         factory->details.longname, ef->npadtemplates);
913
914     /* load pad templates */
915     for (i = 0; i < ef->npadtemplates; i++) {
916       if (!gst_registry_binary_load_pad_template (factory, in)) {
917         GST_ERROR ("Error while loading binary pad template");
918         goto fail;
919       }
920     }
921
922     /* load uritypes */
923     if (ef->nuriprotocols) {
924       GST_DEBUG ("Reading %d UriTypes at address %p", ef->nuriprotocols, *in);
925
926       align (*in);
927       factory->uri_type = *((guint *) * in);
928       *in += sizeof (factory->uri_type);
929       //unpack_element(*in, &factory->uri_type, factory->uri_type);
930
931       factory->uri_protocols = g_new0 (gchar *, ef->nuriprotocols + 1);
932       for (i = 0; i < ef->nuriprotocols; i++) {
933         unpack_string (*in, str);
934         factory->uri_protocols[i] = str;
935       }
936     }
937     /* load interfaces */
938     GST_DEBUG ("Reading %d Interfaces at address %p", ef->ninterfaces, *in);
939     for (i = 0; i < ef->ninterfaces; i++) {
940       unpack_string (*in, str);
941       __gst_element_factory_add_interface (factory, str);
942       g_free (str);
943     }
944   } else if (GST_IS_TYPE_FIND_FACTORY (feature)) {
945     GstBinaryTypeFindFactory *tff;
946     GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (feature);
947
948     align (*in);
949     GST_DEBUG ("Reading/casting for GstBinaryPluginFeature at address %p", *in);
950     unpack_element (*in, tff, GstBinaryTypeFindFactory);
951     pf = (GstBinaryPluginFeature *) tff;
952
953     /* load caps */
954     unpack_string (*in, str);
955     factory->caps = gst_caps_from_string (str);
956     g_free (str);
957     /* load extensions */
958     if (tff->nextensions) {
959       GST_DEBUG ("Reading %d Typefind extensions at address %p",
960           tff->nextensions, *in);
961       factory->extensions = g_new0 (gchar *, tff->nextensions + 1);
962       for (i = 0; i < tff->nextensions; i++) {
963         unpack_string (*in, str);
964         factory->extensions[i] = str;
965       }
966     }
967   } else if (GST_IS_INDEX_FACTORY (feature)) {
968     GstIndexFactory *factory = GST_INDEX_FACTORY (feature);
969
970     align (*in);
971     GST_DEBUG ("Reading/casting for GstBinaryPluginFeature at address %p", *in);
972     unpack_element (*in, pf, GstBinaryPluginFeature);
973
974     /* unpack index factory strings */
975     unpack_string (*in, factory->longdesc);
976   } else {
977     GST_WARNING ("unhandled factory type : %s", G_OBJECT_TYPE_NAME (feature));
978     goto fail;
979   }
980
981   feature->rank = pf->rank;
982
983   /* should already be the interned string, but better make sure */
984   feature->plugin_name = g_intern_string (plugin_name);
985
986   gst_registry_add_feature (registry, feature);
987   GST_DEBUG ("Added feature %s", feature->name);
988
989   g_free (type_name);
990   return TRUE;
991
992   /* Errors */
993 fail:
994   g_free (type_name);
995   if (GST_IS_OBJECT (feature))
996     gst_object_unref (feature);
997   else
998     g_object_unref (feature);
999   return FALSE;
1000 }
1001
1002 static gchar **
1003 gst_registry_binary_load_plugin_dep_strv (gchar ** in, guint n)
1004 {
1005   gchar **arr;
1006
1007   if (n == 0)
1008     return NULL;
1009
1010   arr = g_new0 (gchar *, n + 1);
1011   while (n > 0) {
1012     unpack_string (*in, arr[n - 1]);
1013     --n;
1014   }
1015   return arr;
1016 }
1017
1018 static gboolean
1019 gst_registry_binary_load_plugin_dep (GstPlugin * plugin, gchar ** in)
1020 {
1021   GstPluginDep *dep;
1022   GstBinaryDep *d;
1023   gchar **s;
1024
1025   align (*in);
1026   GST_LOG_OBJECT (plugin, "Unpacking GstBinaryDep from %p", *in);
1027   unpack_element (*in, d, GstBinaryDep);
1028
1029   dep = g_new0 (GstPluginDep, 1);
1030
1031   dep->env_hash = d->env_hash;
1032   dep->stat_hash = d->stat_hash;
1033
1034   dep->flags = d->flags;
1035
1036   dep->names = gst_registry_binary_load_plugin_dep_strv (in, d->n_names);
1037   dep->paths = gst_registry_binary_load_plugin_dep_strv (in, d->n_paths);
1038   dep->env_vars = gst_registry_binary_load_plugin_dep_strv (in, d->n_env_vars);
1039
1040   plugin->priv->deps = g_list_append (plugin->priv->deps, dep);
1041
1042   GST_DEBUG_OBJECT (plugin, "Loaded external plugin dependency from registry: "
1043       "env_hash: %08x, stat_hash: %08x", dep->env_hash, dep->stat_hash);
1044   for (s = dep->env_vars; s != NULL && *s != NULL; ++s)
1045     GST_LOG_OBJECT (plugin, " evar: %s", *s);
1046   for (s = dep->paths; s != NULL && *s != NULL; ++s)
1047     GST_LOG_OBJECT (plugin, " path: %s", *s);
1048   for (s = dep->names; s != NULL && *s != NULL; ++s)
1049     GST_LOG_OBJECT (plugin, " name: %s", *s);
1050
1051   return TRUE;
1052 }
1053
1054 /*
1055  * gst_registry_binary_load_plugin:
1056  *
1057  * Make a new GstPlugin from current GstBinaryPluginElement structure
1058  * and save it to the GstRegistry. Return an offset to the next
1059  * GstBinaryPluginElement structure.
1060  */
1061 static gboolean
1062 gst_registry_binary_load_plugin (GstRegistry * registry, gchar ** in)
1063 {
1064   GstBinaryPluginElement *pe;
1065   GstPlugin *plugin = NULL;
1066   guint i;
1067
1068   align (*in);
1069   GST_LOG ("Reading/casting for GstBinaryPluginElement at address %p", *in);
1070   unpack_element (*in, pe, GstBinaryPluginElement);
1071
1072   plugin = g_object_new (GST_TYPE_PLUGIN, NULL);
1073
1074   /* TODO: also set GST_PLUGIN_FLAG_CONST */
1075   plugin->flags |= GST_PLUGIN_FLAG_CACHED;
1076   plugin->file_mtime = pe->file_mtime;
1077   plugin->file_size = pe->file_size;
1078
1079   /* unpack plugin element strings */
1080   unpack_const_string (*in, plugin->desc.name);
1081   unpack_string (*in, plugin->desc.description);
1082   unpack_string (*in, plugin->filename);
1083   unpack_const_string (*in, plugin->desc.version);
1084   unpack_const_string (*in, plugin->desc.license);
1085   unpack_const_string (*in, plugin->desc.source);
1086   unpack_const_string (*in, plugin->desc.package);
1087   unpack_const_string (*in, plugin->desc.origin);
1088   GST_LOG ("read strings for name='%s'", plugin->desc.name);
1089   GST_LOG ("  desc.description='%s'", plugin->desc.description);
1090   GST_LOG ("  filename='%s'", plugin->filename);
1091   GST_LOG ("  desc.version='%s'", plugin->desc.version);
1092   GST_LOG ("  desc.license='%s'", plugin->desc.license);
1093   GST_LOG ("  desc.source='%s'", plugin->desc.source);
1094   GST_LOG ("  desc.package='%s'", plugin->desc.package);
1095   GST_LOG ("  desc.origin='%s'", plugin->desc.origin);
1096
1097   plugin->basename = g_path_get_basename (plugin->filename);
1098
1099   /* Takes ownership of plugin */
1100   gst_registry_add_plugin (registry, plugin);
1101   GST_DEBUG ("Added plugin '%s' plugin with %d features from binary registry",
1102       plugin->desc.name, pe->nfeatures);
1103
1104   /* Load plugin features */
1105   for (i = 0; i < pe->nfeatures; i++) {
1106     if (!gst_registry_binary_load_feature (registry, in, plugin->desc.name)) {
1107       GST_ERROR ("Error while loading binary feature");
1108       goto fail;
1109     }
1110   }
1111
1112   /* Load external plugin dependencies */
1113   for (i = 0; i < pe->n_deps; ++i) {
1114     if (!gst_registry_binary_load_plugin_dep (plugin, in)) {
1115       GST_ERROR_OBJECT (plugin, "Could not read external plugin dependency");
1116       goto fail;
1117     }
1118   }
1119
1120   return TRUE;
1121
1122   /* Errors */
1123 fail:
1124   return FALSE;
1125 }
1126
1127
1128 /**
1129  * gst_registry_binary_read_cache:
1130  * @registry: a #GstRegistry
1131  * @location: a filename
1132  *
1133  * Read the contents of the binary cache file at @location into @registry.
1134  *
1135  * Returns: %TRUE on success.
1136  */
1137 gboolean
1138 gst_registry_binary_read_cache (GstRegistry * registry, const char *location)
1139 {
1140   GMappedFile *mapped = NULL;
1141   gchar *contents = NULL;
1142   gchar *in = NULL;
1143   gsize size;
1144   GError *err = NULL;
1145   gboolean res = FALSE;
1146   gint check_magic_result;
1147 #ifndef GST_DISABLE_GST_DEBUG
1148   GTimer *timer = NULL;
1149   gdouble seconds;
1150 #endif
1151
1152   /* make sure these types exist */
1153   GST_TYPE_ELEMENT_FACTORY;
1154   GST_TYPE_TYPE_FIND_FACTORY;
1155   GST_TYPE_INDEX_FACTORY;
1156
1157 #ifndef GST_DISABLE_GST_DEBUG
1158   timer = g_timer_new ();
1159 #endif
1160
1161   mapped = g_mapped_file_new (location, FALSE, &err);
1162   if (err != NULL) {
1163     GST_INFO ("Unable to mmap file %s : %s", location, err->message);
1164     g_error_free (err);
1165     err = NULL;
1166
1167     g_file_get_contents (location, &contents, &size, &err);
1168     if (err != NULL) {
1169       GST_INFO ("Unable to read file %s : %s", location, err->message);
1170 #ifndef GST_DISABLE_GST_DEBUG
1171       g_timer_destroy (timer);
1172 #endif
1173       g_error_free (err);
1174       return FALSE;
1175     }
1176   } else {
1177     if ((contents = g_mapped_file_get_contents (mapped)) == NULL) {
1178       GST_ERROR ("Can't load file %s : %s", location, g_strerror (errno));
1179       goto Error;
1180     }
1181     /* check length for header */
1182     size = g_mapped_file_get_length (mapped);
1183   }
1184   /* in is a cursor pointer, we initialize it with the begin of registry and is updated on each read */
1185   in = contents;
1186   GST_DEBUG ("File data at address %p", in);
1187   if (size < sizeof (GstBinaryRegistryMagic)) {
1188     GST_ERROR ("No or broken registry header");
1189     goto Error;
1190   }
1191   /* check if header is valid */
1192   if ((check_magic_result = gst_registry_binary_check_magic (&in, size)) < 0) {
1193
1194     if (check_magic_result == -1)
1195       GST_ERROR
1196           ("Binary registry type not recognized (invalid magic) for file at %s",
1197           location);
1198     goto Error;
1199   }
1200
1201   /* check if there are plugins in the file */
1202
1203   if (!(((gsize) in + sizeof (GstBinaryPluginElement)) <
1204           (gsize) contents + size)) {
1205     GST_INFO ("No binary plugins structure to read");
1206     /* empty file, this is not an error */
1207   } else {
1208     for (;
1209         ((gsize) in + sizeof (GstBinaryPluginElement)) <
1210         (gsize) contents + size;) {
1211       GST_DEBUG ("reading binary registry %" G_GSIZE_FORMAT "(%x)/%"
1212           G_GSIZE_FORMAT, (gsize) in - (gsize) contents,
1213           (guint) ((gsize) in - (gsize) contents), size);
1214       if (!gst_registry_binary_load_plugin (registry, &in)) {
1215         GST_ERROR ("Problem while reading binary registry");
1216         goto Error;
1217       }
1218     }
1219   }
1220
1221 #ifndef GST_DISABLE_GST_DEBUG
1222   g_timer_stop (timer);
1223   seconds = g_timer_elapsed (timer, NULL);
1224 #endif
1225
1226   GST_INFO ("loaded %s in %lf seconds", location, seconds);
1227
1228   res = TRUE;
1229   /* TODO: once we re-use the pointers to registry contents return here */
1230
1231 Error:
1232 #ifndef GST_DISABLE_GST_DEBUG
1233   g_timer_destroy (timer);
1234 #endif
1235   if (mapped) {
1236     g_mapped_file_free (mapped);
1237   } else {
1238     g_free (contents);
1239   }
1240   return res;
1241 }
1242
1243
1244 /* FIXME 0.11: these are here for backwards compatibility */
1245
1246 gboolean
1247 gst_registry_xml_read_cache (GstRegistry * registry, const char *location)
1248 {
1249   return FALSE;
1250 }
1251
1252 gboolean
1253 gst_registry_xml_write_cache (GstRegistry * registry, const char *location)
1254 {
1255   return FALSE;
1256 }