[libvirt] [PATCH 2/2] Add impl of APIs to get user directories on Win32

Daniel P. Berrange berrange at redhat.com
Thu May 24 13:44:33 UTC 2012


From: "Daniel P. Berrange" <berrange at redhat.com>

Add an impl of +virGetUserRuntimeDirectory, virGetUserCacheDirectory
virGetUserConfigDirectory and virGetUserDirectory for Win32 platform.
Also create stubs for non-Win32 platforms which lack getpwuid_r()

In adding these two helpers were added virFileIsAbsPath and
virFileSkipRoot, along with some macros VIR_FILE_DIR_SEPARATOR,
VIR_FILE_DIR_SEPARATOR_S, VIR_FILE_IS_DIR_SEPARATOR,
VIR_FILE_PATH_SEPARATOR, VIR_FILE_PATH_SEPARATOR_S

All this code was adapted from GLib2 under terms of LGPLv2+ license.

Signed-off-by: Daniel P. Berrange <berrange at redhat.com>
---
 src/libvirt_private.syms |    2 +
 src/util/util.c          |  253 +++++++++++++++++++++++++++++++++++++++++++++-
 src/util/util.h          |   24 +++++
 3 files changed, 277 insertions(+), 2 deletions(-)

diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms
index 3bf0794..c6fe0e3 100644
--- a/src/libvirt_private.syms
+++ b/src/libvirt_private.syms
@@ -1131,6 +1131,7 @@ virFileBuildPath;
 virFileExists;
 virFileFindMountPoint;
 virFileHasSuffix;
+virFileIsAbsPath;
 virFileIsExecutable;
 virFileIsLink;
 virFileIsDir;
@@ -1145,6 +1146,7 @@ virFileReadLimFD;
 virFileResolveAllLinks;
 virFileResolveLink;
 virFileSanitizePath;
+virFileSkipRoot;
 virFileStripSuffix;
 virFileUnlock;
 virFileWaitForDevices;
diff --git a/src/util/util.c b/src/util/util.c
index afe5a8c..28a4fe7 100644
--- a/src/util/util.c
+++ b/src/util/util.c
@@ -64,6 +64,11 @@
 # include <mntent.h>
 #endif
 
+#ifdef WIN32
+# include <windows.h>
+# include <shlobj.h>
+#endif
+
 #include "c-ctype.h"
 #include "dirname.h"
 #include "virterror_internal.h"
@@ -1411,6 +1416,77 @@ int virFileOpenTty(int *ttymaster ATTRIBUTE_UNUSED,
 }
 #endif /* WIN32 */
 
