qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 1/3] cutils: Add multiplatform capability to resolve


From: Jeff Cody
Subject: [Qemu-devel] [PATCH 1/3] cutils: Add multiplatform capability to resolve canonical filenames
Date: Tue, 17 Apr 2012 09:17:12 -0400

This patch is essentially Paolo Bonzini's patch submission for glib,
to add the ability to resolve canonical filenames:

https://bugzilla.gnome.org/show_bug.cgi?id=111848#c23

The differences are some minor bug fixes, and style changes for QEMU.

In addition, a configure check for readlink was added.

Signed-off-by: Jeff Cody <address@hidden>
---
 configure     |   21 +++++
 cutils.c      |  270 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 qemu-common.h |    8 ++
 3 files changed, 299 insertions(+), 0 deletions(-)

diff --git a/configure b/configure
index 671b232..6b204de 100755
--- a/configure
+++ b/configure
@@ -2660,6 +2660,23 @@ if compile_prog "" "" ; then
 fi
 
 ##########################################
+# check if we have readlink
+
+posix_readlink=no
+cat > $TMPC << EOF
+#include <unistd.h>
+
+int main(int argc, char **argv) {
+    char buf[1024];
+    readlink("/test/path", buf, sizeof(buf));
+    return 0;
+}
+EOF
+if compile_prog "" "" ; then
+    posix_readlink=yes
+fi
+
+##########################################
 # check if trace backend exists
 
 sh "$source_path/scripts/tracetool" "--$trace_backend" --check-backend > 
/dev/null 2> /dev/null
@@ -3298,6 +3315,10 @@ if test "$linux_magic_h" = "yes" ; then
   echo "CONFIG_LINUX_MAGIC_H=y" >> $config_host_mak
 fi
 
+if test "$posix_readlink" = "yes" ; then
+  echo "CONFIG_READLINK=y" >> $config_host_mak
+fi
+
 # USB host support
 case "$usb" in
 linux)
diff --git a/cutils.c b/cutils.c
index af308cd..dd82375 100644
--- a/cutils.c
+++ b/cutils.c
@@ -24,6 +24,10 @@
 #include "qemu-common.h"
 #include "host-utils.h"
 #include <math.h>
+#include <libgen.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/param.h>
 
 #include "qemu_socket.h"
 
@@ -549,3 +553,269 @@ int qemu_sendv(int sockfd, struct iovec *iov, int len, 
int iov_offset)
     return do_sendv_recvv(sockfd, iov, len, iov_offset, 1);
 }
 
