libvirt/src/util/glibcompat.c
Daniel P. Berrangé f08d5dada4 util: introduce compile time API version checking
GLib header files annotate every API with a version number.

It is possible to define some constants before including
glib.h which will result in useful compile time warnings.

Setting GLIB_VERSION_MIN_REQUIRED will result in a warning
if libvirt uses an API that was deprecated in the declared
version, or before. Such API usage should be rewritten to
use the documented new replacement API.

Setting GLIB_VERSION_MAX_ALLOWED will result in a warning
if libvirt uses an API that was not introduced until a
version of GLib that's newer than our minimum declared
version. This avoids accidentally using functionality
that is not available on some supported platforms.

Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
Reviewed-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2020-01-07 14:42:26 +00:00

214 lines
5.7 KiB
C

/*
* Copyright (C) 2019 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "glibcompat.h"
/*
* Note that because of the GLIB_VERSION_MAX_ALLOWED constant in
* config-post.h, allowing use of functions from newer GLib via
* this compat impl needs a little trickery to prevent warnings
* being emitted.
*
* Consider a function from newer glib-X.Y that we want to use
*
* int g_foo(const char *wibble)
*
* We must define a function with the same signature that does
* what we need, but with a "vir_" prefix e.g.
*
* void vir_g_foo(const char *wibble)
* {
* #if GLIB_CHECK_VERSION(X, Y, 0)
* g_foo(wibble)
* #else
* g_something_equivalent_in_older_glib(wibble);
* #endif
* }
*
* The #pragma at the top of this file turns off -Wdeprecated-declarations,
* ensuring this wrapper function impl doesn't trigger the compiler
* warning about using too new glib APIs. Finally in glibcompat.h we can
* add
*
* #define g_foo(a) vir_g_foo(a)
*
* Thus all the code elsewhere in libvirt, which *does* have the
* -Wdeprecated-declarations warning active, can call g_foo(...) as
* normal, without generating warnings. The cost is an extra function
* call when using new glib, but this compat code will go away over
* time as we update the supported platforms target.
*/
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#undef g_canonicalize_filename
#undef g_fsync
#undef g_strdup_printf
#undef g_strdup_vprintf
gchar *
vir_g_canonicalize_filename(const gchar *filename,
const gchar *relative_to)
{
#if GLIB_CHECK_VERSION(2, 58, 0)
return g_canonicalize_filename(filename, relative_to);
#else /* ! GLIB_CHECK_VERSION(2, 58, 0) */
gchar *canon, *start, *p, *q;
guint i;
g_return_val_if_fail(relative_to == NULL || g_path_is_absolute(relative_to), NULL);
if (!g_path_is_absolute(filename)) {
gchar *cwd_allocated = NULL;
const gchar *cwd;
if (relative_to != NULL)
cwd = relative_to;
else
cwd = cwd_allocated = g_get_current_dir();
canon = g_build_filename(cwd, filename, NULL);
g_free(cwd_allocated);
} else {
canon = g_strdup(filename);
}
start = (char *)g_path_skip_root(canon);
if (start == NULL) {
/* This shouldn't really happen, as g_get_current_dir() should
return an absolute pathname, but bug 573843 shows this is
not always happening */
g_free(canon);
return g_build_filename(G_DIR_SEPARATOR_S, filename, NULL);
}
/* POSIX allows double slashes at the start to
* mean something special (as does windows too).
* So, "//" != "/", but more than two slashes
* is treated as "/".
*/
i = 0;
for (p = start - 1;
(p >= canon) &&
G_IS_DIR_SEPARATOR(*p);
p--)
i++;
if (i > 2) {
i -= 1;
start -= i;
memmove(start, start+i, strlen(start+i) + 1);
}
/* Make sure we're using the canonical dir separator */
p++;
while (p < start && G_IS_DIR_SEPARATOR(*p))
*p++ = G_DIR_SEPARATOR;
p = start;
while (*p != 0) {
if (p[0] == '.' && (p[1] == 0 || G_IS_DIR_SEPARATOR(p[1]))) {
memmove(p, p+1, strlen(p+1)+1);
} else if (p[0] == '.' && p[1] == '.' &&
(p[2] == 0 || G_IS_DIR_SEPARATOR(p[2]))) {
q = p + 2;
/* Skip previous separator */
p = p - 2;
if (p < start)
p = start;
while (p > start && !G_IS_DIR_SEPARATOR(*p))
p--;
if (G_IS_DIR_SEPARATOR(*p))
*p++ = G_DIR_SEPARATOR;
memmove(p, q, strlen(q)+1);
} else {
/* Skip until next separator */
while (*p != 0 && !G_IS_DIR_SEPARATOR(*p))
p++;
if (*p != 0) {
/* Canonicalize one separator */
*p++ = G_DIR_SEPARATOR;
}
}
/* Remove additional separators */
q = p;
while (*q && G_IS_DIR_SEPARATOR(*q))
q++;
if (p != q)
memmove(p, q, strlen(q) + 1);
}
/* Remove trailing slashes */
if (p > start && G_IS_DIR_SEPARATOR(*(p-1)))
*(p-1) = 0;
return canon;
#endif /* ! GLIB_CHECK_VERSION(2, 58, 0) */
}
/* Drop when min glib >= 2.63.0 */
gint
vir_g_fsync(gint fd)
{
#ifdef G_OS_WIN32
return _commit(fd);
#else
return fsync(fd);
#endif
}
/* Due to a bug in glib, g_strdup_printf() nor g_strdup_vprintf()
* abort on OOM. It's fixed in glib's upstream. Provide our own
* implementation until the fix gets distributed. */
char *
vir_g_strdup_printf(const char *msg, ...)
{
va_list args;
char *ret;
va_start(args, msg);
ret = g_strdup_vprintf(msg, args);
if (!ret)
abort();
va_end(args);
return ret;
}
char *
vir_g_strdup_vprintf(const char *msg, va_list args)
{
char *ret;
ret = g_strdup_vprintf(msg, args);
if (!ret)
abort();
return ret;
}