From: "Daniel P. Berrange" <berrange@xxxxxxxxxx> 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@xxxxxxxxxx> --- 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 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list