+/**
+ * g_get_full_path:
+ * @program: a file name in the GLib file name encoding
+ *
+ * Returns an allocated string with the full path corresponding to the
+ * possibly relative @path in the current directory, or %NULL if an
+ * error happens, for example while following a symbolic link.
+ *
+ * @flags allows to modify the behavior of the function.  Right now
+ * the only valid flag is %QEMU_FILE_PATH_RESOLVE_SYMLINKS, which
+ * directs the function to resolve symbolic links.  "." and ".."
+ * directory entries are always resolved.
+ *
+ * If %NULL is returned, an error will be optionally stored in @error,
+ * if it is not %NULL.
+ *
+ * Return value: absolute path, or %NULL
+ *
+ **/
+gchar *qemu_file_get_full_path(const gchar *path,
+                               QEMUFileFullPathFlags flags,
+                               GError **error)
+{
+    gchar *result = NULL;
+    gchar *extra_buf = NULL;
+#ifdef _WIN32
+    wchar_t dummy[2], *wpath, *wresult;
+    int len;
+
+    if (error) {
+        *error = NULL;
+    }
+
+    g_return_val_if_fail(!(flags & ~QEMU_FILE_PATH_RESOLVE_SYMLINKS), NULL);
+    wpath = g_utf8_to_utf16(path, -1, NULL, NULL, error);
+    if (!wpath) {
+        return NULL;
+    }
+
+    len = GetFullPathNameW(wpath, 2, dummy, NULL);
+    if (len != 0) {
+        wresult = g_new(wchar_t, len);
+        len = GetFullPathNameW(wpath, len, wresult, NULL);
+
+        /* There is a tiny difference between the code below and
+           GetFullPathName; work around it.  The 3 is for "d:\" */
+        while (len > 3 && wresult[len - 1] == L'\\') {
+            wresult[--len] = L'\0';
+        }
+
+        if (len != 0) {
+            result = g_utf16_to_utf8(wresult, -1, NULL, NULL, error);
+        } else {
+            result = NULL;
+        }
+
+        g_free(wresult);
+    }
+
+    g_free(wpath);
+
+    /* Shouldn't happen, how could GetFullPathNameW fail?  Try to
+       be sensible, though.  */
+    if (!result && !(error && *error)) {
+        goto error_exit_ENAMETOOLONG;
+    }
+    return result;
+#else
+    gchar *dest;
+    const gchar *start, *end;
+    guint size;
+    gboolean must_end;
+    struct stat st;
+#ifdef CONFIG_READLINK
+    int num_links = 0;
+    gsize len, n;
+    gchar *buf;
+#endif
+
+    if (error) {
+        *error = NULL;
+    }
+
+    g_return_val_if_fail(!(flags & ~QEMU_FILE_PATH_RESOLVE_SYMLINKS), NULL);
+    size = PATH_MAX;
+    result = g_malloc(size);
+
+    if (path[0] != '/') {
+        if (!getcwd(result, size) || !result[0]) {
+            goto error_exit;
+        }
+
+        dest = strchr(result, '\0');
+    } else {
+        result[0] = '/';
+        dest = result + 1;
+    }
+
+    /* must_end signals that the last part of result..dest is not a directory,
+       and hence should terminate the string.  */
+    for (must_end = FALSE, start = end = path; *start; start = end) {
+        if (must_end) {
+            errno = ENOTDIR;
+            goto error_exit;
+        }
+
+        /* Skip sequence of multiple path-separators.  Exit if there is nothing
+           else after them.  */
+        while (*start == '/') {
+            ++start;
+        }
+        if (!*start) {
+            break;
+        }
+
+        /* Find end of path component, handle special cases "." and "..".  */
+        end = start + 1;
+        while (*end && *end != '/') {
+            ++end;
+        }
+
+        if (end - start == 1 && start[0] == '.') {
+            must_end = FALSE;
+            continue;
+        }
+
+        if (end - start == 2 && start[0] == '.' && start[1] == '.') {
+            /* Back up to previous component, ignore if at root already.  */
+            while (dest > result + 1 && dest[-1] == '/') {
+                dest--;
+            }
+            do {
+                --dest;
+            } while (dest > result + 1 && dest[-1] != '/');
+            must_end = FALSE;
+            continue;
+        }
+
+        /* Tack on the next component.  */
+        if (dest[-1] != '/') {
+            *dest++ = '/';
+        }
+
+        if ((dest - result) + (end - start) >= size) {
+            gsize dest_offset = dest - result;
+            gsize new_size;
+
+            /* Look out for overflow.  */
+            if (end - start + 1 > size || size * 2 < size) {
+                new_size = size + end - start + 1;
+            } else {
+                new_size = size * 2;
+            }
+            if (new_size < size) {
+                goto error_exit_ENAMETOOLONG;
+            }
+
+            result = (char *) g_realloc(result, new_size);
+            dest = result + dest_offset;
+        }
+
+        memcpy(dest, start, end - start);
+        dest += end - start;
+        *dest = '\0';
+        if ((flags & QEMU_FILE_PATH_RESOLVE_SYMLINKS) == 0) {
+            continue;
+        }
+
+        /* A non-existent file is okay at the last level.  */
+        if (lstat(result, &st) < 0) {
+            if (errno != ENOENT) {
+                goto error_exit;
+            }
+
+            must_end = TRUE;
+            continue;
+        }
+
+#ifdef CONFIG_READLINK
+        if (!S_ISLNK(st.st_mode))
+#endif
+        {
+            must_end = !S_ISDIR(st.st_mode);
+            continue;
+        }
+
+#ifdef CONFIG_READLINK
+        if (num_links++ > MAXSYMLINKS) {
+            errno = ELOOP;
+            goto error_exit;
+        }
+
+        buf = g_file_read_link(result, error);
+        if (!buf) {
+            return NULL;
+        }
+
+        n = strlen(buf);
+        len = strlen(end);
+        if (!extra_buf) {
+            extra_buf = g_malloc(PATH_MAX);
+        }
+        if (n + len > PATH_MAX) {
+            goto error_exit_ENAMETOOLONG;
+        }
+
+        /* Careful here, end may be a pointer into extra_buf.  We have to
+           copy the tail first, and then place the symlink's destination.  */
+        g_memmove(&extra_buf[n], end, len + 1);
+        memcpy(extra_buf, buf, n);
+        end = extra_buf;
+
+        /* Is it an absolute symlink?  If not, and if not at root already,
+           back up to the previous component.  Else reparse.  */
+        if (buf[0] == '/') {
+            dest = result + 1;
+        } else {
+            while (dest > result + 1 && dest[-1] == '/') {
+                dest--;
+            }
+            do {
+                --dest;
+            } while (dest > result + 1 && dest[-1] != '/');
+        }
+
+        g_free(buf);
+        must_end = FALSE;
+#endif
+    }
+
+    while (dest > result + 1 && dest[-1] == '/') {
+        --dest;
+    }
+    *dest = '\0';
+    g_free(extra_buf);
+#endif
+
+    return result;
+
+error_exit_ENAMETOOLONG:
+#ifdef ENAMETOOLONG
+    errno = ENAMETOOLONG;
+#else
+    errno = EINVAL;
+#endif
+
+error_exit: G_GNUC_UNUSED
+    if (error && !*error) {
+        int save_errno = errno;
+        gchar *display_path = g_filename_display_name(path);
+
+        g_set_error(error,
+                    G_FILE_ERROR,
+                    g_file_error_from_errno(save_errno),
+                    "Failed to resolve path '%s': %s",
+                    display_path,
+                    g_strerror(save_errno));
+    }
+
+    g_free(result);
+    g_free(extra_buf);
+    return NULL;
+}
+
+
+
diff --git a/qemu-common.h b/qemu-common.h
index 4647dd9..3c2af73 100644
--- a/qemu-common.h
+++ b/qemu-common.h
@@ -352,6 +352,14 @@ void qemu_progress_init(int enabled, float min_skip);
 void qemu_progress_end(void);
 void qemu_progress_print(float delta, int max);
 
+typedef enum {
+  QEMU_FILE_PATH_RESOLVE_SYMLINKS = 1 << 0
+} QEMUFileFullPathFlags;
+
+gchar *qemu_file_get_full_path(const gchar *path,
+                               QEMUFileFullPathFlags flags,
+                               GError **error);
+
 #define QEMU_FILE_TYPE_BIOS   0
 #define QEMU_FILE_TYPE_KEYMAP 1
 char *qemu_find_file(int type, const char *name);
-- 
1.7.9.rc2.1.g69204




reply via email to

[Prev in Thread] Current Thread [Next in Thread]