gst/parse: Also pass -DGST_EXPORTS here
[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., 51 Franklin St, Fifth Floor,
22  * Boston, MA 02110-1301, 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  */
38
39 #ifdef HAVE_CONFIG_H
40 #  include "config.h"
41 #endif
42
43 #ifdef HAVE_UNISTD_H
44 #include <unistd.h>
45 #endif
46
47 #include <errno.h>
48 #include <stdio.h>
49
50 #if defined (_MSC_VER) && _MSC_VER >= 1400
51 #include <io.h>
52 #endif
53
54 #include <gst/gst_private.h>
55 #include <gst/gstconfig.h>
56 #include <gst/gstelement.h>
57 #include <gst/gsttypefind.h>
58 #include <gst/gsttypefindfactory.h>
59 #include <gst/gstdeviceproviderfactory.h>
60 #include <gst/gsturi.h>
61 #include <gst/gstinfo.h>
62 #include <gst/gstenumtypes.h>
63 #include <gst/gstpadtemplate.h>
64
65 #include <gst/gstregistrychunks.h>
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 /* reading macros */
76 #define unpack_element(inptr, outptr, element, endptr, error_label) G_STMT_START{ \
77   if (inptr + sizeof(element) >= endptr) \
78     goto error_label; \
79   outptr = (element *) inptr; \
80   inptr += sizeof (element); \
81 }G_STMT_END
82
83 #define ALIGNMENT            (sizeof (void *))
84 #define alignment(_address)  (gsize)_address%ALIGNMENT
85 #define align(_ptr)          _ptr += (( alignment(_ptr) == 0) ? 0 : ALIGNMENT-alignment(_ptr))
86
87 /* Registry saving */
88
89 #ifdef G_OS_WIN32
90 /* On win32, we can't use g_mkstmp(), because of cross-DLL file I/O problems.
91  * So, we just create the entire binary registry in memory, then write it out
92  * with g_file_set_contents(), which creates a temporary file internally
93  */
94
95 typedef struct BinaryRegistryCache
96 {
97   const char *location;
98   guint8 *mem;
99   gssize len;
100 } BinaryRegistryCache;
101
102 static BinaryRegistryCache *
103 gst_registry_binary_cache_init (GstRegistry * registry, const char *location)
104 {
105   BinaryRegistryCache *cache = g_slice_new0 (BinaryRegistryCache);
106   cache->location = location;
107   return cache;
108 }
109
110 static int
111 gst_registry_binary_cache_write (BinaryRegistryCache * cache,
112     unsigned long offset, const void *data, int length)
113 {
114   cache->len = MAX (offset + length, cache->len);
115   cache->mem = g_realloc (cache->mem, cache->len);
116
117   memcpy (cache->mem + offset, data, length);
118
119   return length;
120 }
121
122 static gboolean
123 gst_registry_binary_cache_finish (BinaryRegistryCache * cache, gboolean success)
124 {
125   gboolean ret = TRUE;
126   GError *error = NULL;
127   if (!g_file_set_contents (cache->location, (const gchar *) cache->mem,
128           cache->len, &error)) {
129     /* Probably the directory didn't exist; create it */
130     gchar *dir;
131     dir = g_path_get_dirname (cache->location);
132     g_mkdir_with_parents (dir, 0777);
133     g_free (dir);
134
135     g_error_free (error);
136     error = NULL;
137
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
158   g_free (cache->mem);
159   g_slice_free (BinaryRegistryCache, cache);
160   return ret;
161 }
162
163 #else
164 typedef struct BinaryRegistryCache
165 {
166   const char *location;
167   char *tmp_location;
168   unsigned long currentoffset;
169   int cache_fd;
170 } BinaryRegistryCache;
171
172 static BinaryRegistryCache *
173 gst_registry_binary_cache_init (GstRegistry * registry, const char *location)
174 {
175   BinaryRegistryCache *cache = g_slice_new0 (BinaryRegistryCache);
176
177   cache->location = location;
178   cache->tmp_location = g_strconcat (location, ".tmpXXXXXX", NULL);
179   cache->cache_fd = g_mkstemp (cache->tmp_location);
180   if (cache->cache_fd == -1) {
181     int ret;
182     GStatBuf statbuf;
183     gchar *dir;
184
185     /* oops, I bet the directory doesn't exist */
186     dir = g_path_get_dirname (location);
187     g_mkdir_with_parents (dir, 0777);
188
189     ret = g_stat (dir, &statbuf);
190     if (ret != -1 && (statbuf.st_mode & 0700) != 0700) {
191       g_chmod (dir, 0700);
192     }
193
194     g_free (dir);
195
196     /* the previous g_mkstemp call overwrote the XXXXXX placeholder ... */
197     g_free (cache->tmp_location);
198     cache->tmp_location = g_strconcat (location, ".tmpXXXXXX", NULL);
199     cache->cache_fd = g_mkstemp (cache->tmp_location);
200
201     if (cache->cache_fd == -1) {
202       GST_DEBUG ("g_mkstemp() failed: %s", g_strerror (errno));
203       g_free (cache->tmp_location);
204       g_slice_free (BinaryRegistryCache, cache);
205       return NULL;
206     }
207
208     ret = g_stat (cache->tmp_location, &statbuf);
209     if (ret != -1 && (statbuf.st_mode & 0600) != 0600) {
210       g_chmod (cache->tmp_location, 0600);
211     }
212   }
213
214   return cache;
215 }
216
217 static int
218 gst_registry_binary_cache_write (BinaryRegistryCache * cache,
219     unsigned long offset, const void *data, int length)
220 {
221   long written;
222   if (offset != cache->currentoffset) {
223     if (lseek (cache->cache_fd, offset, SEEK_SET) < 0) {
224       GST_ERROR ("Seeking to new offset failed: %s", g_strerror (errno));
225       return -1;
226     }
227     GST_LOG ("Seeked from offset %lu to %lu", offset, cache->currentoffset);
228     cache->currentoffset = offset;
229   }
230
231   written = write (cache->cache_fd, data, length);
232   if (written != length) {
233     GST_ERROR ("Failed to write to cache file");
234   }
235   cache->currentoffset += written;
236
237   return written;
238 }
239
240 static gboolean
241 gst_registry_binary_cache_finish (BinaryRegistryCache * cache, gboolean success)
242 {
243   /* only fsync if we're actually going to use and rename the file below */
244   if (success && fsync (cache->cache_fd) < 0)
245     goto fsync_failed;
246
247   if (close (cache->cache_fd) < 0)
248     goto close_failed;
249
250   if (!success)
251     goto fail_after_close;
252
253   /* Only do the rename if we wrote the entire file successfully */
254   if (g_rename (cache->tmp_location, cache->location) < 0) {
255     GST_ERROR ("g_rename() failed: %s", g_strerror (errno));
256     goto rename_failed;
257   }
258
259   g_free (cache->tmp_location);
260   g_slice_free (BinaryRegistryCache, cache);
261   GST_INFO ("Wrote binary registry cache");
262   return TRUE;
263
264 /* ERRORS */
265 fail_after_close:
266   {
267     g_unlink (cache->tmp_location);
268     g_free (cache->tmp_location);
269     g_slice_free (BinaryRegistryCache, cache);
270     return FALSE;
271   }
272 fsync_failed:
273   {
274     GST_ERROR ("fsync() failed: %s", g_strerror (errno));
275     goto fail_after_close;
276   }
277 close_failed:
278   {
279     GST_ERROR ("close() failed: %s", g_strerror (errno));
280     goto fail_after_close;
281   }
282 rename_failed:
283   {
284     GST_ERROR ("g_rename() failed: %s", g_strerror (errno));
285     goto fail_after_close;
286   }
287 }
288 #endif
289
290 /*
291  * gst_registry_binary_write_chunk:
292  *
293  * Write from a memory location to the registry cache file
294  *
295  * Returns: %TRUE for success
296  */
297 inline static gboolean
298 gst_registry_binary_write_chunk (BinaryRegistryCache * cache,
299     GstRegistryChunk * chunk, unsigned long *file_position)
300 {
301   gchar padder[ALIGNMENT] = { 0, };
302   int padsize = 0;
303
304   /* Padding to insert the struct that requiere word alignment */
305   if ((chunk->align) && (alignment (*file_position) != 0)) {
306     padsize = ALIGNMENT - alignment (*file_position);
307     if (gst_registry_binary_cache_write (cache, *file_position,
308             padder, padsize) != padsize) {
309       GST_ERROR ("Failed to write binary registry padder");
310       return FALSE;
311     }
312     *file_position += padsize;
313   }
314
315   if (gst_registry_binary_cache_write (cache, *file_position,
316           chunk->data, chunk->size) != chunk->size) {
317     GST_ERROR ("Failed to write binary registry element");
318     return FALSE;
319   }
320
321   *file_position += chunk->size;
322
323   return TRUE;
324 }
325
326
327 /*
328  * gst_registry_binary_initialize_magic:
329  *
330  * Initialize the GstBinaryRegistryMagic, setting both our magic number and
331  * gstreamer major/minor version
332  */
333 inline static gboolean
334 gst_registry_binary_initialize_magic (GstBinaryRegistryMagic * m)
335 {
336   memset (m, 0, sizeof (GstBinaryRegistryMagic));
337
338   if (!strncpy (m->magic, GST_MAGIC_BINARY_REGISTRY_STR,
339           GST_MAGIC_BINARY_REGISTRY_LEN)
340       || !strncpy (m->version, GST_MAGIC_BINARY_VERSION_STR,
341           GST_MAGIC_BINARY_VERSION_LEN)) {
342     GST_ERROR ("Failed to write magic to the registry magic structure");
343     return FALSE;
344   }
345
346   return TRUE;
347 }
348
349 /**
350  * gst_registry_binary_write_cache:
351  * @registry: a #GstRegistry
352  * @location: a filename
353  *
354  * Write the @registry to a cache to file at given @location.
355  *
356  * Returns: %TRUE on success.
357  */
358 gboolean
359 priv_gst_registry_binary_write_cache (GstRegistry * registry, GList * plugins,
360     const char *location)
361 {
362   GList *walk;
363   GstBinaryRegistryMagic magic;
364   GList *to_write = NULL;
365   unsigned long file_position = 0;
366   BinaryRegistryCache *cache;
367
368   GST_INFO ("Building binary registry cache image");
369
370   g_return_val_if_fail (GST_IS_REGISTRY (registry), FALSE);
371
372   if (!gst_registry_binary_initialize_magic (&magic))
373     goto fail;
374
375   /* iterate trough the list of plugins and fit them into binary structures */
376   for (walk = plugins; walk != NULL; walk = walk->next) {
377     GstPlugin *plugin = GST_PLUGIN (walk->data);
378
379     if (!plugin->filename)
380       continue;
381
382     if (GST_OBJECT_FLAG_IS_SET (plugin, GST_PLUGIN_FLAG_CACHED)) {
383       GStatBuf statbuf;
384
385       if (g_stat (plugin->filename, &statbuf) < 0 ||
386           plugin->file_mtime != statbuf.st_mtime ||
387           plugin->file_size != statbuf.st_size)
388         continue;
389     }
390
391     if (!_priv_gst_registry_chunks_save_plugin (&to_write, registry, plugin)) {
392       GST_ERROR ("Can't write binary plugin information for \"%s\"",
393           plugin->filename);
394     }
395   }
396
397   _priv_gst_registry_chunks_save_global_header (&to_write, registry,
398       priv_gst_plugin_loading_get_whitelist_hash ());
399
400   GST_INFO ("Writing binary registry cache");
401
402   cache = gst_registry_binary_cache_init (registry, location);
403   if (!cache)
404     goto fail_free_list;
405
406   /* write magic */
407   if (gst_registry_binary_cache_write (cache, file_position,
408           &magic, sizeof (GstBinaryRegistryMagic)) !=
409       sizeof (GstBinaryRegistryMagic)) {
410     GST_ERROR ("Failed to write binary registry magic");
411     goto fail_free_list;
412   }
413   file_position += sizeof (GstBinaryRegistryMagic);
414
415   /* write out data chunks */
416   for (walk = to_write; walk; walk = g_list_next (walk)) {
417     GstRegistryChunk *cur = walk->data;
418     gboolean res;
419
420     res = gst_registry_binary_write_chunk (cache, cur, &file_position);
421
422     _priv_gst_registry_chunk_free (cur);
423     walk->data = NULL;
424     if (!res)
425       goto fail_free_list;
426   }
427   g_list_free (to_write);
428
429   if (!gst_registry_binary_cache_finish (cache, TRUE))
430     return FALSE;
431
432   return TRUE;
433
434   /* Errors */
435 fail_free_list:
436   {
437     for (walk = to_write; walk; walk = g_list_next (walk)) {
438       GstRegistryChunk *cur = walk->data;
439
440       if (cur)
441         _priv_gst_registry_chunk_free (cur);
442     }
443     g_list_free (to_write);
444
445     if (cache)
446       (void) gst_registry_binary_cache_finish (cache, FALSE);
447     /* fall through */
448   }
449 fail:
450   {
451     return FALSE;
452   }
453 }
454
455
456 /* Registry loading */
457
458 /*
459  * gst_registry_binary_check_magic:
460  *
461  * Check GstBinaryRegistryMagic validity.
462  * Return < 0 if something is wrong, -2 means
463  * that just the version of the registry is out of
464  * date, -1 is a general failure.
465  */
466 static gint
467 gst_registry_binary_check_magic (gchar ** in, gsize size)
468 {
469   GstBinaryRegistryMagic *m;
470
471   align (*in);
472   GST_DEBUG ("Reading/casting for GstBinaryRegistryMagic at address %p", *in);
473   unpack_element (*in, m, GstBinaryRegistryMagic, (*in + size), fail);
474
475   if (strncmp (m->magic, GST_MAGIC_BINARY_REGISTRY_STR,
476           GST_MAGIC_BINARY_REGISTRY_LEN) != 0) {
477     GST_WARNING
478         ("Binary registry magic is different : %02x%02x%02x%02x != %02x%02x%02x%02x",
479         GST_MAGIC_BINARY_REGISTRY_STR[0] & 0xff,
480         GST_MAGIC_BINARY_REGISTRY_STR[1] & 0xff,
481         GST_MAGIC_BINARY_REGISTRY_STR[2] & 0xff,
482         GST_MAGIC_BINARY_REGISTRY_STR[3] & 0xff, m->magic[0] & 0xff,
483         m->magic[1] & 0xff, m->magic[2] & 0xff, m->magic[3] & 0xff);
484     return -1;
485   }
486   if (strncmp (m->version, GST_MAGIC_BINARY_VERSION_STR,
487           GST_MAGIC_BINARY_VERSION_LEN)) {
488     GST_WARNING ("Binary registry magic version is different : %s != %s",
489         GST_MAGIC_BINARY_VERSION_STR, m->version);
490     return -2;
491   }
492
493   return 0;
494
495 fail:
496   GST_WARNING ("Not enough data for binary registry magic structure");
497   return -1;
498 }
499
500 /**
501  * gst_registry_binary_read_cache:
502  * @registry: a #GstRegistry
503  * @location: a filename
504  *
505  * Read the contents of the binary cache file at @location into @registry.
506  *
507  * Returns: %TRUE on success.
508  */
509 gboolean
510 priv_gst_registry_binary_read_cache (GstRegistry * registry,
511     const char *location)
512 {
513   GMappedFile *mapped = NULL;
514   gchar *contents = NULL;
515   gchar *in = NULL;
516   gsize size;
517   GError *err = NULL;
518   gboolean res = FALSE;
519   guint32 filter_env_hash = 0;
520   gint check_magic_result;
521 #ifndef GST_DISABLE_GST_DEBUG
522   GTimer *timer = NULL;
523   gdouble seconds;
524 #endif
525
526   /* make sure these types exist */
527   GST_TYPE_ELEMENT_FACTORY;
528   GST_TYPE_TYPE_FIND_FACTORY;
529   GST_TYPE_DEVICE_PROVIDER_FACTORY;
530
531 #ifndef GST_DISABLE_GST_DEBUG
532   timer = g_timer_new ();
533 #endif
534
535   mapped = g_mapped_file_new (location, FALSE, &err);
536   if (G_UNLIKELY (err != NULL)) {
537     GST_INFO ("Unable to mmap file %s : %s", location, err->message);
538     g_error_free (err);
539     err = NULL;
540   }
541
542   if (mapped == NULL) {
543     /* Error mmap-ing the cache, try a plain memory read */
544
545     g_file_get_contents (location, &contents, &size, &err);
546     if (err != NULL) {
547       GST_INFO ("Unable to read file %s : %s", location, err->message);
548 #ifndef GST_DISABLE_GST_DEBUG
549       g_timer_destroy (timer);
550 #endif
551       g_error_free (err);
552       return FALSE;
553     }
554   } else {
555     /* This can't fail if g_mapped_file_new() succeeded */
556     contents = g_mapped_file_get_contents (mapped);
557     size = g_mapped_file_get_length (mapped);
558   }
559
560   /* in is a cursor pointer, we initialize it with the begin of registry and is updated on each read */
561   in = contents;
562   GST_DEBUG ("File data at address %p", in);
563   if (G_UNLIKELY (size < sizeof (GstBinaryRegistryMagic))) {
564     GST_ERROR ("No or broken registry header for file at %s", location);
565     goto Error;
566   }
567
568   /* check if header is valid */
569   if (G_UNLIKELY ((check_magic_result =
570               gst_registry_binary_check_magic (&in, size)) < 0)) {
571
572     if (check_magic_result == -1)
573       GST_ERROR
574           ("Binary registry type not recognized (invalid magic) for file at %s",
575           location);
576     goto Error;
577   }
578
579   if (!_priv_gst_registry_chunks_load_global_header (registry, &in,
580           contents + size, &filter_env_hash)) {
581     GST_ERROR ("Couldn't read global header chunk");
582     goto Error;
583   }
584
585   if (filter_env_hash != priv_gst_plugin_loading_get_whitelist_hash ()) {
586     GST_INFO_OBJECT (registry, "Plugin loading filter environment changed, "
587         "ignoring plugin cache to force update with new filter environment");
588     goto done;
589   }
590
591   /* check if there are plugins in the file */
592   if (G_UNLIKELY (!(((gsize) in + sizeof (GstRegistryChunkPluginElement)) <
593               (gsize) contents + size))) {
594     GST_INFO ("No binary plugins structure to read");
595     /* empty file, this is not an error */
596   } else {
597     gchar *end = contents + size;
598     /* read as long as we still have space for a GstRegistryChunkPluginElement */
599     for (;
600         ((gsize) in + sizeof (GstRegistryChunkPluginElement)) <
601         (gsize) contents + size;) {
602       GST_DEBUG ("reading binary registry %" G_GSIZE_FORMAT "(%x)/%"
603           G_GSIZE_FORMAT, (gsize) in - (gsize) contents,
604           (guint) ((gsize) in - (gsize) contents), size);
605       if (!_priv_gst_registry_chunks_load_plugin (registry, &in, end, NULL)) {
606         GST_ERROR ("Problem while reading binary registry %s", location);
607         goto Error;
608       }
609     }
610   }
611
612 done:
613
614 #ifndef GST_DISABLE_GST_DEBUG
615   g_timer_stop (timer);
616   seconds = g_timer_elapsed (timer, NULL);
617 #endif
618
619   GST_INFO ("loaded %s in %lf seconds", location, seconds);
620
621   res = TRUE;
622   /* TODO: once we re-use the pointers to registry contents, return here */
623
624 Error:
625 #ifndef GST_DISABLE_GST_DEBUG
626   g_timer_destroy (timer);
627 #endif
628   if (mapped) {
629     g_mapped_file_unref (mapped);
630   } else {
631     g_free (contents);
632   }
633   return res;
634 }