+bool virFileIsAbsPath(const char *path)
+{
+    if (!path)
+        return false;
+
+    if (VIR_FILE_IS_DIR_SEPARATOR(path[0]))
+        return true;
+
+#ifdef WIN32
+    if (c_isalpha(path[0]) &&
+        path[1] == ':' &&
+        VIR_FILE_IS_DIR_SEPARATOR(path[2]))
+        return true;
+#endif
+
+    return false;
+}
+
+
+const char *virFileSkipRoot(const char *path)
+{
+#ifdef WIN32
+    /* Skip \\server\share or //server/share */
+    if (VIR_FILE_IS_DIR_SEPARATOR(path[0]) &&
+        VIR_FILE_IS_DIR_SEPARATOR(path[1]) &&
+        path[2] &&
+        !VIR_FILE_IS_DIR_SEPARATOR(path[2]))
+    {
+        const char *p = strchr(path + 2, VIR_FILE_DIR_SEPARATOR);
+        const char *q = strchr(path + 2, '/');
+
+        if (p == NULL || (q != NULL && q < p))
+            p = q;
+
+        if (p && p > path + 2 && p[1]) {
+            path = p + 1;
+
+            while (path[0] &&
+                   !VIR_FILE_IS_DIR_SEPARATOR(path[0]))
+                path++;
+
+            /* Possibly skip a backslash after the share name */
+            if (VIR_FILE_IS_DIR_SEPARATOR(path[0]))
+                path++;
+
+            return path;
+        }
+    }
+#endif
+
+    /* Skip initial slashes */
+    if (VIR_FILE_IS_DIR_SEPARATOR(path[0])) {
+        while (VIR_FILE_IS_DIR_SEPARATOR(path[0]))
+            path++;
+
+        return path;
+    }
+
+#ifdef WIN32
+    /* Skip X:\ */
+    if (c_isalpha(path[0]) &&
+        path[1] == ':' &&
+        VIR_FILE_IS_DIR_SEPARATOR(path[2]))
+        return path + 3;
+#endif
+
+    return path;
+}
+
+
+
 /*
  * Creates an absolute path for a potentially relative path.
  * Return 0 if the path was not relative, or on success.
@@ -2305,7 +2381,7 @@ static char *virGetGroupEnt(gid_t gid)
     return ret;
 }
 
-char *virGetUserDirectory()
+char *virGetUserDirectory(void)
 {
     return virGetUserEnt(geteuid(), VIR_USER_ENT_DIRECTORY);
 }
@@ -2542,11 +2618,156 @@ error:
     return -1;
 }
 
-#else /* HAVE_GETPWUID_R */
+#else /* ! HAVE_GETPWUID_R */
+
+# ifdef WIN32
+/* These methods are adapted from GLib2 under terms of LGPLv2+ */
+static int
+virGetWin32SpecialFolder(int csidl, char **path)
+{
+    char buf[MAX_PATH+1];
+    LPITEMIDLIST pidl = NULL;
+    int ret = 0;
+
+    *path = NULL;
+
+    if (SHGetSpecialFolderLocation(NULL, csidl, &pidl) == S_OK) {
+        if (SHGetPathFromIDList(pidl, buf)) {
+            if (!(*path = strdup(buf))) {
+                virReportOOMError();
+                ret = -1;
+            }
+        }
+        CoTaskMemFree(pidl);
+    }
+    return ret;
+}
+
+static int
+virGetWin32DirectoryRoot(char **path)
+{
+    char windowsdir[MAX_PATH];
+    int ret = 0;
+
+    *path = NULL;
+
+    if (GetWindowsDirectory(windowsdir, ARRAY_CARDINALITY(windowsdir)))
+    {
+        const char *tmp;
+        /* Usually X:\Windows, but in terminal server environments
+         * might be an UNC path, AFAIK.
+         */
+        tmp = virFileSkipRoot(windowsdir);
+        if (VIR_FILE_IS_DIR_SEPARATOR(tmp[-1]) &&
+            tmp[-2] != ':')
+            tmp--;
+
+        windowsdir[tmp - windowsdir] = '\0';
+    } else {
+        strcpy(windowsdir, "C:\\");
+    }
+
+    if (!(*path = strdup(windowsdir))) {
+        virReportOOMError();
+        ret = -1;
+    }
+
+    return ret;
+}
+
+
 
 char *
 virGetUserDirectory(void)
 {
+    const char *dir;
+    char *ret;
+
+    dir = getenv("HOME");
+
+    /* Only believe HOME if it is an absolute path and exists */
+    if (dir) {
+        if (!virFileIsAbsPath(dir) ||
+            !virFileExists(dir))
+            dir = NULL;
+    }
+
+    /* In case HOME is Unix-style (it happens), convert it to
+     * Windows style.
+     */
+    if (dir) {
+        char *p;
+        while ((p = strchr (dir, '/')) != NULL)
+            *p = '\\';
+    }
+
+    if (!dir)
+        /* USERPROFILE is probably the closest equivalent to $HOME? */
+        dir = getenv("USERPROFILE");
+
+    if (dir) {
+        if (!(ret = strdup(dir))) {
+            virReportOOMError();
+            return NULL;
+        }
+    }
+
+    if (!ret &&
+        virGetWin32SpecialFolder(CSIDL_PROFILE, &ret) < 0)
+        return NULL;
+
+    if (!ret &&
+        virGetWin32DirectoryRoot(&ret) < 0)
+        return NULL;
+
+    if (!ret) {
+        virUtilError(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("Unable to determine home directory"));
+        return NULL;
+    }
+
+    return ret;
+}
+
+char *
+virGetUserConfigDirectory(void)
+{
+    char *ret;
+    if (virGetWin32SpecialFolder(CSIDL_LOCAL_APPDATA, &ret) < 0)
+        return NULL;
+
+    if (!ret) {
+        virUtilError(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("Unable to determine config directory"));
+        return NULL;
+    }
+    return ret;
+}
+
+char *
+virGetUserCacheDirectory(void)
+{
+    char *ret;
+    if (virGetWin32SpecialFolder(CSIDL_INTERNET_CACHE, &ret) < 0)
+        return NULL;
+
+    if (!ret) {
+        virUtilError(VIR_ERR_INTERNAL_ERROR, "%s",
+                     _("Unable to determine config directory"));
+        return NULL;
+    }
+    return ret;
+}
+
+char *
+virGetUserRuntimeDirectory(void)
+{
+    return virGetUserCacheDirectory();
+}
+# else /* !HAVE_GETPWUID_R && !WIN32 */
+char *
+virGetUserDirectory(void)
+{
     virUtilError(VIR_ERR_INTERNAL_ERROR,
                  "%s", _("virGetUserDirectory is not available"));
 
@@ -2554,6 +2775,34 @@ virGetUserDirectory(void)
 }
 
 char *
