diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 9185e49fda..d8f049b960 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1504,6 +1504,7 @@ virSecurityManagerVerify; # util/glibcompat.h +vir_g_canonicalize_filename; vir_g_fsync; vir_g_strdup_printf; vir_g_strdup_vprintf; diff --git a/src/util/glibcompat.c b/src/util/glibcompat.c index 049ac587ca..c61772f953 100644 --- a/src/util/glibcompat.c +++ b/src/util/glibcompat.c @@ -19,15 +19,121 @@ #include #include +#include #include #include "glibcompat.h" +#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) diff --git a/src/util/glibcompat.h b/src/util/glibcompat.h index ce31a4de04..6f50a76f3c 100644 --- a/src/util/glibcompat.h +++ b/src/util/glibcompat.h @@ -21,6 +21,8 @@ #include #include +gchar * vir_g_canonicalize_filename(const gchar *filename, + const gchar *relative_to); gint vir_g_fsync(gint fd); char *vir_g_strdup_printf(const char *msg, ...) G_GNUC_PRINTF(1, 2); @@ -32,5 +34,6 @@ char *vir_g_strdup_vprintf(const char *msg, va_list args) # define g_strdup_vprintf vir_g_strdup_vprintf #endif +#define g_canonicalize_filename vir_g_canonicalize_filename #undef g_fsync #define g_fsync vir_g_fsync