[Top][All Lists]
[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