+virGetUserConfigDirectory(void)
+{
+    virUtilError(VIR_ERR_INTERNAL_ERROR,
+                 "%s", _("virGetUserConfigDirectory is not available"));
+
+    return NULL;
+}
+
+char *
+virGetUserCacheDirectory(void)
+{
+    virUtilError(VIR_ERR_INTERNAL_ERROR,
+                 "%s", _("virGetUserCacheDirectory is not available"));
+
+    return NULL;
+}
+
+char *
+virGetUserRuntimeDirectory(void)
+{
+    virUtilError(VIR_ERR_INTERNAL_ERROR,
+                 "%s", _("virGetUserRuntimeDirectory is not available"));
+
+    return NULL;
+}
+# endif /* ! HAVE_GETPWUID_R && ! WIN32 */
+
+char *
 virGetUserName(uid_t uid ATTRIBUTE_UNUSED)
 {
     virUtilError(VIR_ERR_INTERNAL_ERROR,
diff --git a/src/util/util.h b/src/util/util.h
index b45f2b9..0af7e6d 100644
--- a/src/util/util.h
+++ b/src/util/util.h
@@ -120,8 +120,32 @@ char *virFileBuildPath(const char *dir,
                        const char *name,
                        const char *ext) ATTRIBUTE_RETURN_CHECK;
 
+
+# ifdef WIN32
+/* On Win32, the canonical directory separator is the backslash, and
+ * the search path separator is the semicolon. Note that also the
+ * (forward) slash works as directory separator.
+ */
+#  define VIR_FILE_DIR_SEPARATOR '\\'
+#  define VIR_FILE_DIR_SEPARATOR_S "\\"
+#  define VIR_FILE_IS_DIR_SEPARATOR(c) ((c) == VIR_FILE_DIR_SEPARATOR || (c) == '/')
+#  define VIR_FILE_PATH_SEPARATOR ';'
+#  define VIR_FILE_PATH_SEPARATOR_S ";"
+
+# else  /* !WIN32 */
+
+#  define VIR_FILE_DIR_SEPARATOR '/'
+#  define VIR_FILE_DIR_SEPARATOR_S "/"
+#  define VIR_FILE_IS_DIR_SEPARATOR(c) ((c) == VIR_FILE_DIR_SEPARATOR)
+#  define VIR_FILE_PATH_SEPARATOR ':'
+#  define VIR_FILE_PATH_SEPARATOR_S ":"
+
+# endif /* !WIN32 */
+
+bool virFileIsAbsPath(const char *path);
 int virFileAbsPath(const char *path,
                    char **abspath) ATTRIBUTE_RETURN_CHECK;
+const char *virFileSkipRoot(const char *path);
 
 int virFileOpenTty(int *ttymaster,
                    char **ttyName,
-- 
1.7.7.6




More information about the libvir-list mailing list