Without this patch, at least tests/daemon-conf (which sticks $builddir/src in the PATH) tries to execute the directory $builddir/src/qemu rather than the real /usr/bin/qemu binary.
That was fine when qemu_capabilities silently ignored execution failure, but not so good once it is converted to use virCommand. * src/util/util.h (virFileExists): Adjust prototype. (virFileIsExecutable): New prototype. * src/util/util.c (virFindFileInPath): Reject non-executables and directories. Avoid huge stack allocation. (virFileExists): Use lighter-weight syscall. (virFileIsExecutable): New function. * src/libvirt_private.syms (util.h): Export new function. --- Questions: Should I be requiring S_ISREG, rather than !S_ISDIR (that is, should we be rejecting devices and sockets as non-exectuable)? Should I import the gnulib module euidaccess (and/or faccessat) for the access check? Using access(F_OK) is okay regardless of uid/gid, but access(X_OK) may have different answers than euidaccess(X_OK) when the effective uid/gid do not match the current uid/gid. However, dragging in the gnulib module will require adding an extra link library for some platforms (for example, Solaris needs -lgen), which means the change is more invasive as it will also affect Makefiles. src/libvirt_private.syms | 1 + src/util/util.c | 53 ++++++++++++++++++++++++++++++--------------- src/util/util.h | 7 +++-- 3 files changed, 40 insertions(+), 21 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 65911df..948bbe1 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -850,6 +850,7 @@ virFileDeletePid; virFileExists; virFileFindMountPoint; virFileHasSuffix; +virFileIsExecutable; virFileLinkPointsTo; virFileMakePath; virFileMatchesNameSuffix; diff --git a/src/util/util.c b/src/util/util.c index 60feb79..25e6185 100644 --- a/src/util/util.c +++ b/src/util/util.c @@ -1255,7 +1255,7 @@ int virFileResolveLink(const char *linkpath, } /* - * Finds a requested file in the PATH env. e.g.: + * Finds a requested executable file in the PATH env. e.g.: * "kvm-img" will return "/usr/bin/kvm-img" * * You must free the result @@ -1263,19 +1263,18 @@ int virFileResolveLink(const char *linkpath, char *virFindFileInPath(const char *file) { char *path; - char pathenv[PATH_MAX]; - char *penv = pathenv; + char *pathiter; char *pathseg; - char fullpath[PATH_MAX]; + char *fullpath = NULL; if (file == NULL) return NULL; /* if we are passed an absolute path (starting with /), return a - * copy of that path + * copy of that path, after validating that it is executable */ - if (file[0] == '/') { - if (virFileExists(file)) + if (IS_ABSOLUTE_FILE_NAME(file)) { + if (virFileIsExecutable(file)) return strdup(file); else return NULL; @@ -1284,27 +1283,45 @@ char *virFindFileInPath(const char *file) /* copy PATH env so we can tweak it */ path = getenv("PATH"); - if (path == NULL || virStrcpyStatic(pathenv, path) == NULL) + if (path == NULL || (path = strdup(path)) == NULL) return NULL; /* for each path segment, append the file to search for and test for * it. return it if found. */ - while ((pathseg = strsep(&penv, ":")) != NULL) { - snprintf(fullpath, PATH_MAX, "%s/%s", pathseg, file); - if (virFileExists(fullpath)) - return strdup(fullpath); + pathiter = path; + while ((pathseg = strsep(&pathiter, ":")) != NULL) { + if (virAsprintf(&fullpath, "%s/%s", pathseg, file) < 0 || + virFileIsExecutable(fullpath)) + break; + VIR_FREE(fullpath); } - return NULL; + VIR_FREE(path); + return fullpath; } -int virFileExists(const char *path) + +bool virFileExists(const char *path) { - struct stat st; + return access(path, F_OK) == 0; +} - if (stat(path, &st) >= 0) - return(1); - return(0); +/* Check that a file can be executed by the current user, and is not + * a directory. */ +bool +virFileIsExecutable(const char *file) +{ + struct stat sb; + + /* The existence of ACLs means that checking (sb.st_mode&0111) for + * executable bits may give false results; plus, access is + * lighter-weight than stat for a first pass filter. + * + * XXX: should this use gnulib's euidaccess module? + */ + return (access(file, X_OK) == 0 && + stat(file, &sb) == 0 && + !S_ISDIR(sb.st_mode)); } #ifndef WIN32 diff --git a/src/util/util.h b/src/util/util.h index 989962f..54c3058 100644 --- a/src/util/util.h +++ b/src/util/util.h @@ -1,8 +1,7 @@ - /* * utils.h: common, generic utility functions * - * Copyright (C) 2010 Red Hat, Inc. + * Copyright (C) 2010-2011 Red Hat, Inc. * Copyright (C) 2006, 2007 Binary Karma * Copyright (C) 2006 Shuveb Hussain * @@ -32,6 +31,7 @@ # include <sys/select.h> # include <sys/types.h> # include <stdarg.h> +# include <stdbool.h> # ifndef MIN # define MIN(a, b) ((a) < (b) ? (a) : (b)) @@ -120,7 +120,8 @@ int virFileResolveLink(const char *linkpath, char *virFindFileInPath(const char *file); -int virFileExists(const char *path); +bool virFileExists(const char *file); +bool virFileIsExecutable(const char *file); char *virFileSanitizePath(const char *path); -- 1.7.3.4 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list