# List all syntax-check exemptions:
exclude_file_name_regexp--sc_avoid_strcase = ^tools/virsh\.h$$
-_src1=libvirt|fdstream|qemu/qemu_monitor|util/(vircommand|virutil)|xen/xend_internal|rpc/virnetsocket|lxc/lxc_controller|locking/lock_daemon
+_src1=libvirt|fdstream|qemu/qemu_monitor|util/(vircommand|virfile)|xen/xend_internal|rpc/virnetsocket|lxc/lxc_controller|locking/lock_daemon
_test1=shunloadtest|virnettlscontexttest|vircgroupmock
exclude_file_name_regexp--sc_avoid_write = \
^(src/($(_src1))|daemon/libvirtd|tools/console|tests/($(_test1)))\.c$$
/*
* esx_driver.c: core driver functions for managing VMware ESX hosts
*
- * Copyright (C) 2010-2012 Red Hat, Inc.
+ * Copyright (C) 2010-2013 Red Hat, Inc.
* Copyright (C) 2009-2013 Matthias Bolte <matthias.bolte@googlemail.com>
* Copyright (C) 2009 Maximilian Wilhelm <max@rfc2324.org>
*
#include "snapshot_conf.h"
#include "virauth.h"
#include "viralloc.h"
+#include "virfile.h"
#include "virlog.h"
#include "viruuid.h"
#include "vmx.h"
#include "esx_vi.h"
#include "esx_vi_methods.h"
#include "esx_util.h"
-#include "viruri.h"
#include "virstring.h"
+#include "viruri.h"
#define VIR_FROM_THIS VIR_FROM_ESX
#include "internal.h"
#include "md5.h"
#include "viralloc.h"
+#include "virfile.h"
#include "virlog.h"
#include "viruuid.h"
#include "storage_conf.h"
# include "rpc/virnettlscontext.h"
#endif
#include "vircommand.h"
+#include "virfile.h"
#include "virrandom.h"
#include "viruri.h"
#include "virthread.h"
# util/virfile.h
+saferead;
+safewrite;
+safezero;
+virBuildPathInternal;
+virDirCreate;
+virFileAbsPath;
+virFileAccessibleAs;
+virFileBuildPath;
virFileClose;
virFileDeleteTree;
virFileDirectFdFlag;
+virFileExists;
virFileFclose;
virFileFdopen;
+virFileFindMountPoint;
+virFileHasSuffix;
+virFileIsAbsPath;
+virFileIsDir;
+virFileIsExecutable;
+virFileIsLink;
+virFileLinkPointsTo;
+virFileLock;
virFileLoopDeviceAssociate;
+virFileMakePath;
+virFileMakePathWithMode;
+virFileMatchesNameSuffix;
+virFileOpenAs;
+virFileOpenTty;
+virFileReadAll;
+virFileReadLimFD;
+virFileResolveAllLinks;
+virFileResolveLink;
virFileRewrite;
+virFileSanitizePath;
+virFileSkipRoot;
+virFileStripSuffix;
virFileTouch;
+virFileUnlock;
virFileUpdatePerm;
+virFileWaitForDevices;
virFileWrapperFdClose;
virFileWrapperFdFree;
virFileWrapperFdNew;
+virFileWriteStr;
+virFindFileInPath;
# util/virhash.h
# util/virutil.h
-saferead;
-safewrite;
-safezero;
-virBuildPathInternal;
virCompareLimitUlong;
-virDirCreate;
virDoubleToStr;
virEnumFromString;
virEnumToString;
-virFileAbsPath;
-virFileAccessibleAs;
-virFileBuildPath;
-virFileExists;
-virFileFindMountPoint;
-virFileHasSuffix;
-virFileIsAbsPath;
-virFileIsDir;
-virFileIsExecutable;
-virFileIsLink;
-virFileLinkPointsTo;
-virFileLock;
-virFileMakePath;
-virFileMakePathWithMode;
-virFileMatchesNameSuffix;
-virFileOpenAs;
-virFileOpenTty;
-virFileReadAll;
-virFileReadLimFD;
-virFileResolveAllLinks;
-virFileResolveLink;
-virFileSanitizePath;
-virFileSkipRoot;
-virFileStripSuffix;
-virFileUnlock;
-virFileWaitForDevices;
-virFileWriteStr;
virFindFCHostCapableVport;
-virFindFileInPath;
virFormatIntDecimal;
virGetDeviceID;
virGetDeviceUnprivSGIO;
/*
* node_device.c: node device enumeration
*
- * Copyright (C) 2010-2011 Red Hat, Inc.
+ * Copyright (C) 2010-2013 Red Hat, Inc.
* Copyright (C) 2008 Virtual Iron Software, Inc.
* Copyright (C) 2008 David F. Lively
*
#include "virerror.h"
#include "datatypes.h"
#include "viralloc.h"
+#include "virfile.h"
#include "virlog.h"
#include "virstring.h"
#include "node_device_conf.h"
#include "viralloc.h"
#include "viruuid.h"
#include "virbuffer.h"
+#include "virfile.h"
#include "virpci.h"
#include "virstring.h"
#include "virlog.h"
#include "vircommand.h"
#include "configmake.h"
+#include "virfile.h"
#include "virstoragefile.h"
#include "nodeinfo.h"
#include "c-ctype.h"
#include "dirname.h"
#include "viralloc.h"
#include "virerror.h"
+#include "virfile.h"
#include "md5.h"
#include "parallels_utils.h"
#include "virstring.h"
#include "configmake.h"
#include "virstoragefile.h"
#include "virerror.h"
+#include "virfile.h"
#include "parallels_utils.h"
#include "virstring.h"
/*
* virnetsshsession.c: ssh network transport provider based on libssh2
*
- * Copyright (C) 2012 Red Hat, Inc.
+ * Copyright (C) 2012-2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "virthread.h"
#include "virutil.h"
#include "virerror.h"
+#include "virfile.h"
#include "virobject.h"
#include "virstring.h"
#include "viralloc.h"
#include "virerror.h"
+#include "virfile.h"
#include "virutil.h"
#include "virlog.h"
#include "virthread.h"
#include "security_dac.h"
#include "virerror.h"
+#include "virfile.h"
#include "viralloc.h"
#include "virlog.h"
#include "virpci.h"
* in a reliable fashion if merely after a list of partitions & sizes,
* though it is fine for creating partitions.
*
- * Copyright (C) 2007-2008, 2010 Red Hat, Inc.
+ * Copyright (C) 2007-2008, 2010, 2013 Red Hat, Inc.
* Copyright (C) 2007-2008 Daniel P. Berrange
*
* This library is free software; you can redistribute it and/or
#include <locale.h>
#include "virutil.h"
+#include "virfile.h"
#include "c-ctype.h"
#include "configmake.h"
#include "virstring.h"
#include "storage_backend_disk.h"
#include "viralloc.h"
#include "vircommand.h"
+#include "virfile.h"
#include "configmake.h"
#include "virstring.h"
#include "vircommand.h"
#include "viralloc.h"
#include "virerror.h"
+#include "virfile.h"
#include "virlog.h"
#include "virthread.h"
#include "virstring.h"
/*
* virfile.c: safer file handling
*
- * Copyright (C) 2010-2012 Red Hat, Inc.
+ * Copyright (C) 2010-2013 Red Hat, Inc.
* Copyright (C) 2010 IBM Corporation
* Copyright (C) 2010 Stefan Berger
* Copyright (C) 2010 Eric Blake
#include <config.h>
#include "internal.h"
-#include "virfile.h"
-
+#include <passfd.h>
#include <fcntl.h>
+#include <pty.h>
#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
#include <unistd.h>
#include <dirent.h>
+#include <dirname.h>
+#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R
+# include <mntent.h>
+#endif
+#include <stdlib.h>
#if defined(__linux__) && HAVE_DECL_LO_FLAGS_AUTOCLEAR
# include <linux/loop.h>
# include <sys/ioctl.h>
#endif
-#include "vircommand.h"
#include "configmake.h"
#include "viralloc.h"
+#include "vircommand.h"
#include "virerror.h"
+#include "virfile.h"
#include "virlog.h"
+#include "virprocess.h"
#include "virstring.h"
+#include "virstoragefile.h"
+#include "virutil.h"
#define VIR_FROM_THIS VIR_FROM_NONE
closedir(dh);
return ret;
}
+
+int
+virFileStripSuffix(char *str, const char *suffix)
+{
+ int len = strlen(str);
+ int suffixlen = strlen(suffix);
+
+ if (len < suffixlen)
+ return 0;
+
+ if (!STREQ(str + len - suffixlen, suffix))
+ return 0;
+
+ str[len-suffixlen] = '\0';
+
+ return 1;
+}
+
+
+/* Like read(), but restarts after EINTR. Doesn't play
+ * nicely with nonblocking FD and EAGAIN, in which case
+ * you want to use bare read(). Or even use virSocket()
+ * if the FD is related to a socket rather than a plain
+ * file or pipe. */
+ssize_t
+saferead(int fd, void *buf, size_t count)
+{
+ size_t nread = 0;
+ while (count > 0) {
+ ssize_t r = read(fd, buf, count);
+ if (r < 0 && errno == EINTR)
+ continue;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return nread;
+ buf = (char *)buf + r;
+ count -= r;
+ nread += r;
+ }
+ return nread;
+}
+
+/* Like write(), but restarts after EINTR. Doesn't play
+ * nicely with nonblocking FD and EAGAIN, in which case
+ * you want to use bare write(). Or even use virSocket()
+ * if the FD is related to a socket rather than a plain
+ * file or pipe. */
+ssize_t
+safewrite(int fd, const void *buf, size_t count)
+{
+ size_t nwritten = 0;
+ while (count > 0) {
+ ssize_t r = write(fd, buf, count);
+
+ if (r < 0 && errno == EINTR)
+ continue;
+ if (r < 0)
+ return r;
+ if (r == 0)
+ return nwritten;
+ buf = (const char *)buf + r;
+ count -= r;
+ nwritten += r;
+ }
+ return nwritten;
+}
+
+#ifdef HAVE_POSIX_FALLOCATE
+int
+safezero(int fd, off_t offset, off_t len)
+{
+ int ret = posix_fallocate(fd, offset, len);
+ if (ret == 0)
+ return 0;
+ errno = ret;
+ return -1;
+}
+#else
+
+# ifdef HAVE_MMAP
+int
+safezero(int fd, off_t offset, off_t len)
+{
+ int r;
+ char *buf;
+
+ /* memset wants the mmap'ed file to be present on disk so create a
+ * sparse file
+ */
+ r = ftruncate(fd, offset + len);
+ if (r < 0)
+ return -1;
+
+ buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
+ if (buf == MAP_FAILED)
+ return -1;
+
+ memset(buf, 0, len);
+ munmap(buf, len);
+
+ return 0;
+}
+
+# else /* HAVE_MMAP */
+
+int
+safezero(int fd, off_t offset, off_t len)
+{
+ int r;
+ char *buf;
+ unsigned long long remain, bytes;
+
+ if (lseek(fd, offset, SEEK_SET) < 0)
+ return -1;
+
+ /* Split up the write in small chunks so as not to allocate lots of RAM */
+ remain = len;
+ bytes = 1024 * 1024;
+
+ r = VIR_ALLOC_N(buf, bytes);
+ if (r < 0) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ while (remain) {
+ if (bytes > remain)
+ bytes = remain;
+
+ r = safewrite(fd, buf, bytes);
+ if (r < 0) {
+ VIR_FREE(buf);
+ return -1;
+ }
+
+ /* safewrite() guarantees all data will be written */
+ remain -= bytes;
+ }
+ VIR_FREE(buf);
+ return 0;
+}
+# endif /* HAVE_MMAP */
+#endif /* HAVE_POSIX_FALLOCATE */
+
+
+#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R
+/* search /proc/mounts for mount point of *type; return pointer to
+ * malloc'ed string of the path if found, otherwise return NULL
+ * with errno set to an appropriate value.
+ */
+char *
+virFileFindMountPoint(const char *type)
+{
+ FILE *f;
+ struct mntent mb;
+ char mntbuf[1024];
+ char *ret = NULL;
+
+ f = setmntent("/proc/mounts", "r");
+ if (!f)
+ return NULL;
+
+ while (getmntent_r(f, &mb, mntbuf, sizeof(mntbuf))) {
+ if (STREQ(mb.mnt_type, type)) {
+ ret = strdup(mb.mnt_dir);
+ goto cleanup;
+ }
+ }
+
+ if (!ret)
+ errno = ENOENT;
+
+cleanup:
+ endmntent(f);
+
+ return ret;
+}
+
+#else /* defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R */
+
+char *
+virFileFindMountPoint(const char *type ATTRIBUTE_UNUSED)
+{
+ errno = ENOSYS;
+
+ return NULL;
+}
+
+#endif /* defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R */
+
+int
+virBuildPathInternal(char **path, ...)
+{
+ char *path_component = NULL;
+ virBuffer buf = VIR_BUFFER_INITIALIZER;
+ va_list ap;
+ int ret = 0;
+
+ va_start(ap, path);
+
+ path_component = va_arg(ap, char *);
+ virBufferAdd(&buf, path_component, -1);
+
+ while ((path_component = va_arg(ap, char *)) != NULL)
+ {
+ virBufferAddChar(&buf, '/');
+ virBufferAdd(&buf, path_component, -1);
+ }
+
+ va_end(ap);
+
+ *path = virBufferContentAndReset(&buf);
+ if (*path == NULL) {
+ ret = -1;
+ }
+
+ return ret;
+}
+
+/* Like gnulib's fread_file, but read no more than the specified maximum
+ number of bytes. If the length of the input is <= max_len, and
+ upon error while reading that data, it works just like fread_file. */
+static char *
+saferead_lim(int fd, size_t max_len, size_t *length)
+{
+ char *buf = NULL;
+ size_t alloc = 0;
+ size_t size = 0;
+ int save_errno;
+
+ for (;;) {
+ int count;
+ int requested;
+
+ if (size + BUFSIZ + 1 > alloc) {
+ alloc += alloc / 2;
+ if (alloc < size + BUFSIZ + 1)
+ alloc = size + BUFSIZ + 1;
+
+ if (VIR_REALLOC_N(buf, alloc) < 0) {
+ save_errno = errno;
+ break;
+ }
+ }
+
+ /* Ensure that (size + requested <= max_len); */
+ requested = MIN(size < max_len ? max_len - size : 0,
+ alloc - size - 1);
+ count = saferead(fd, buf + size, requested);
+ size += count;
+
+ if (count != requested || requested == 0) {
+ save_errno = errno;
+ if (count < 0)
+ break;
+ buf[size] = '\0';
+ *length = size;
+ return buf;
+ }
+ }
+
+ VIR_FREE(buf);
+ errno = save_errno;
+ return NULL;
+}
+
+/* A wrapper around saferead_lim that maps a failure due to
+ exceeding the maximum size limitation to EOVERFLOW. */
+int
+virFileReadLimFD(int fd, int maxlen, char **buf)
+{
+ size_t len;
+ char *s;
+
+ if (maxlen <= 0) {
+ errno = EINVAL;
+ return -1;
+ }
+ s = saferead_lim(fd, maxlen+1, &len);
+ if (s == NULL)
+ return -1;
+ if (len > maxlen || (int)len != len) {
+ VIR_FREE(s);
+ /* There was at least one byte more than MAXLEN.
+ Set errno accordingly. */
+ errno = EOVERFLOW;
+ return -1;
+ }
+ *buf = s;
+ return len;
+}
+
+int
+virFileReadAll(const char *path, int maxlen, char **buf)
+{
+ int fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ virReportSystemError(errno, _("Failed to open file '%s'"), path);
+ return -1;
+ }
+
+ int len = virFileReadLimFD(fd, maxlen, buf);
+ VIR_FORCE_CLOSE(fd);
+ if (len < 0) {
+ virReportSystemError(errno, _("Failed to read file '%s'"), path);
+ return -1;
+ }
+
+ return len;
+}
+
+/* Truncate @path and write @str to it. If @mode is 0, ensure that
+ @path exists; otherwise, use @mode if @path must be created.
+ Return 0 for success, nonzero for failure.
+ Be careful to preserve any errno value upon failure. */
+int
+virFileWriteStr(const char *path, const char *str, mode_t mode)
+{
+ int fd;
+
+ if (mode)
+ fd = open(path, O_WRONLY|O_TRUNC|O_CREAT, mode);
+ else
+ fd = open(path, O_WRONLY|O_TRUNC);
+ if (fd == -1)
+ return -1;
+
+ if (safewrite(fd, str, strlen(str)) < 0) {
+ VIR_FORCE_CLOSE(fd);
+ return -1;
+ }
+
+ /* Use errno from failed close only if there was no write error. */
+ if (VIR_CLOSE(fd) != 0)
+ return -1;
+
+ return 0;
+}
+
+int
+virFileMatchesNameSuffix(const char *file,
+ const char *name,
+ const char *suffix)
+{
+ int filelen = strlen(file);
+ int namelen = strlen(name);
+ int suffixlen = strlen(suffix);
+
+ if (filelen == (namelen + suffixlen) &&
+ STREQLEN(file, name, namelen) &&
+ STREQLEN(file + namelen, suffix, suffixlen))
+ return 1;
+ else
+ return 0;
+}
+
+int
+virFileHasSuffix(const char *str,
+ const char *suffix)
+{
+ int len = strlen(str);
+ int suffixlen = strlen(suffix);
+
+ if (len < suffixlen)
+ return 0;
+
+ return STRCASEEQ(str + len - suffixlen, suffix);
+}
+
+#define SAME_INODE(Stat_buf_1, Stat_buf_2) \
+ ((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \
+ && (Stat_buf_1).st_dev == (Stat_buf_2).st_dev)
+
+/* Return nonzero if checkLink and checkDest
+ refer to the same file. Otherwise, return 0. */
+int
+virFileLinkPointsTo(const char *checkLink,
+ const char *checkDest)
+{
+ struct stat src_sb;
+ struct stat dest_sb;
+
+ return (stat(checkLink, &src_sb) == 0
+ && stat(checkDest, &dest_sb) == 0
+ && SAME_INODE(src_sb, dest_sb));
+}
+
+
+static int
+virFileResolveLinkHelper(const char *linkpath,
+ bool intermediatePaths,
+ char **resultpath)
+{
+ struct stat st;
+
+ *resultpath = NULL;
+
+ /* We don't need the full canonicalization of intermediate
+ * directories, if linkpath is absolute and the basename is
+ * already a non-symlink. */
+ if (IS_ABSOLUTE_FILE_NAME(linkpath) && !intermediatePaths) {
+ if (lstat(linkpath, &st) < 0)
+ return -1;
+
+ if (!S_ISLNK(st.st_mode)) {
+ if (!(*resultpath = strdup(linkpath)))
+ return -1;
+ return 0;
+ }
+ }
+
+ *resultpath = canonicalize_file_name(linkpath);
+
+ return *resultpath == NULL ? -1 : 0;
+}
+
+/*
+ * Attempt to resolve a symbolic link, returning an
+ * absolute path where only the last component is guaranteed
+ * not to be a symlink.
+ *
+ * Return 0 if path was not a symbolic, or the link was
+ * resolved. Return -1 with errno set upon error
+ */
+int
+virFileResolveLink(const char *linkpath, char **resultpath)
+{
+ return virFileResolveLinkHelper(linkpath, false, resultpath);
+}
+
+/*
+ * Attempt to resolve a symbolic link, returning an
+ * absolute path where every component is guaranteed
+ * not to be a symlink.
+ *
+ * Return 0 if path was not a symbolic, or the link was
+ * resolved. Return -1 with errno set upon error
+ */
+int
+virFileResolveAllLinks(const char *linkpath, char **resultpath)
+{
+ return virFileResolveLinkHelper(linkpath, true, resultpath);
+}
+
+/*
+ * Check whether the given file is a link.
+ * Returns 1 in case of the file being a link, 0 in case it is not
+ * a link and the negative errno in all other cases.
+ */
+int
+virFileIsLink(const char *linkpath)
+{
+ struct stat st;
+
+ if (lstat(linkpath, &st) < 0)
+ return -errno;
+
+ return S_ISLNK(st.st_mode) != 0;
+}
+
+
+/*
+ * Finds a requested executable file in the PATH env. e.g.:
+ * "kvm-img" will return "/usr/bin/kvm-img"
+ *
+ * You must free the result
+ */
+char *
+virFindFileInPath(const char *file)
+{
+ char *path = NULL;
+ char *pathiter;
+ char *pathseg;
+ char *fullpath = NULL;
+
+ if (file == NULL)
+ return NULL;
+
+ /* if we are passed an absolute path (starting with /), return a
+ * copy of that path, after validating that it is executable
+ */
+ if (IS_ABSOLUTE_FILE_NAME(file)) {
+ if (virFileIsExecutable(file))
+ return strdup(file);
+ else
+ return NULL;
+ }
+
+ /* If we are passed an anchored path (containing a /), then there
+ * is no path search - it must exist in the current directory
+ */
+ if (strchr(file, '/')) {
+ if (virFileIsExecutable(file))
+ ignore_value(virFileAbsPath(file, &path));
+ return path;
+ }
+
+ /* copy PATH env so we can tweak it */
+ path = getenv("PATH");
+
+ 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.
+ */
+ pathiter = path;
+ while ((pathseg = strsep(&pathiter, ":")) != NULL) {
+ if (virAsprintf(&fullpath, "%s/%s", pathseg, file) < 0 ||
+ virFileIsExecutable(fullpath))
+ break;
+ VIR_FREE(fullpath);
+ }
+
+ VIR_FREE(path);
+ return fullpath;
+}
+
+bool
+virFileIsDir(const char *path)
+{
+ struct stat s;
+ return (stat(path, &s) == 0) && S_ISDIR(s.st_mode);
+}
+
+bool
+virFileExists(const char *path)
+{
+ return access(path, F_OK) == 0;
+}
+
+/* Check that a file is regular and has executable bits. If false is
+ * returned, errno is valid.
+ *
+ * Note: In the presence of ACLs, this may return true for a file that
+ * would actually fail with EACCES for a given user, or false for a
+ * file that the user could actually execute, but setups with ACLs
+ * that weird are unusual. */
+bool
+virFileIsExecutable(const char *file)
+{
+ struct stat sb;
+
+ /* We would also want to check faccessat if we cared about ACLs,
+ * but we don't. */
+ if (stat(file, &sb) < 0)
+ return false;
+ if (S_ISREG(sb.st_mode) && (sb.st_mode & 0111) != 0)
+ return true;
+ errno = S_ISDIR(sb.st_mode) ? EISDIR : EACCES;
+ return false;
+}
+
+#ifndef WIN32
+/* Check that a file is accessible under certain
+ * user & gid.
+ * @mode can be F_OK, or a bitwise combination of R_OK, W_OK, and X_OK.
+ * see 'man access' for more details.
+ * Returns 0 on success, -1 on fail with errno set.
+ */
+int
+virFileAccessibleAs(const char *path, int mode,
+ uid_t uid, gid_t gid)
+{
+ pid_t pid = 0;
+ int status, ret = 0;
+ int forkRet = 0;
+
+ if (uid == getuid() &&
+ gid == getgid())
+ return access(path, mode);
+
+ forkRet = virFork(&pid);
+
+ if (pid < 0) {
+ return -1;
+ }
+
+ if (pid) { /* parent */
+ if (virProcessWait(pid, &status) < 0) {
+ /* virProcessWait() already
+ * reported error */
+ return -1;
+ }
+
+ if (!WIFEXITED(status)) {
+ errno = EINTR;
+ return -1;
+ }
+
+ if (status) {
+ errno = WEXITSTATUS(status);
+ return -1;
+ }
+
+ return 0;
+ }
+
+ /* child.
+ * Return positive value here. Parent
+ * will change it to negative one. */
+
+ if (forkRet < 0) {
+ ret = errno;
+ goto childerror;
+ }
+
+ if (virSetUIDGID(uid, gid) < 0) {
+ ret = errno;
+ goto childerror;
+ }
+
+ if (access(path, mode) < 0)
+ ret = errno;
+
+childerror:
+ if ((ret & 0xFF) != ret) {
+ VIR_WARN("unable to pass desired return value %d", ret);
+ ret = 0xFF;
+ }
+
+ _exit(ret);
+}
+
+/* virFileOpenForceOwnerMode() - an internal utility function called
+ * only by virFileOpenAs(). Sets the owner and mode of the file
+ * opened as "fd" if it's not correct AND the flags say it should be
+ * forced. */
+static int
+virFileOpenForceOwnerMode(const char *path, int fd, mode_t mode,
+ uid_t uid, gid_t gid, unsigned int flags)
+{
+ int ret = 0;
+ struct stat st;
+
+ if (!(flags & (VIR_FILE_OPEN_FORCE_OWNER | VIR_FILE_OPEN_FORCE_MODE)))
+ return 0;
+
+ if (fstat(fd, &st) == -1) {
+ ret = -errno;
+ virReportSystemError(errno, _("stat of '%s' failed"), path);
+ return ret;
+ }
+ /* NB: uid:gid are never "-1" (default) at this point - the caller
+ * has always changed -1 to the value of get[gu]id().
+ */
+ if ((flags & VIR_FILE_OPEN_FORCE_OWNER) &&
+ ((st.st_uid != uid) || (st.st_gid != gid)) &&
+ (fchown(fd, uid, gid) < 0)) {
+ ret = -errno;
+ virReportSystemError(errno,
+ _("cannot chown '%s' to (%u, %u)"),
+ path, (unsigned int) uid,
+ (unsigned int) gid);
+ return ret;
+ }
+ if ((flags & VIR_FILE_OPEN_FORCE_MODE) &&
+ ((mode & (S_IRWXU|S_IRWXG|S_IRWXO)) !=
+ (st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))) &&
+ (fchmod(fd, mode) < 0)) {
+ ret = -errno;
+ virReportSystemError(errno,
+ _("cannot set mode of '%s' to %04o"),
+ path, mode);
+ return ret;
+ }
+ return ret;
+}
+
+/* virFileOpenForked() - an internal utility function called only by
+ * virFileOpenAs(). It forks, then the child does setuid+setgid to
+ * given uid:gid and attempts to open the file, while the parent just
+ * calls recvfd to get the open fd back from the child. returns the
+ * fd, or -errno if there is an error. */
+static int
+virFileOpenForked(const char *path, int openflags, mode_t mode,
+ uid_t uid, gid_t gid, unsigned int flags)
+{
+ pid_t pid;
+ int waitret, status, ret = 0;
+ int fd = -1;
+ int pair[2] = { -1, -1 };
+ int forkRet;
+
+ /* parent is running as root, but caller requested that the
+ * file be opened as some other user and/or group). The
+ * following dance avoids problems caused by root-squashing
+ * NFS servers. */
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) {
+ ret = -errno;
+ virReportSystemError(errno,
+ _("failed to create socket needed for '%s'"),
+ path);
+ return ret;
+ }
+
+ forkRet = virFork(&pid);
+ if (pid < 0)
+ return -errno;
+
+ if (pid == 0) {
+
+ /* child */
+
+ VIR_FORCE_CLOSE(pair[0]); /* preserves errno */
+ if (forkRet < 0) {
+ /* error encountered and logged in virFork() after the fork. */
+ ret = -errno;
+ goto childerror;
+ }
+
+ /* set desired uid/gid, then attempt to create the file */
+
+ if (virSetUIDGID(uid, gid) < 0) {
+ ret = -errno;
+ goto childerror;
+ }
+
+ if ((fd = open(path, openflags, mode)) < 0) {
+ ret = -errno;
+ virReportSystemError(errno,
+ _("child process failed to create file '%s'"),
+ path);
+ goto childerror;
+ }
+
+ /* File is successfully open. Set permissions if requested. */
+ ret = virFileOpenForceOwnerMode(path, fd, mode, uid, gid, flags);
+ if (ret < 0)
+ goto childerror;
+
+ do {
+ ret = sendfd(pair[1], fd);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ ret = -errno;
+ virReportSystemError(errno, "%s",
+ _("child process failed to send fd to parent"));
+ goto childerror;
+ }
+
+ childerror:
+ /* ret tracks -errno on failure, but exit value must be positive.
+ * If the child exits with EACCES, then the parent tries again. */
+ /* XXX This makes assumptions about errno being < 255, which is
+ * not true on Hurd. */
+ VIR_FORCE_CLOSE(pair[1]);
+ if (ret < 0) {
+ VIR_FORCE_CLOSE(fd);
+ }
+ ret = -ret;
+ if ((ret & 0xff) != ret) {
+ VIR_WARN("unable to pass desired return value %d", ret);
+ ret = 0xff;
+ }
+ _exit(ret);
+ }
+
+ /* parent */
+
+ VIR_FORCE_CLOSE(pair[1]);
+
+ do {
+ fd = recvfd(pair[0], 0);
+ } while (fd < 0 && errno == EINTR);
+ VIR_FORCE_CLOSE(pair[0]); /* NB: this preserves errno */
+
+ if (fd < 0 && errno != EACCES) {
+ ret = -errno;
+ while (waitpid(pid, NULL, 0) == -1 && errno == EINTR);
+ return ret;
+ }
+
+ /* wait for child to complete, and retrieve its exit code */
+ while ((waitret = waitpid(pid, &status, 0) == -1)
+ && (errno == EINTR));
+ if (waitret == -1) {
+ ret = -errno;
+ virReportSystemError(errno,
+ _("failed to wait for child creating '%s'"),
+ path);
+ VIR_FORCE_CLOSE(fd);
+ return ret;
+ }
+ if (!WIFEXITED(status) || (ret = -WEXITSTATUS(status)) == -EACCES ||
+ fd == -1) {
+ /* fall back to the simpler method, which works better in
+ * some cases */
+ VIR_FORCE_CLOSE(fd);
+ if (flags & VIR_FILE_OPEN_NOFORK) {
+ /* If we had already tried opening w/o fork+setuid and
+ * failed, no sense trying again. Just set return the
+ * original errno that we got at that time (by
+ * definition, always either EACCES or EPERM - EACCES
+ * is close enough).
+ */
+ return -EACCES;
+ }
+ if ((fd = open(path, openflags, mode)) < 0)
+ return -errno;
+ ret = virFileOpenForceOwnerMode(path, fd, mode, uid, gid, flags);
+ if (ret < 0) {
+ VIR_FORCE_CLOSE(fd);
+ return ret;
+ }
+ }
+ return fd;
+}
+
+/**
+ * virFileOpenAs:
+ * @path: file to open or create
+ * @openflags: flags to pass to open
+ * @mode: mode to use on creation or when forcing permissions
+ * @uid: uid that should own file on creation
+ * @gid: gid that should own file
+ * @flags: bit-wise or of VIR_FILE_OPEN_* flags
+ *
+ * Open @path, and return an fd to the open file. @openflags contains
+ * the flags normally passed to open(2), while those in @flags are
+ * used internally. If @flags includes VIR_FILE_OPEN_NOFORK, then try
+ * opening the file while executing with the current uid:gid
+ * (i.e. don't fork+setuid+setgid before the call to open()). If
+ * @flags includes VIR_FILE_OPEN_FORK, then try opening the file while
+ * the effective user id is @uid (by forking a child process); this
+ * allows one to bypass root-squashing NFS issues; NOFORK is always
+ * tried before FORK (the absence of both flags is treated identically
+ * to (VIR_FILE_OPEN_NOFORK | VIR_FILE_OPEN_FORK)). If @flags includes
+ * VIR_FILE_OPEN_FORCE_OWNER, then ensure that @path is owned by
+ * uid:gid before returning (even if it already existed with a
+ * different owner). If @flags includes VIR_FILE_OPEN_FORCE_MODE,
+ * ensure it has those permissions before returning (again, even if
+ * the file already existed with different permissions).
+ *
+ * The return value (if non-negative) is the file descriptor, left
+ * open. Returns -errno on failure.
+ */
+int
+virFileOpenAs(const char *path, int openflags, mode_t mode,
+ uid_t uid, gid_t gid, unsigned int flags)
+{
+ int ret = 0, fd = -1;
+
+ /* allow using -1 to mean "current value" */
+ if (uid == (uid_t) -1)
+ uid = getuid();
+ if (gid == (gid_t) -1)
+ gid = getgid();
+
+ /* treat absence of both flags as presence of both for simpler
+ * calling. */
+ if (!(flags & (VIR_FILE_OPEN_NOFORK|VIR_FILE_OPEN_FORK)))
+ flags |= VIR_FILE_OPEN_NOFORK|VIR_FILE_OPEN_FORK;
+
+ if ((flags & VIR_FILE_OPEN_NOFORK)
+ || (getuid() != 0)
+ || ((uid == 0) && (gid == 0))) {
+
+ if ((fd = open(path, openflags, mode)) < 0) {
+ ret = -errno;
+ if (!(flags & VIR_FILE_OPEN_FORK))
+ goto error;
+ } else {
+ ret = virFileOpenForceOwnerMode(path, fd, mode, uid, gid, flags);
+ if (ret < 0)
+ goto error;
+ }
+ }
+
+ /* If we either 1) didn't try opening as current user at all, or
+ * 2) failed, and errno/virStorageFileIsSharedFS indicate we might
+ * be successful if we try as a different uid, then try doing
+ * fork+setuid+setgid before opening.
+ */
+ if ((fd < 0) && (flags & VIR_FILE_OPEN_FORK)) {
+
+ if (ret < 0) {
+ /* An open(2) that failed due to insufficient permissions
+ * could return one or the other of these depending on OS
+ * version and circumstances. Any other errno indicates a
+ * problem that couldn't be remedied by fork+setuid
+ * anyway. */
+ if (ret != -EACCES && ret != -EPERM)
+ goto error;
+
+ /* On Linux we can also verify the FS-type of the
+ * directory. (this is a NOP on other platforms). */
+ if (virStorageFileIsSharedFS(path) <= 0)
+ goto error;
+ }
+
+ /* passed all prerequisites - retry the open w/fork+setuid */
+ if ((fd = virFileOpenForked(path, openflags, mode, uid, gid, flags)) < 0) {
+ ret = fd;
+ goto error;
+ }
+ }
+
+ /* File is successfully opened */
+ return fd;
+
+error:
+ if (fd >= 0) {
+ /* some other failure after the open succeeded */
+ VIR_FORCE_CLOSE(fd);
+ }
+ /* whoever failed the open last has already set ret = -errno */
+ return ret;
+}
+
+/* return -errno on failure, or 0 on success */
+static int
+virDirCreateNoFork(const char *path,
+ mode_t mode, uid_t uid, gid_t gid,
+ unsigned int flags)
+{
+ int ret = 0;
+ struct stat st;
+
+ if ((mkdir(path, mode) < 0)
+ && !((errno == EEXIST) && (flags & VIR_DIR_CREATE_ALLOW_EXIST))) {
+ ret = -errno;
+ virReportSystemError(errno, _("failed to create directory '%s'"),
+ path);
+ goto error;
+ }
+
+ if (stat(path, &st) == -1) {
+ ret = -errno;
+ virReportSystemError(errno, _("stat of '%s' failed"), path);
+ goto error;
+ }
+ if (((st.st_uid != uid) || (st.st_gid != gid))
+ && (chown(path, uid, gid) < 0)) {
+ ret = -errno;
+ virReportSystemError(errno, _("cannot chown '%s' to (%u, %u)"),
+ path, (unsigned int) uid, (unsigned int) gid);
+ goto error;
+ }
+ if ((flags & VIR_DIR_CREATE_FORCE_PERMS)
+ && (chmod(path, mode) < 0)) {
+ ret = -errno;
+ virReportSystemError(errno,
+ _("cannot set mode of '%s' to %04o"),
+ path, mode);
+ goto error;
+ }
+error:
+ return ret;
+}
+
+/* return -errno on failure, or 0 on success */
+int
+virDirCreate(const char *path,
+ mode_t mode, uid_t uid, gid_t gid,
+ unsigned int flags)
+{
+ struct stat st;
+ pid_t pid;
+ int waitret;
+ int status, ret = 0;
+
+ /* allow using -1 to mean "current value" */
+ if (uid == (uid_t) -1)
+ uid = getuid();
+ if (gid == (gid_t) -1)
+ gid = getgid();
+
+ if ((!(flags & VIR_DIR_CREATE_AS_UID))
+ || (getuid() != 0)
+ || ((uid == 0) && (gid == 0))
+ || ((flags & VIR_DIR_CREATE_ALLOW_EXIST) && (stat(path, &st) >= 0))) {
+ return virDirCreateNoFork(path, mode, uid, gid, flags);
+ }
+
+ int forkRet = virFork(&pid);
+
+ if (pid < 0) {
+ ret = -errno;
+ return ret;
+ }
+
+ if (pid) { /* parent */
+ /* wait for child to complete, and retrieve its exit code */
+ while ((waitret = waitpid(pid, &status, 0) == -1) && (errno == EINTR));
+ if (waitret == -1) {
+ ret = -errno;
+ virReportSystemError(errno,
+ _("failed to wait for child creating '%s'"),
+ path);
+ goto parenterror;
+ }
+ if (!WIFEXITED(status) || (ret = -WEXITSTATUS(status)) == -EACCES) {
+ /* fall back to the simpler method, which works better in
+ * some cases */
+ return virDirCreateNoFork(path, mode, uid, gid, flags);
+ }
+parenterror:
+ return ret;
+ }
+
+ /* child */
+
+ if (forkRet < 0) {
+ /* error encountered and logged in virFork() after the fork. */
+ goto childerror;
+ }
+
+ /* set desired uid/gid, then attempt to create the directory */
+
+ if (virSetUIDGID(uid, gid) < 0) {
+ ret = -errno;
+ goto childerror;
+ }
+ if (mkdir(path, mode) < 0) {
+ ret = -errno;
+ if (ret != -EACCES) {
+ /* in case of EACCES, the parent will retry */
+ virReportSystemError(errno, _("child failed to create directory '%s'"),
+ path);
+ }
+ goto childerror;
+ }
+ /* check if group was set properly by creating after
+ * setgid. If not, try doing it with chown */
+ if (stat(path, &st) == -1) {
+ ret = -errno;
+ virReportSystemError(errno,
+ _("stat of '%s' failed"), path);
+ goto childerror;
+ }
+ if ((st.st_gid != gid) && (chown(path, (uid_t) -1, gid) < 0)) {
+ ret = -errno;
+ virReportSystemError(errno,
+ _("cannot chown '%s' to group %u"),
+ path, (unsigned int) gid);
+ goto childerror;
+ }
+ if ((flags & VIR_DIR_CREATE_FORCE_PERMS)
+ && chmod(path, mode) < 0) {
+ virReportSystemError(errno,
+ _("cannot set mode of '%s' to %04o"),
+ path, mode);
+ goto childerror;
+ }
+childerror:
+ _exit(ret);
+}
+
+#else /* WIN32 */
+
+int
+virFileAccessibleAs(const char *path,
+ int mode,
+ uid_t uid ATTRIBUTE_UNUSED,
+ gid_t gid ATTRIBUTE_UNUSED)
+{
+
+ VIR_WARN("Ignoring uid/gid due to WIN32");
+
+ return access(path, mode);
+}
+
+/* return -errno on failure, or 0 on success */
+int
+virFileOpenAs(const char *path ATTRIBUTE_UNUSED,
+ int openflags ATTRIBUTE_UNUSED,
+ mode_t mode ATTRIBUTE_UNUSED,
+ uid_t uid ATTRIBUTE_UNUSED,
+ gid_t gid ATTRIBUTE_UNUSED,
+ unsigned int flags_unused ATTRIBUTE_UNUSED)
+{
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("virFileOpenAs is not implemented for WIN32"));
+
+ return -ENOSYS;
+}
+
+int
+virDirCreate(const char *path ATTRIBUTE_UNUSED,
+ mode_t mode ATTRIBUTE_UNUSED,
+ uid_t uid ATTRIBUTE_UNUSED,
+ gid_t gid ATTRIBUTE_UNUSED,
+ unsigned int flags_unused ATTRIBUTE_UNUSED)
+{
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("virDirCreate is not implemented for WIN32"));
+
+ return -ENOSYS;
+}
+#endif /* WIN32 */
+
+static int
+virFileMakePathHelper(char *path, mode_t mode)
+{
+ struct stat st;
+ char *p;
+
+ VIR_DEBUG("path=%s mode=0%o", path, mode);
+
+ if (stat(path, &st) >= 0) {
+ if (S_ISDIR(st.st_mode))
+ return 0;
+
+ errno = ENOTDIR;
+ return -1;
+ }
+
+ if (errno != ENOENT)
+ return -1;
+
+ if ((p = strrchr(path, '/')) == NULL) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (p != path) {
+ *p = '\0';
+
+ if (virFileMakePathHelper(path, mode) < 0)
+ return -1;
+
+ *p = '/';
+ }
+
+ if (mkdir(path, mode) < 0 && errno != EEXIST)
+ return -1;
+
+ return 0;
+}
+
+/**
+ * Creates the given directory with mode 0777 if it's not already existing.
+ *
+ * Returns 0 on success, or -1 if an error occurred (in which case, errno
+ * is set appropriately).
+ */
+int
+virFileMakePath(const char *path)
+{
+ return virFileMakePathWithMode(path, 0777);
+}
+
+int
+virFileMakePathWithMode(const char *path,
+ mode_t mode)
+{
+ int ret = -1;
+ char *tmp;
+
+ if ((tmp = strdup(path)) == NULL)
+ goto cleanup;
+
+ ret = virFileMakePathHelper(tmp, mode);
+
+cleanup:
+ VIR_FREE(tmp);
+ return ret;
+}
+
+/* Build up a fully qualified path for a config file to be
+ * associated with a persistent guest or network */
+char *
+virFileBuildPath(const char *dir, const char *name, const char *ext)
+{
+ char *path;
+
+ if (ext == NULL) {
+ if (virAsprintf(&path, "%s/%s", dir, name) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+ } else {
+ if (virAsprintf(&path, "%s/%s%s", dir, name, ext) < 0) {
+ virReportOOMError();
+ return NULL;
+ }
+ }
+
+ return path;
+}
+
+/* Open a non-blocking master side of a pty. If ttyName is not NULL,
+ * then populate it with the name of the slave. If rawmode is set,
+ * also put the master side into raw mode before returning. */
+#ifndef WIN32
+int
+virFileOpenTty(int *ttymaster, char **ttyName, int rawmode)
+{
+ /* XXX A word of caution - on some platforms (Solaris and HP-UX),
+ * additional ioctl() calls are needs after opening the slave
+ * before it will cause isatty() to return true. Should we make
+ * virFileOpenTty also return the opened slave fd, so the caller
+ * doesn't have to worry about that mess? */
+ int ret = -1;
+ int slave = -1;
+ char *name = NULL;
+
+ /* Unfortunately, we can't use the name argument of openpty, since
+ * there is no guarantee on how large the buffer has to be.
+ * Likewise, we can't use the termios argument: we have to use
+ * read-modify-write since there is no portable way to initialize
+ * a struct termios without use of tcgetattr. */
+ if (openpty(ttymaster, &slave, NULL, NULL, NULL) < 0)
+ return -1;
+
+ /* What a shame that openpty cannot atomically set FD_CLOEXEC, but
+ * that using posix_openpt/grantpt/unlockpt/ptsname is not
+ * thread-safe, and that ptsname_r is not portable. */
+ if (virSetNonBlock(*ttymaster) < 0 ||
+ virSetCloseExec(*ttymaster) < 0)
+ goto cleanup;
+
+ /* While Linux supports tcgetattr on either the master or the
+ * slave, Solaris requires it to be on the slave. */
+ if (rawmode) {
+ struct termios ttyAttr;
+ if (tcgetattr(slave, &ttyAttr) < 0)
+ goto cleanup;
+
+ cfmakeraw(&ttyAttr);
+
+ if (tcsetattr(slave, TCSADRAIN, &ttyAttr) < 0)
+ goto cleanup;
+ }
+
+ /* ttyname_r on the slave is required by POSIX, while ptsname_r on
+ * the master is a glibc extension, and the POSIX ptsname is not
+ * thread-safe. Since openpty gave us both descriptors, guess
+ * which way we will determine the name? :) */
+ if (ttyName) {
+ /* Initial guess of 64 is generally sufficient; rely on ERANGE
+ * to tell us if we need to grow. */
+ size_t len = 64;
+ int rc;
+
+ if (VIR_ALLOC_N(name, len) < 0)
+ goto cleanup;
+
+ while ((rc = ttyname_r(slave, name, len)) == ERANGE) {
+ if (VIR_RESIZE_N(name, len, len, len) < 0)
+ goto cleanup;
+ }
+ if (rc != 0) {
+ errno = rc;
+ goto cleanup;
+ }
+ *ttyName = name;
+ name = NULL;
+ }
+
+ ret = 0;
+
+cleanup:
+ if (ret != 0)
+ VIR_FORCE_CLOSE(*ttymaster);
+ VIR_FORCE_CLOSE(slave);
+ VIR_FREE(name);
+
+ return ret;
+}
+#else /* WIN32 */
+int
+virFileOpenTty(int *ttymaster ATTRIBUTE_UNUSED,
+ char **ttyName ATTRIBUTE_UNUSED,
+ int rawmode ATTRIBUTE_UNUSED)
+{
+ /* mingw completely lacks pseudo-terminals, and the gnulib
+ * replacements are not (yet) license compatible. */
+ errno = ENOSYS;
+ return -1;
+}
+#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.
+ * Return -1 on error.
+ *
+ * You must free the result.
+ */
+int
+virFileAbsPath(const char *path, char **abspath)
+{
+ char *buf;
+
+ if (path[0] == '/') {
+ if (!(*abspath = strdup(path)))
+ return -1;
+ } else {
+ buf = getcwd(NULL, 0);
+ if (buf == NULL)
+ return -1;
+
+ if (virAsprintf(abspath, "%s/%s", buf, path) < 0) {
+ VIR_FREE(buf);
+ return -1;
+ }
+ VIR_FREE(buf);
+ }
+
+ return 0;
+}
+
+/* Remove spurious / characters from a path. The result must be freed */
+char *
+virFileSanitizePath(const char *path)
+{
+ const char *cur = path;
+ char *cleanpath;
+ int idx = 0;
+
+ cleanpath = strdup(path);
+ if (!cleanpath) {
+ virReportOOMError();
+ return NULL;
+ }
+
+ /* Need to sanitize:
+ * // -> //
+ * /// -> /
+ * /../foo -> /../foo
+ * /foo///bar/ -> /foo/bar
+ */
+
+ /* Starting with // is valid posix, but ///foo == /foo */
+ if (cur[0] == '/' && cur[1] == '/' && cur[2] != '/') {
+ idx = 2;
+ cur += 2;
+ }
+
+ /* Sanitize path in place */
+ while (*cur != '\0') {
+ if (*cur != '/') {
+ cleanpath[idx++] = *cur++;
+ continue;
+ }
+
+ /* Skip all extra / */
+ while (*++cur == '/')
+ continue;
+
+ /* Don't add a trailing / */
+ if (idx != 0 && *cur == '\0')
+ break;
+
+ cleanpath[idx++] = '/';
+ }
+ cleanpath[idx] = '\0';
+
+ return cleanpath;
+}
/*
* virfile.h: safer file handling
*
- * Copyright (C) 2010-2011 Red Hat, Inc.
+ * Copyright (C) 2010-2011, 2013 Red Hat, Inc.
* Copyright (C) 2010 IBM Corporation
* Copyright (C) 2010 Stefan Berger
* Copyright (C) 2010 Eric Blake
*/
-#ifndef __VIR_FILES_H_
-# define __VIR_FILES_H_
+#ifndef __VIR_FILE_H_
+# define __VIR_FILE_H_
# include <stdio.h>
VIR_FILE_CLOSE_DONT_LOG = 1 << 2,
} virFileCloseFlags;
+ssize_t saferead(int fd, void *buf, size_t count) ATTRIBUTE_RETURN_CHECK;
+ssize_t safewrite(int fd, const void *buf, size_t count)
+ ATTRIBUTE_RETURN_CHECK;
+int safezero(int fd, off_t offset, off_t len)
+ ATTRIBUTE_RETURN_CHECK;
+
/* Don't call these directly - use the macros below */
int virFileClose(int *fdptr, virFileCloseFlags flags)
ATTRIBUTE_RETURN_CHECK;
int virFileDeleteTree(const char *dir);
-#endif /* __VIR_FILES_H */
+int virFileReadLimFD(int fd, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK;
+
+int virFileReadAll(const char *path, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK;
+
+int virFileWriteStr(const char *path, const char *str, mode_t mode)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
+
+int virFileMatchesNameSuffix(const char *file,
+ const char *name,
+ const char *suffix);
+
+int virFileHasSuffix(const char *str,
+ const char *suffix);
+
+int virFileStripSuffix(char *str,
+ const char *suffix) ATTRIBUTE_RETURN_CHECK;
+
+int virFileLinkPointsTo(const char *checkLink,
+ const char *checkDest);
+
+int virFileResolveLink(const char *linkpath,
+ char **resultpath) ATTRIBUTE_RETURN_CHECK;
+int virFileResolveAllLinks(const char *linkpath,
+ char **resultpath) ATTRIBUTE_RETURN_CHECK;
+
+int virFileIsLink(const char *linkpath)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
+
+char *virFindFileInPath(const char *file);
+
+bool virFileIsDir (const char *file) ATTRIBUTE_NONNULL(1);
+bool virFileExists(const char *file) ATTRIBUTE_NONNULL(1);
+bool virFileIsExecutable(const char *file) ATTRIBUTE_NONNULL(1);
+
+char *virFileSanitizePath(const char *path);
+
+enum {
+ VIR_FILE_OPEN_NONE = 0,
+ VIR_FILE_OPEN_NOFORK = (1 << 0),
+ VIR_FILE_OPEN_FORK = (1 << 1),
+ VIR_FILE_OPEN_FORCE_MODE = (1 << 2),
+ VIR_FILE_OPEN_FORCE_OWNER = (1 << 3),
+};
+int virFileAccessibleAs(const char *path, int mode,
+ uid_t uid, gid_t gid)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
+int virFileOpenAs(const char *path, int openflags, mode_t mode,
+ uid_t uid, gid_t gid,
+ unsigned int flags)
+ ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
+
+enum {
+ VIR_DIR_CREATE_NONE = 0,
+ VIR_DIR_CREATE_AS_UID = (1 << 0),
+ VIR_DIR_CREATE_FORCE_PERMS = (1 << 1),
+ VIR_DIR_CREATE_ALLOW_EXIST = (1 << 2),
+};
+int virDirCreate(const char *path, mode_t mode, uid_t uid, gid_t gid,
+ unsigned int flags) ATTRIBUTE_RETURN_CHECK;
+int virFileMakePath(const char *path) ATTRIBUTE_RETURN_CHECK;
+int virFileMakePathWithMode(const char *path,
+ mode_t mode) ATTRIBUTE_RETURN_CHECK;
+
+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,
+ int rawmode);
+
+char *virFileFindMountPoint(const char *type);
+
+void virFileWaitForDevices(void);
+
+/* NB: this should be combined with virFileBuildPath */
+# define virBuildPath(path, ...) \
+ virBuildPathInternal(path, __VA_ARGS__, NULL)
+int virBuildPathInternal(char **path, ...) ATTRIBUTE_SENTINEL;
+
+#endif /* __VIR_FILE_H */
/*
* virhook.c: implementation of the synchronous hooks support
*
- * Copyright (C) 2010-2012 Red Hat, Inc.
+ * Copyright (C) 2010-2013 Red Hat, Inc.
* Copyright (C) 2010 Daniel Veillard
*
* This library is free software; you can redistribute it and/or
#include "vircommand.h"
#include "viralloc.h"
#include "virerror.h"
+#include "virfile.h"
#include "virlog.h"
#include "virthread.h"
#include "virstring.h"
/*
* virkeyfile.c: "ini"-style configuration file handling
*
- * Copyright (C) 2012 Red Hat, Inc.
+ * Copyright (C) 2012-2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "c-ctype.h"
#include "virlog.h"
#include "viralloc.h"
+#include "virfile.h"
#include "virutil.h"
#include "virhash.h"
#include "virkeyfile.h"
/*
- * Copyright (C) 2010-2011 Red Hat, Inc.
+ * Copyright (C) 2010-2013 Red Hat, Inc.
* Copyright IBM Corp. 2008
*
* This library is free software; you can redistribute it and/or
#include "virlog.h"
#include "vircommand.h"
#include "virerror.h"
+#include "virfile.h"
#include "virstring.h"
#include "virutil.h"
/*
* virsysinfo.c: get SMBIOS/sysinfo information from the host
*
- * Copyright (C) 2010-2012 Red Hat, Inc.
+ * Copyright (C) 2010-2013 Red Hat, Inc.
* Copyright (C) 2010 Daniel Veillard
*
* This library is free software; you can redistribute it and/or
#include "virlog.h"
#include "viralloc.h"
#include "vircommand.h"
+#include "virfile.h"
#include "virstring.h"
#define VIR_FROM_THIS VIR_FROM_SYSINFO
/*
* virusb.c: helper APIs for managing host USB devices
*
- * Copyright (C) 2009-2012 Red Hat, Inc.
+ * Copyright (C) 2009-2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "viralloc.h"
#include "virutil.h"
#include "virerror.h"
+#include "virfile.h"
#include "virstring.h"
#define USB_SYSFS "/sys/bus/usb"
#include <dirent.h>
#include <stdio.h>
#include <stdarg.h>
-#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>
-#include <sys/wait.h>
#if HAVE_MMAP
# include <sys/mman.h>
#endif
#include <string.h>
-#include <signal.h>
#include <termios.h>
-#include <pty.h>
#include <locale.h>
#if HAVE_LIBDEVMAPPER_H
# include <cap-ng.h>
# include <sys/prctl.h>
#endif
-#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R
-# include <mntent.h>
-#endif
#ifdef WIN32
# ifdef HAVE_WINSOCK2_H
#endif
#include "c-ctype.h"
-#include "dirname.h"
#include "virerror.h"
#include "virlog.h"
#include "virbuffer.h"
-#include "virstoragefile.h"
#include "viralloc.h"
#include "virthread.h"
#include "verify.h"
#include "virfile.h"
#include "vircommand.h"
#include "nonblocking.h"
-#include "passfd.h"
-#include "virprocess.h"
-#include "virstring.h"
-
-#ifndef NSIG
-# define NSIG 32
-#endif
-
-verify(sizeof(gid_t) <= sizeof(unsigned int) &&
- sizeof(uid_t) <= sizeof(unsigned int));
-
-#define VIR_FROM_THIS VIR_FROM_NONE
-
-/* Like read(), but restarts after EINTR. Doesn't play
- * nicely with nonblocking FD and EAGAIN, in which case
- * you want to use bare read(). Or even use virSocket()
- * if the FD is related to a socket rather than a plain
- * file or pipe. */
-ssize_t
-saferead(int fd, void *buf, size_t count)
-{
- size_t nread = 0;
- while (count > 0) {
- ssize_t r = read(fd, buf, count);
- if (r < 0 && errno == EINTR)
- continue;
- if (r < 0)
- return r;
- if (r == 0)
- return nread;
- buf = (char *)buf + r;
- count -= r;
- nread += r;
- }
- return nread;
-}
-
-/* Like write(), but restarts after EINTR. Doesn't play
- * nicely with nonblocking FD and EAGAIN, in which case
- * you want to use bare write(). Or even use virSocket()
- * if the FD is related to a socket rather than a plain
- * file or pipe. */
-ssize_t
-safewrite(int fd, const void *buf, size_t count)
-{
- size_t nwritten = 0;
- while (count > 0) {
- ssize_t r = write(fd, buf, count);
-
- if (r < 0 && errno == EINTR)
- continue;
- if (r < 0)
- return r;
- if (r == 0)
- return nwritten;
- buf = (const char *)buf + r;
- count -= r;
- nwritten += r;
- }
- return nwritten;
-}
-
-#ifdef HAVE_POSIX_FALLOCATE
-int safezero(int fd, off_t offset, off_t len)
-{
- int ret = posix_fallocate(fd, offset, len);
- if (ret == 0)
- return 0;
- errno = ret;
- return -1;
-}
-#else
-
-# ifdef HAVE_MMAP
-int safezero(int fd, off_t offset, off_t len)
-{
- int r;
- char *buf;
-
- /* memset wants the mmap'ed file to be present on disk so create a
- * sparse file
- */
- r = ftruncate(fd, offset + len);
- if (r < 0)
- return -1;
-
- buf = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset);
- if (buf == MAP_FAILED)
- return -1;
-
- memset(buf, 0, len);
- munmap(buf, len);
-
- return 0;
-}
-
-# else /* HAVE_MMAP */
-
-int safezero(int fd, off_t offset, off_t len)
-{
- int r;
- char *buf;
- unsigned long long remain, bytes;
-
- if (lseek(fd, offset, SEEK_SET) < 0)
- return -1;
-
- /* Split up the write in small chunks so as not to allocate lots of RAM */
- remain = len;
- bytes = 1024 * 1024;
-
- r = VIR_ALLOC_N(buf, bytes);
- if (r < 0) {
- errno = ENOMEM;
- return -1;
- }
-
- while (remain) {
- if (bytes > remain)
- bytes = remain;
-
- r = safewrite(fd, buf, bytes);
- if (r < 0) {
- VIR_FREE(buf);
- return -1;
- }
-
- /* safewrite() guarantees all data will be written */
- remain -= bytes;
- }
- VIR_FREE(buf);
- return 0;
-}
-# endif /* HAVE_MMAP */
-#endif /* HAVE_POSIX_FALLOCATE */
-
-int virFileStripSuffix(char *str,
- const char *suffix)
-{
- int len = strlen(str);
- int suffixlen = strlen(suffix);
-
- if (len < suffixlen)
- return 0;
-
- if (!STREQ(str + len - suffixlen, suffix))
- return 0;
-
- str[len-suffixlen] = '\0';
-
- return 1;
-}
-
-#ifndef WIN32
-
-int virSetInherit(int fd, bool inherit) {
- int fflags;
- if ((fflags = fcntl(fd, F_GETFD)) < 0)
- return -1;
- if (inherit)
- fflags &= ~FD_CLOEXEC;
- else
- fflags |= FD_CLOEXEC;
- if ((fcntl(fd, F_SETFD, fflags)) < 0)
- return -1;
- return 0;
-}
-
-#else /* WIN32 */
-
-int virSetInherit(int fd ATTRIBUTE_UNUSED, bool inherit ATTRIBUTE_UNUSED)
-{
- /* FIXME: Currently creating child processes is not supported on
- * Win32, so there is no point in failing calls that are only relevant
- * when creating child processes. So just pretend that we changed the
- * inheritance property of the given fd as requested. */
- return 0;
-}
-
-#endif /* WIN32 */
-
-int virSetBlocking(int fd, bool blocking) {
- return set_nonblocking_flag(fd, !blocking);
-}
-
-int virSetNonBlock(int fd) {
- return virSetBlocking(fd, false);
-}
-
-int virSetCloseExec(int fd)
-{
- return virSetInherit(fd, false);
-}
-
-int
-virPipeReadUntilEOF(int outfd, int errfd,
- char **outbuf, char **errbuf) {
-
- struct pollfd fds[2];
- int i;
- int finished[2];
-
- fds[0].fd = outfd;
- fds[0].events = POLLIN;
- fds[0].revents = 0;
- finished[0] = 0;
- fds[1].fd = errfd;
- fds[1].events = POLLIN;
- fds[1].revents = 0;
- finished[1] = 0;
-
- while (!(finished[0] && finished[1])) {
-
- if (poll(fds, ARRAY_CARDINALITY(fds), -1) < 0) {
- if ((errno == EAGAIN) || (errno == EINTR))
- continue;
- goto pollerr;
- }
-
- for (i = 0; i < ARRAY_CARDINALITY(fds); ++i) {
- char data[1024], **buf;
- int got, size;
-
- if (!(fds[i].revents))
- continue;
- else if (fds[i].revents & POLLHUP)
- finished[i] = 1;
-
- if (!(fds[i].revents & POLLIN)) {
- if (fds[i].revents & POLLHUP)
- continue;
-
- virReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("Unknown poll response."));
- goto error;
- }
-
- got = read(fds[i].fd, data, sizeof(data));
-
- if (got == sizeof(data))
- finished[i] = 0;
-
- if (got == 0) {
- finished[i] = 1;
- continue;
- }
- if (got < 0) {
- if (errno == EINTR)
- continue;
- if (errno == EAGAIN)
- break;
- goto pollerr;
- }
-
- buf = ((fds[i].fd == outfd) ? outbuf : errbuf);
- size = (*buf ? strlen(*buf) : 0);
- if (VIR_REALLOC_N(*buf, size+got+1) < 0) {
- virReportOOMError();
- goto error;
- }
- memmove(*buf+size, data, got);
- (*buf)[size+got] = '\0';
- }
- continue;
-
- pollerr:
- virReportSystemError(errno,
- "%s", _("poll error"));
- goto error;
- }
-
- return 0;
-
-error:
- VIR_FREE(*outbuf);
- VIR_FREE(*errbuf);
- return -1;
-}
-
-/* Like gnulib's fread_file, but read no more than the specified maximum
- number of bytes. If the length of the input is <= max_len, and
- upon error while reading that data, it works just like fread_file. */
-static char *
-saferead_lim(int fd, size_t max_len, size_t *length)
-{
- char *buf = NULL;
- size_t alloc = 0;
- size_t size = 0;
- int save_errno;
-
- for (;;) {
- int count;
- int requested;
-
- if (size + BUFSIZ + 1 > alloc) {
- alloc += alloc / 2;
- if (alloc < size + BUFSIZ + 1)
- alloc = size + BUFSIZ + 1;
-
- if (VIR_REALLOC_N(buf, alloc) < 0) {
- save_errno = errno;
- break;
- }
- }
-
- /* Ensure that (size + requested <= max_len); */
- requested = MIN(size < max_len ? max_len - size : 0,
- alloc - size - 1);
- count = saferead(fd, buf + size, requested);
- size += count;
-
- if (count != requested || requested == 0) {
- save_errno = errno;
- if (count < 0)
- break;
- buf[size] = '\0';
- *length = size;
- return buf;
- }
- }
-
- VIR_FREE(buf);
- errno = save_errno;
- return NULL;
-}
-
-/* A wrapper around saferead_lim that maps a failure due to
- exceeding the maximum size limitation to EOVERFLOW. */
-int
-virFileReadLimFD(int fd, int maxlen, char **buf)
-{
- size_t len;
- char *s;
-
- if (maxlen <= 0) {
- errno = EINVAL;
- return -1;
- }
- s = saferead_lim(fd, maxlen+1, &len);
- if (s == NULL)
- return -1;
- if (len > maxlen || (int)len != len) {
- VIR_FREE(s);
- /* There was at least one byte more than MAXLEN.
- Set errno accordingly. */
- errno = EOVERFLOW;
- return -1;
- }
- *buf = s;
- return len;
-}
-
-int virFileReadAll(const char *path, int maxlen, char **buf)
-{
- int fd = open(path, O_RDONLY);
- if (fd < 0) {
- virReportSystemError(errno, _("Failed to open file '%s'"), path);
- return -1;
- }
-
- int len = virFileReadLimFD(fd, maxlen, buf);
- VIR_FORCE_CLOSE(fd);
- if (len < 0) {
- virReportSystemError(errno, _("Failed to read file '%s'"), path);
- return -1;
- }
-
- return len;
-}
-
-/* Truncate @path and write @str to it. If @mode is 0, ensure that
- @path exists; otherwise, use @mode if @path must be created.
- Return 0 for success, nonzero for failure.
- Be careful to preserve any errno value upon failure. */
-int virFileWriteStr(const char *path, const char *str, mode_t mode)
-{
- int fd;
-
- if (mode)
- fd = open(path, O_WRONLY|O_TRUNC|O_CREAT, mode);
- else
- fd = open(path, O_WRONLY|O_TRUNC);
- if (fd == -1)
- return -1;
-
- if (safewrite(fd, str, strlen(str)) < 0) {
- VIR_FORCE_CLOSE(fd);
- return -1;
- }
-
- /* Use errno from failed close only if there was no write error. */
- if (VIR_CLOSE(fd) != 0)
- return -1;
-
- return 0;
-}
-
-int virFileMatchesNameSuffix(const char *file,
- const char *name,
- const char *suffix)
-{
- int filelen = strlen(file);
- int namelen = strlen(name);
- int suffixlen = strlen(suffix);
-
- if (filelen == (namelen + suffixlen) &&
- STREQLEN(file, name, namelen) &&
- STREQLEN(file + namelen, suffix, suffixlen))
- return 1;
- else
- return 0;
-}
-
-int virFileHasSuffix(const char *str,
- const char *suffix)
-{
- int len = strlen(str);
- int suffixlen = strlen(suffix);
-
- if (len < suffixlen)
- return 0;
-
- return STRCASEEQ(str + len - suffixlen, suffix);
-}
-
-#define SAME_INODE(Stat_buf_1, Stat_buf_2) \
- ((Stat_buf_1).st_ino == (Stat_buf_2).st_ino \
- && (Stat_buf_1).st_dev == (Stat_buf_2).st_dev)
-
-/* Return nonzero if checkLink and checkDest
- refer to the same file. Otherwise, return 0. */
-int virFileLinkPointsTo(const char *checkLink,
- const char *checkDest)
-{
- struct stat src_sb;
- struct stat dest_sb;
-
- return (stat(checkLink, &src_sb) == 0
- && stat(checkDest, &dest_sb) == 0
- && SAME_INODE(src_sb, dest_sb));
-}
-
-
-
-static int
-virFileResolveLinkHelper(const char *linkpath,
- bool intermediatePaths,
- char **resultpath)
-{
- struct stat st;
-
- *resultpath = NULL;
-
- /* We don't need the full canonicalization of intermediate
- * directories, if linkpath is absolute and the basename is
- * already a non-symlink. */
- if (IS_ABSOLUTE_FILE_NAME(linkpath) && !intermediatePaths) {
- if (lstat(linkpath, &st) < 0)
- return -1;
-
- if (!S_ISLNK(st.st_mode)) {
- if (!(*resultpath = strdup(linkpath)))
- return -1;
- return 0;
- }
- }
-
- *resultpath = canonicalize_file_name(linkpath);
-
- return *resultpath == NULL ? -1 : 0;
-}
-
-/*
- * Attempt to resolve a symbolic link, returning an
- * absolute path where only the last component is guaranteed
- * not to be a symlink.
- *
- * Return 0 if path was not a symbolic, or the link was
- * resolved. Return -1 with errno set upon error
- */
-int virFileResolveLink(const char *linkpath,
- char **resultpath)
-{
- return virFileResolveLinkHelper(linkpath, false, resultpath);
-}
-
-/*
- * Attempt to resolve a symbolic link, returning an
- * absolute path where every component is guaranteed
- * not to be a symlink.
- *
- * Return 0 if path was not a symbolic, or the link was
- * resolved. Return -1 with errno set upon error
- */
-int virFileResolveAllLinks(const char *linkpath,
- char **resultpath)
-{
- return virFileResolveLinkHelper(linkpath, true, resultpath);
-}
-
-/*
- * Check whether the given file is a link.
- * Returns 1 in case of the file being a link, 0 in case it is not
- * a link and the negative errno in all other cases.
- */
-int virFileIsLink(const char *linkpath)
-{
- struct stat st;
-
- if (lstat(linkpath, &st) < 0)
- return -errno;
-
- return S_ISLNK(st.st_mode) != 0;
-}
-
-
-/*
- * Finds a requested executable file in the PATH env. e.g.:
- * "kvm-img" will return "/usr/bin/kvm-img"
- *
- * You must free the result
- */
-char *virFindFileInPath(const char *file)
-{
- char *path = NULL;
- char *pathiter;
- char *pathseg;
- char *fullpath = NULL;
-
- if (file == NULL)
- return NULL;
-
- /* if we are passed an absolute path (starting with /), return a
- * copy of that path, after validating that it is executable
- */
- if (IS_ABSOLUTE_FILE_NAME(file)) {
- if (virFileIsExecutable(file))
- return strdup(file);
- else
- return NULL;
- }
-
- /* If we are passed an anchored path (containing a /), then there
- * is no path search - it must exist in the current directory
- */
- if (strchr(file, '/')) {
- if (virFileIsExecutable(file))
- ignore_value(virFileAbsPath(file, &path));
- return path;
- }
-
- /* copy PATH env so we can tweak it */
- path = getenv("PATH");
-
- 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.
- */
- pathiter = path;
- while ((pathseg = strsep(&pathiter, ":")) != NULL) {
- if (virAsprintf(&fullpath, "%s/%s", pathseg, file) < 0 ||
- virFileIsExecutable(fullpath))
- break;
- VIR_FREE(fullpath);
- }
-
- VIR_FREE(path);
- return fullpath;
-}
-
-bool virFileIsDir(const char *path)
-{
- struct stat s;
- return (stat(path, &s) == 0) && S_ISDIR(s.st_mode);
-}
-
-bool virFileExists(const char *path)
-{
- return access(path, F_OK) == 0;
-}
-
-/* Check that a file is regular and has executable bits. If false is
- * returned, errno is valid.
- *
- * Note: In the presence of ACLs, this may return true for a file that
- * would actually fail with EACCES for a given user, or false for a
- * file that the user could actually execute, but setups with ACLs
- * that weird are unusual. */
-bool
-virFileIsExecutable(const char *file)
-{
- struct stat sb;
-
- /* We would also want to check faccessat if we cared about ACLs,
- * but we don't. */
- if (stat(file, &sb) < 0)
- return false;
- if (S_ISREG(sb.st_mode) && (sb.st_mode & 0111) != 0)
- return true;
- errno = S_ISDIR(sb.st_mode) ? EISDIR : EACCES;
- return false;
-}
-
-#ifndef WIN32
-/* Check that a file is accessible under certain
- * user & gid.
- * @mode can be F_OK, or a bitwise combination of R_OK, W_OK, and X_OK.
- * see 'man access' for more details.
- * Returns 0 on success, -1 on fail with errno set.
- */
-int
-virFileAccessibleAs(const char *path, int mode,
- uid_t uid, gid_t gid)
-{
- pid_t pid = 0;
- int status, ret = 0;
- int forkRet = 0;
-
- if (uid == getuid() &&
- gid == getgid())
- return access(path, mode);
-
- forkRet = virFork(&pid);
-
- if (pid < 0) {
- return -1;
- }
-
- if (pid) { /* parent */
- if (virProcessWait(pid, &status) < 0) {
- /* virProcessWait() already
- * reported error */
- return -1;
- }
-
- if (!WIFEXITED(status)) {
- errno = EINTR;
- return -1;
- }
-
- if (status) {
- errno = WEXITSTATUS(status);
- return -1;
- }
-
- return 0;
- }
-
- /* child.
- * Return positive value here. Parent
- * will change it to negative one. */
-
- if (forkRet < 0) {
- ret = errno;
- goto childerror;
- }
-
- if (virSetUIDGID(uid, gid) < 0) {
- ret = errno;
- goto childerror;
- }
-
- if (access(path, mode) < 0)
- ret = errno;
-
-childerror:
- if ((ret & 0xFF) != ret) {
- VIR_WARN("unable to pass desired return value %d", ret);
- ret = 0xFF;
- }
-
- _exit(ret);
-}
-
-/* virFileOpenForceOwnerMode() - an internal utility function called
- * only by virFileOpenAs(). Sets the owner and mode of the file
- * opened as "fd" if it's not correct AND the flags say it should be
- * forced. */
-static int
-virFileOpenForceOwnerMode(const char *path, int fd, mode_t mode,
- uid_t uid, gid_t gid, unsigned int flags)
-{
- int ret = 0;
- struct stat st;
-
- if (!(flags & (VIR_FILE_OPEN_FORCE_OWNER | VIR_FILE_OPEN_FORCE_MODE)))
- return 0;
-
- if (fstat(fd, &st) == -1) {
- ret = -errno;
- virReportSystemError(errno, _("stat of '%s' failed"), path);
- return ret;
- }
- /* NB: uid:gid are never "-1" (default) at this point - the caller
- * has always changed -1 to the value of get[gu]id().
- */
- if ((flags & VIR_FILE_OPEN_FORCE_OWNER) &&
- ((st.st_uid != uid) || (st.st_gid != gid)) &&
- (fchown(fd, uid, gid) < 0)) {
- ret = -errno;
- virReportSystemError(errno,
- _("cannot chown '%s' to (%u, %u)"),
- path, (unsigned int) uid,
- (unsigned int) gid);
- return ret;
- }
- if ((flags & VIR_FILE_OPEN_FORCE_MODE) &&
- ((mode & (S_IRWXU|S_IRWXG|S_IRWXO)) !=
- (st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO))) &&
- (fchmod(fd, mode) < 0)) {
- ret = -errno;
- virReportSystemError(errno,
- _("cannot set mode of '%s' to %04o"),
- path, mode);
- return ret;
- }
- return ret;
-}
-
-/* virFileOpenForked() - an internal utility function called only by
- * virFileOpenAs(). It forks, then the child does setuid+setgid to
- * given uid:gid and attempts to open the file, while the parent just
- * calls recvfd to get the open fd back from the child. returns the
- * fd, or -errno if there is an error. */
-static int
-virFileOpenForked(const char *path, int openflags, mode_t mode,
- uid_t uid, gid_t gid, unsigned int flags)
-{
- pid_t pid;
- int waitret, status, ret = 0;
- int fd = -1;
- int pair[2] = { -1, -1 };
- int forkRet;
-
- /* parent is running as root, but caller requested that the
- * file be opened as some other user and/or group). The
- * following dance avoids problems caused by root-squashing
- * NFS servers. */
-
- if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) < 0) {
- ret = -errno;
- virReportSystemError(errno,
- _("failed to create socket needed for '%s'"),
- path);
- return ret;
- }
-
- forkRet = virFork(&pid);
- if (pid < 0)
- return -errno;
-
- if (pid == 0) {
-
- /* child */
-
- VIR_FORCE_CLOSE(pair[0]); /* preserves errno */
- if (forkRet < 0) {
- /* error encountered and logged in virFork() after the fork. */
- ret = -errno;
- goto childerror;
- }
-
- /* set desired uid/gid, then attempt to create the file */
-
- if (virSetUIDGID(uid, gid) < 0) {
- ret = -errno;
- goto childerror;
- }
-
- if ((fd = open(path, openflags, mode)) < 0) {
- ret = -errno;
- virReportSystemError(errno,
- _("child process failed to create file '%s'"),
- path);
- goto childerror;
- }
-
- /* File is successfully open. Set permissions if requested. */
- ret = virFileOpenForceOwnerMode(path, fd, mode, uid, gid, flags);
- if (ret < 0)
- goto childerror;
-
- do {
- ret = sendfd(pair[1], fd);
- } while (ret < 0 && errno == EINTR);
-
- if (ret < 0) {
- ret = -errno;
- virReportSystemError(errno, "%s",
- _("child process failed to send fd to parent"));
- goto childerror;
- }
-
- childerror:
- /* ret tracks -errno on failure, but exit value must be positive.
- * If the child exits with EACCES, then the parent tries again. */
- /* XXX This makes assumptions about errno being < 255, which is
- * not true on Hurd. */
- VIR_FORCE_CLOSE(pair[1]);
- if (ret < 0) {
- VIR_FORCE_CLOSE(fd);
- }
- ret = -ret;
- if ((ret & 0xff) != ret) {
- VIR_WARN("unable to pass desired return value %d", ret);
- ret = 0xff;
- }
- _exit(ret);
- }
-
- /* parent */
-
- VIR_FORCE_CLOSE(pair[1]);
-
- do {
- fd = recvfd(pair[0], 0);
- } while (fd < 0 && errno == EINTR);
- VIR_FORCE_CLOSE(pair[0]); /* NB: this preserves errno */
-
- if (fd < 0 && errno != EACCES) {
- ret = -errno;
- while (waitpid(pid, NULL, 0) == -1 && errno == EINTR);
- return ret;
- }
-
- /* wait for child to complete, and retrieve its exit code */
- while ((waitret = waitpid(pid, &status, 0) == -1)
- && (errno == EINTR));
- if (waitret == -1) {
- ret = -errno;
- virReportSystemError(errno,
- _("failed to wait for child creating '%s'"),
- path);
- VIR_FORCE_CLOSE(fd);
- return ret;
- }
- if (!WIFEXITED(status) || (ret = -WEXITSTATUS(status)) == -EACCES ||
- fd == -1) {
- /* fall back to the simpler method, which works better in
- * some cases */
- VIR_FORCE_CLOSE(fd);
- if (flags & VIR_FILE_OPEN_NOFORK) {
- /* If we had already tried opening w/o fork+setuid and
- * failed, no sense trying again. Just set return the
- * original errno that we got at that time (by
- * definition, always either EACCES or EPERM - EACCES
- * is close enough).
- */
- return -EACCES;
- }
- if ((fd = open(path, openflags, mode)) < 0)
- return -errno;
- ret = virFileOpenForceOwnerMode(path, fd, mode, uid, gid, flags);
- if (ret < 0) {
- VIR_FORCE_CLOSE(fd);
- return ret;
- }
- }
- return fd;
-}
-
-/**
- * virFileOpenAs:
- * @path: file to open or create
- * @openflags: flags to pass to open
- * @mode: mode to use on creation or when forcing permissions
- * @uid: uid that should own file on creation
- * @gid: gid that should own file
- * @flags: bit-wise or of VIR_FILE_OPEN_* flags
- *
- * Open @path, and return an fd to the open file. @openflags contains
- * the flags normally passed to open(2), while those in @flags are
- * used internally. If @flags includes VIR_FILE_OPEN_NOFORK, then try
- * opening the file while executing with the current uid:gid
- * (i.e. don't fork+setuid+setgid before the call to open()). If
- * @flags includes VIR_FILE_OPEN_FORK, then try opening the file while
- * the effective user id is @uid (by forking a child process); this
- * allows one to bypass root-squashing NFS issues; NOFORK is always
- * tried before FORK (the absence of both flags is treated identically
- * to (VIR_FILE_OPEN_NOFORK | VIR_FILE_OPEN_FORK)). If @flags includes
- * VIR_FILE_OPEN_FORCE_OWNER, then ensure that @path is owned by
- * uid:gid before returning (even if it already existed with a
- * different owner). If @flags includes VIR_FILE_OPEN_FORCE_MODE,
- * ensure it has those permissions before returning (again, even if
- * the file already existed with different permissions).
- *
- * The return value (if non-negative) is the file descriptor, left
- * open. Returns -errno on failure.
- */
-int
-virFileOpenAs(const char *path, int openflags, mode_t mode,
- uid_t uid, gid_t gid, unsigned int flags)
-{
- int ret = 0, fd = -1;
-
- /* allow using -1 to mean "current value" */
- if (uid == (uid_t) -1)
- uid = getuid();
- if (gid == (gid_t) -1)
- gid = getgid();
-
- /* treat absence of both flags as presence of both for simpler
- * calling. */
- if (!(flags & (VIR_FILE_OPEN_NOFORK|VIR_FILE_OPEN_FORK)))
- flags |= VIR_FILE_OPEN_NOFORK|VIR_FILE_OPEN_FORK;
-
- if ((flags & VIR_FILE_OPEN_NOFORK)
- || (getuid() != 0)
- || ((uid == 0) && (gid == 0))) {
-
- if ((fd = open(path, openflags, mode)) < 0) {
- ret = -errno;
- if (!(flags & VIR_FILE_OPEN_FORK))
- goto error;
- } else {
- ret = virFileOpenForceOwnerMode(path, fd, mode, uid, gid, flags);
- if (ret < 0)
- goto error;
- }
- }
-
- /* If we either 1) didn't try opening as current user at all, or
- * 2) failed, and errno/virStorageFileIsSharedFS indicate we might
- * be successful if we try as a different uid, then try doing
- * fork+setuid+setgid before opening.
- */
- if ((fd < 0) && (flags & VIR_FILE_OPEN_FORK)) {
-
- if (ret < 0) {
- /* An open(2) that failed due to insufficient permissions
- * could return one or the other of these depending on OS
- * version and circumstances. Any other errno indicates a
- * problem that couldn't be remedied by fork+setuid
- * anyway. */
- if (ret != -EACCES && ret != -EPERM)
- goto error;
-
- /* On Linux we can also verify the FS-type of the
- * directory. (this is a NOP on other platforms). */
- if (virStorageFileIsSharedFS(path) <= 0)
- goto error;
- }
-
- /* passed all prerequisites - retry the open w/fork+setuid */
- if ((fd = virFileOpenForked(path, openflags, mode, uid, gid, flags)) < 0) {
- ret = fd;
- goto error;
- }
- }
-
- /* File is successfully opened */
- return fd;
-
-error:
- if (fd >= 0) {
- /* some other failure after the open succeeded */
- VIR_FORCE_CLOSE(fd);
- }
- /* whoever failed the open last has already set ret = -errno */
- return ret;
-}
-
-/* return -errno on failure, or 0 on success */
-static int virDirCreateNoFork(const char *path, mode_t mode, uid_t uid, gid_t gid,
- unsigned int flags) {
- int ret = 0;
- struct stat st;
-
- if ((mkdir(path, mode) < 0)
- && !((errno == EEXIST) && (flags & VIR_DIR_CREATE_ALLOW_EXIST))) {
- ret = -errno;
- virReportSystemError(errno, _("failed to create directory '%s'"),
- path);
- goto error;
- }
-
- if (stat(path, &st) == -1) {
- ret = -errno;
- virReportSystemError(errno, _("stat of '%s' failed"), path);
- goto error;
- }
- if (((st.st_uid != uid) || (st.st_gid != gid))
- && (chown(path, uid, gid) < 0)) {
- ret = -errno;
- virReportSystemError(errno, _("cannot chown '%s' to (%u, %u)"),
- path, (unsigned int) uid, (unsigned int) gid);
- goto error;
- }
- if ((flags & VIR_DIR_CREATE_FORCE_PERMS)
- && (chmod(path, mode) < 0)) {
- ret = -errno;
- virReportSystemError(errno,
- _("cannot set mode of '%s' to %04o"),
- path, mode);
- goto error;
- }
-error:
- return ret;
-}
-
-/* return -errno on failure, or 0 on success */
-int virDirCreate(const char *path, mode_t mode,
- uid_t uid, gid_t gid, unsigned int flags) {
- struct stat st;
- pid_t pid;
- int waitret;
- int status, ret = 0;
-
- /* allow using -1 to mean "current value" */
- if (uid == (uid_t) -1)
- uid = getuid();
- if (gid == (gid_t) -1)
- gid = getgid();
-
- if ((!(flags & VIR_DIR_CREATE_AS_UID))
- || (getuid() != 0)
- || ((uid == 0) && (gid == 0))
- || ((flags & VIR_DIR_CREATE_ALLOW_EXIST) && (stat(path, &st) >= 0))) {
- return virDirCreateNoFork(path, mode, uid, gid, flags);
- }
-
- int forkRet = virFork(&pid);
-
- if (pid < 0) {
- ret = -errno;
- return ret;
- }
-
- if (pid) { /* parent */
- /* wait for child to complete, and retrieve its exit code */
- while ((waitret = waitpid(pid, &status, 0) == -1) && (errno == EINTR));
- if (waitret == -1) {
- ret = -errno;
- virReportSystemError(errno,
- _("failed to wait for child creating '%s'"),
- path);
- goto parenterror;
- }
- if (!WIFEXITED(status) || (ret = -WEXITSTATUS(status)) == -EACCES) {
- /* fall back to the simpler method, which works better in
- * some cases */
- return virDirCreateNoFork(path, mode, uid, gid, flags);
- }
-parenterror:
- return ret;
- }
-
- /* child */
-
- if (forkRet < 0) {
- /* error encountered and logged in virFork() after the fork. */
- goto childerror;
- }
-
- /* set desired uid/gid, then attempt to create the directory */
-
- if (virSetUIDGID(uid, gid) < 0) {
- ret = -errno;
- goto childerror;
- }
- if (mkdir(path, mode) < 0) {
- ret = -errno;
- if (ret != -EACCES) {
- /* in case of EACCES, the parent will retry */
- virReportSystemError(errno, _("child failed to create directory '%s'"),
- path);
- }
- goto childerror;
- }
- /* check if group was set properly by creating after
- * setgid. If not, try doing it with chown */
- if (stat(path, &st) == -1) {
- ret = -errno;
- virReportSystemError(errno,
- _("stat of '%s' failed"), path);
- goto childerror;
- }
- if ((st.st_gid != gid) && (chown(path, (uid_t) -1, gid) < 0)) {
- ret = -errno;
- virReportSystemError(errno,
- _("cannot chown '%s' to group %u"),
- path, (unsigned int) gid);
- goto childerror;
- }
- if ((flags & VIR_DIR_CREATE_FORCE_PERMS)
- && chmod(path, mode) < 0) {
- virReportSystemError(errno,
- _("cannot set mode of '%s' to %04o"),
- path, mode);
- goto childerror;
- }
-childerror:
- _exit(ret);
-}
-
-#else /* WIN32 */
-
-int
-virFileAccessibleAs(const char *path,
- int mode,
- uid_t uid ATTRIBUTE_UNUSED,
- gid_t gid ATTRIBUTE_UNUSED)
-{
-
- VIR_WARN("Ignoring uid/gid due to WIN32");
-
- return access(path, mode);
-}
-
-/* return -errno on failure, or 0 on success */
-int virFileOpenAs(const char *path ATTRIBUTE_UNUSED,
- int openflags ATTRIBUTE_UNUSED,
- mode_t mode ATTRIBUTE_UNUSED,
- uid_t uid ATTRIBUTE_UNUSED,
- gid_t gid ATTRIBUTE_UNUSED,
- unsigned int flags_unused ATTRIBUTE_UNUSED)
-{
- virReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("virFileOpenAs is not implemented for WIN32"));
-
- return -ENOSYS;
-}
-
-int virDirCreate(const char *path ATTRIBUTE_UNUSED,
- mode_t mode ATTRIBUTE_UNUSED,
- uid_t uid ATTRIBUTE_UNUSED,
- gid_t gid ATTRIBUTE_UNUSED,
- unsigned int flags_unused ATTRIBUTE_UNUSED)
-{
- virReportError(VIR_ERR_INTERNAL_ERROR,
- "%s", _("virDirCreate is not implemented for WIN32"));
-
- return -ENOSYS;
-}
-#endif /* WIN32 */
-
-static int virFileMakePathHelper(char *path, mode_t mode)
-{
- struct stat st;
- char *p;
+#include "virprocess.h"
+#include "virstring.h"
+#include "virutil.h"
- VIR_DEBUG("path=%s mode=0%o", path, mode);
+#ifndef NSIG
+# define NSIG 32
+#endif
- if (stat(path, &st) >= 0) {
- if (S_ISDIR(st.st_mode))
- return 0;
+verify(sizeof(gid_t) <= sizeof(unsigned int) &&
+ sizeof(uid_t) <= sizeof(unsigned int));
- errno = ENOTDIR;
- return -1;
- }
+#define VIR_FROM_THIS VIR_FROM_NONE
- if (errno != ENOENT)
- return -1;
+#ifndef WIN32
- if ((p = strrchr(path, '/')) == NULL) {
- errno = EINVAL;
+int virSetInherit(int fd, bool inherit) {
+ int fflags;
+ if ((fflags = fcntl(fd, F_GETFD)) < 0)
return -1;
- }
-
- if (p != path) {
- *p = '\0';
-
- if (virFileMakePathHelper(path, mode) < 0)
- return -1;
-
- *p = '/';
- }
-
- if (mkdir(path, mode) < 0 && errno != EEXIST)
+ if (inherit)
+ fflags &= ~FD_CLOEXEC;
+ else
+ fflags |= FD_CLOEXEC;
+ if ((fcntl(fd, F_SETFD, fflags)) < 0)
return -1;
-
return 0;
}
-/**
- * Creates the given directory with mode 0777 if it's not already existing.
- *
- * Returns 0 on success, or -1 if an error occurred (in which case, errno
- * is set appropriately).
- */
-int virFileMakePath(const char *path)
-{
- return virFileMakePathWithMode(path, 0777);
-}
+#else /* WIN32 */
-int
-virFileMakePathWithMode(const char *path,
- mode_t mode)
+int virSetInherit(int fd ATTRIBUTE_UNUSED, bool inherit ATTRIBUTE_UNUSED)
{
- int ret = -1;
- char *tmp;
-
- if ((tmp = strdup(path)) == NULL)
- goto cleanup;
-
- ret = virFileMakePathHelper(tmp, mode);
-
-cleanup:
- VIR_FREE(tmp);
- return ret;
+ /* FIXME: Currently creating child processes is not supported on
+ * Win32, so there is no point in failing calls that are only relevant
+ * when creating child processes. So just pretend that we changed the
+ * inheritance property of the given fd as requested. */
+ return 0;
}
-/* Build up a fully qualified path for a config file to be
- * associated with a persistent guest or network */
-char *
-virFileBuildPath(const char *dir, const char *name, const char *ext)
-{
- char *path;
-
- if (ext == NULL) {
- if (virAsprintf(&path, "%s/%s", dir, name) < 0) {
- virReportOOMError();
- return NULL;
- }
- } else {
- if (virAsprintf(&path, "%s/%s%s", dir, name, ext) < 0) {
- virReportOOMError();
- return NULL;
- }
- }
+#endif /* WIN32 */
- return path;
+int virSetBlocking(int fd, bool blocking) {
+ return set_nonblocking_flag(fd, !blocking);
}
-/* Open a non-blocking master side of a pty. If ttyName is not NULL,
- * then populate it with the name of the slave. If rawmode is set,
- * also put the master side into raw mode before returning. */
-#ifndef WIN32
-int virFileOpenTty(int *ttymaster,
- char **ttyName,
- int rawmode)
-{
- /* XXX A word of caution - on some platforms (Solaris and HP-UX),
- * additional ioctl() calls are needs after opening the slave
- * before it will cause isatty() to return true. Should we make
- * virFileOpenTty also return the opened slave fd, so the caller
- * doesn't have to worry about that mess? */
- int ret = -1;
- int slave = -1;
- char *name = NULL;
-
- /* Unfortunately, we can't use the name argument of openpty, since
- * there is no guarantee on how large the buffer has to be.
- * Likewise, we can't use the termios argument: we have to use
- * read-modify-write since there is no portable way to initialize
- * a struct termios without use of tcgetattr. */
- if (openpty(ttymaster, &slave, NULL, NULL, NULL) < 0)
- return -1;
-
- /* What a shame that openpty cannot atomically set FD_CLOEXEC, but
- * that using posix_openpt/grantpt/unlockpt/ptsname is not
- * thread-safe, and that ptsname_r is not portable. */
- if (virSetNonBlock(*ttymaster) < 0 ||
- virSetCloseExec(*ttymaster) < 0)
- goto cleanup;
-
- /* While Linux supports tcgetattr on either the master or the
- * slave, Solaris requires it to be on the slave. */
- if (rawmode) {
- struct termios ttyAttr;
- if (tcgetattr(slave, &ttyAttr) < 0)
- goto cleanup;
-
- cfmakeraw(&ttyAttr);
-
- if (tcsetattr(slave, TCSADRAIN, &ttyAttr) < 0)
- goto cleanup;
- }
-
- /* ttyname_r on the slave is required by POSIX, while ptsname_r on
- * the master is a glibc extension, and the POSIX ptsname is not
- * thread-safe. Since openpty gave us both descriptors, guess
- * which way we will determine the name? :) */
- if (ttyName) {
- /* Initial guess of 64 is generally sufficient; rely on ERANGE
- * to tell us if we need to grow. */
- size_t len = 64;
- int rc;
-
- if (VIR_ALLOC_N(name, len) < 0)
- goto cleanup;
-
- while ((rc = ttyname_r(slave, name, len)) == ERANGE) {
- if (VIR_RESIZE_N(name, len, len, len) < 0)
- goto cleanup;
- }
- if (rc != 0) {
- errno = rc;
- goto cleanup;
- }
- *ttyName = name;
- name = NULL;
- }
-
- ret = 0;
-
-cleanup:
- if (ret != 0)
- VIR_FORCE_CLOSE(*ttymaster);
- VIR_FORCE_CLOSE(slave);
- VIR_FREE(name);
-
- return ret;
-}
-#else /* WIN32 */
-int virFileOpenTty(int *ttymaster ATTRIBUTE_UNUSED,
- char **ttyName ATTRIBUTE_UNUSED,
- int rawmode ATTRIBUTE_UNUSED)
-{
- /* mingw completely lacks pseudo-terminals, and the gnulib
- * replacements are not (yet) license compatible. */
- errno = ENOSYS;
- return -1;
+int virSetNonBlock(int fd) {
+ return virSetBlocking(fd, false);
}
-#endif /* WIN32 */
-bool virFileIsAbsPath(const char *path)
+int virSetCloseExec(int fd)
{
- 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;
+ return virSetInherit(fd, false);
}
+int
+virPipeReadUntilEOF(int outfd, int errfd,
+ char **outbuf, char **errbuf) {
-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;
+ struct pollfd fds[2];
+ int i;
+ int finished[2];
- while (path[0] &&
- !VIR_FILE_IS_DIR_SEPARATOR(path[0]))
- path++;
+ fds[0].fd = outfd;
+ fds[0].events = POLLIN;
+ fds[0].revents = 0;
+ finished[0] = 0;
+ fds[1].fd = errfd;
+ fds[1].events = POLLIN;
+ fds[1].revents = 0;
+ finished[1] = 0;
- /* Possibly skip a backslash after the share name */
- if (VIR_FILE_IS_DIR_SEPARATOR(path[0]))
- path++;
+ while (!(finished[0] && finished[1])) {
- return path;
+ if (poll(fds, ARRAY_CARDINALITY(fds), -1) < 0) {
+ if ((errno == EAGAIN) || (errno == EINTR))
+ continue;
+ goto pollerr;
}
- }
-#endif
-
- /* Skip initial slashes */
- if (VIR_FILE_IS_DIR_SEPARATOR(path[0])) {
- while (VIR_FILE_IS_DIR_SEPARATOR(path[0]))
- path++;
- return path;
- }
+ for (i = 0; i < ARRAY_CARDINALITY(fds); ++i) {
+ char data[1024], **buf;
+ int got, size;
-#ifdef WIN32
- /* Skip X:\ */
- if (c_isalpha(path[0]) &&
- path[1] == ':' &&
- VIR_FILE_IS_DIR_SEPARATOR(path[2]))
- return path + 3;
-#endif
+ if (!(fds[i].revents))
+ continue;
+ else if (fds[i].revents & POLLHUP)
+ finished[i] = 1;
- return path;
-}
+ if (!(fds[i].revents & POLLIN)) {
+ if (fds[i].revents & POLLHUP)
+ continue;
+ virReportError(VIR_ERR_INTERNAL_ERROR,
+ "%s", _("Unknown poll response."));
+ goto error;
+ }
+ got = read(fds[i].fd, data, sizeof(data));
-/*
- * Creates an absolute path for a potentially relative path.
- * Return 0 if the path was not relative, or on success.
- * Return -1 on error.
- *
- * You must free the result.
- */
-int virFileAbsPath(const char *path, char **abspath)
-{
- char *buf;
+ if (got == sizeof(data))
+ finished[i] = 0;
- if (path[0] == '/') {
- if (!(*abspath = strdup(path)))
- return -1;
- } else {
- buf = getcwd(NULL, 0);
- if (buf == NULL)
- return -1;
+ if (got == 0) {
+ finished[i] = 1;
+ continue;
+ }
+ if (got < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ break;
+ goto pollerr;
+ }
- if (virAsprintf(abspath, "%s/%s", buf, path) < 0) {
- VIR_FREE(buf);
- return -1;
+ buf = ((fds[i].fd == outfd) ? outbuf : errbuf);
+ size = (*buf ? strlen(*buf) : 0);
+ if (VIR_REALLOC_N(*buf, size+got+1) < 0) {
+ virReportOOMError();
+ goto error;
+ }
+ memmove(*buf+size, data, got);
+ (*buf)[size+got] = '\0';
}
- VIR_FREE(buf);
- }
-
- return 0;
-}
-
-/* Remove spurious / characters from a path. The result must be freed */
-char *
-virFileSanitizePath(const char *path)
-{
- const char *cur = path;
- char *cleanpath;
- int idx = 0;
-
- cleanpath = strdup(path);
- if (!cleanpath) {
- virReportOOMError();
- return NULL;
- }
-
- /* Need to sanitize:
- * // -> //
- * /// -> /
- * /../foo -> /../foo
- * /foo///bar/ -> /foo/bar
- */
+ continue;
- /* Starting with // is valid posix, but ///foo == /foo */
- if (cur[0] == '/' && cur[1] == '/' && cur[2] != '/') {
- idx = 2;
- cur += 2;
+ pollerr:
+ virReportSystemError(errno,
+ "%s", _("poll error"));
+ goto error;
}
- /* Sanitize path in place */
- while (*cur != '\0') {
- if (*cur != '/') {
- cleanpath[idx++] = *cur++;
- continue;
- }
-
- /* Skip all extra / */
- while (*++cur == '/')
- continue;
-
- /* Don't add a trailing / */
- if (idx != 0 && *cur == '\0')
- break;
-
- cleanpath[idx++] = '/';
- }
- cleanpath[idx] = '\0';
+ return 0;
- return cleanpath;
+error:
+ VIR_FREE(*outbuf);
+ VIR_FREE(*errbuf);
+ return -1;
}
/* Convert C from hexadecimal character to integer. */
#endif
-#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R
-/* search /proc/mounts for mount point of *type; return pointer to
- * malloc'ed string of the path if found, otherwise return NULL
- * with errno set to an appropriate value.
- */
-char *virFileFindMountPoint(const char *type)
-{
- FILE *f;
- struct mntent mb;
- char mntbuf[1024];
- char *ret = NULL;
-
- f = setmntent("/proc/mounts", "r");
- if (!f)
- return NULL;
-
- while (getmntent_r(f, &mb, mntbuf, sizeof(mntbuf))) {
- if (STREQ(mb.mnt_type, type)) {
- ret = strdup(mb.mnt_dir);
- goto cleanup;
- }
- }
-
- if (!ret)
- errno = ENOENT;
-
-cleanup:
- endmntent(f);
-
- return ret;
-}
-
-#else /* defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R */
-
-char *
-virFileFindMountPoint(const char *type ATTRIBUTE_UNUSED)
-{
- errno = ENOSYS;
-
- return NULL;
-}
-
-#endif /* defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R */
-
#if defined(UDEVADM) || defined(UDEVSETTLE)
void virFileWaitForDevices(void)
{
void virFileWaitForDevices(void) {}
#endif
-int virBuildPathInternal(char **path, ...)
-{
- char *path_component = NULL;
- virBuffer buf = VIR_BUFFER_INITIALIZER;
- va_list ap;
- int ret = 0;
-
- va_start(ap, path);
-
- path_component = va_arg(ap, char *);
- virBufferAdd(&buf, path_component, -1);
-
- while ((path_component = va_arg(ap, char *)) != NULL)
- {
- virBufferAddChar(&buf, '/');
- virBufferAdd(&buf, path_component, -1);
- }
-
- va_end(ap);
-
- *path = virBufferContentAndReset(&buf);
- if (*path == NULL) {
- ret = -1;
- }
-
- return ret;
-}
-
#if HAVE_LIBDEVMAPPER_H
bool
virIsDevMapperDevice(const char *dev_name)
# define MAX(a, b) ((a) > (b) ? (a) : (b))
# endif
-ssize_t saferead(int fd, void *buf, size_t count) ATTRIBUTE_RETURN_CHECK;
-ssize_t safewrite(int fd, const void *buf, size_t count)
- ATTRIBUTE_RETURN_CHECK;
-int safezero(int fd, off_t offset, off_t len)
- ATTRIBUTE_RETURN_CHECK;
-
int virSetBlocking(int fd, bool blocking) ATTRIBUTE_RETURN_CHECK;
int virSetNonBlock(int fd) ATTRIBUTE_RETURN_CHECK;
int virSetInherit(int fd, bool inherit) ATTRIBUTE_RETURN_CHECK;
int virSetUIDGIDWithCaps(uid_t uid, gid_t gid, unsigned long long capBits,
bool clearExistingCaps);
-int virFileReadLimFD(int fd, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK;
-
-int virFileReadAll(const char *path, int maxlen, char **buf) ATTRIBUTE_RETURN_CHECK;
-
-int virFileWriteStr(const char *path, const char *str, mode_t mode)
- ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(2) ATTRIBUTE_RETURN_CHECK;
-
-int virFileMatchesNameSuffix(const char *file,
- const char *name,
- const char *suffix);
-
-int virFileHasSuffix(const char *str,
- const char *suffix);
-
-int virFileStripSuffix(char *str,
- const char *suffix) ATTRIBUTE_RETURN_CHECK;
-
-int virFileLinkPointsTo(const char *checkLink,
- const char *checkDest);
-
-int virFileResolveLink(const char *linkpath,
- char **resultpath) ATTRIBUTE_RETURN_CHECK;
-int virFileResolveAllLinks(const char *linkpath,
- char **resultpath) ATTRIBUTE_RETURN_CHECK;
-
-int virFileIsLink(const char *linkpath)
- ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
-
-char *virFindFileInPath(const char *file);
-
-bool virFileIsDir (const char *file) ATTRIBUTE_NONNULL(1);
-bool virFileExists(const char *file) ATTRIBUTE_NONNULL(1);
-bool virFileIsExecutable(const char *file) ATTRIBUTE_NONNULL(1);
-
-char *virFileSanitizePath(const char *path);
-
-enum {
- VIR_FILE_OPEN_NONE = 0,
- VIR_FILE_OPEN_NOFORK = (1 << 0),
- VIR_FILE_OPEN_FORK = (1 << 1),
- VIR_FILE_OPEN_FORCE_MODE = (1 << 2),
- VIR_FILE_OPEN_FORCE_OWNER = (1 << 3),
-};
-int virFileAccessibleAs(const char *path, int mode,
- uid_t uid, gid_t gid)
- ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
-int virFileOpenAs(const char *path, int openflags, mode_t mode,
- uid_t uid, gid_t gid,
- unsigned int flags)
- ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
-
-enum {
- VIR_DIR_CREATE_NONE = 0,
- VIR_DIR_CREATE_AS_UID = (1 << 0),
- VIR_DIR_CREATE_FORCE_PERMS = (1 << 1),
- VIR_DIR_CREATE_ALLOW_EXIST = (1 << 2),
-};
-int virDirCreate(const char *path, mode_t mode, uid_t uid, gid_t gid,
- unsigned int flags) ATTRIBUTE_RETURN_CHECK;
-int virFileMakePath(const char *path) ATTRIBUTE_RETURN_CHECK;
-int virFileMakePathWithMode(const char *path,
- mode_t mode) ATTRIBUTE_RETURN_CHECK;
-
-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,
- int rawmode);
-
int virScaleInteger(unsigned long long *value, const char *suffix,
unsigned long long scale, unsigned long long limit)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
int virGetGroupID(const char *name,
gid_t *gid) ATTRIBUTE_RETURN_CHECK;
-char *virFileFindMountPoint(const char *type);
-
-void virFileWaitForDevices(void);
-
-# define virBuildPath(path, ...) virBuildPathInternal(path, __VA_ARGS__, NULL)
-int virBuildPathInternal(char **path, ...) ATTRIBUTE_SENTINEL;
-
bool virIsDevMapperDevice(const char *dev_name) ATTRIBUTE_NONNULL(1);
bool virValidateWWN(const char *wwn);
#include "virutil.h"
#include "virlog.h"
#include "virerror.h"
+#include "virfile.h"
#include "virstring.h"
#define VIR_FROM_THIS VIR_FROM_VBOX
#include "internal.h"
#include "virerror.h"
+#include "virfile.h"
#include "virconf.h"
#include "viralloc.h"
#include "virlog.h"
#include <xen/dom0_ops.h>
#include "virerror.h"
+#include "virfile.h"
#include "datatypes.h"
#include "xm_internal.h"
#include "xen_driver.h"
/*
* eventtest.c: Test the libvirtd event loop impl
*
- * Copyright (C) 2009, 2011 Red Hat, Inc.
+ * Copyright (C) 2009, 2011-2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "testutils.h"
#include "internal.h"
+#include "virfile.h"
#include "virthread.h"
#include "virlog.h"
#include "virutil.h"
/*
- * Copyright (C) 2012 Red Hat, Inc.
+ * Copyright (C) 2012-2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "virutil.h"
#include "c-ctype.h"
#include "virerror.h"
+#include "virfile.h"
#include "virlog.h"
#include "virconf.h"
#include "virstring.h"
#include "viralloc.h"
#include "virlog.h"
#include "virerror.h"
+#include "virfile.h"
#include "security/security_manager.h"
#include "virstring.h"
/*
- * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2011, 2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "virutil.h"
#include "virerror.h"
#include "viralloc.h"
+#include "virfile.h"
#include "virlog.h"
#include "virlockspace.h"
# include "virutil.h"
# include "virerror.h"
# include "viralloc.h"
+# include "virfile.h"
# include "virlog.h"
# include "virportallocator.h"
# include "virstring.h"
#include "testutils.h"
#include "vircommand.h"
#include "virerror.h"
+#include "virfile.h"
#include "virlog.h"
#include "virstoragefile.h"
#include "virstring.h"
/*
* virsh-interface.c: Commands to manage host interface
*
- * Copyright (C) 2005, 2007-2012 Red Hat, Inc.
+ * Copyright (C) 2005, 2007-2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "internal.h"
#include "virbuffer.h"
#include "viralloc.h"
+#include "virfile.h"
#include "virutil.h"
#include "virxml.h"
#include "virstring.h"
/*
* virsh-network.c: Commands to manage network
*
- * Copyright (C) 2005, 2007-2012 Red Hat, Inc.
+ * Copyright (C) 2005, 2007-2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "internal.h"
#include "virbuffer.h"
#include "viralloc.h"
+#include "virfile.h"
#include "virxml.h"
#include "conf/network_conf.h"
#include "internal.h"
#include "virbuffer.h"
#include "viralloc.h"
+#include "virfile.h"
#include "virxml.h"
#include "conf/node_device_conf.h"
/*
* virsh-nwfilter.c: Commands to manage network filters
*
- * Copyright (C) 2005, 2007-2012 Red Hat, Inc.
+ * Copyright (C) 2005, 2007-2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "internal.h"
#include "virbuffer.h"
#include "viralloc.h"
+#include "virfile.h"
#include "virutil.h"
#include "virxml.h"
/*
* virsh-pool.c: Commands to manage storage pool
*
- * Copyright (C) 2005, 2007-2012 Red Hat, Inc.
+ * Copyright (C) 2005, 2007-2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "internal.h"
#include "virbuffer.h"
#include "viralloc.h"
+#include "virfile.h"
#include "virxml.h"
#include "conf/storage_conf.h"
#include "virstring.h"
/*
* virsh-secret.c: Commands to manage secret
*
- * Copyright (C) 2005, 2007-2012 Red Hat, Inc.
+ * Copyright (C) 2005, 2007-2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "base64.h"
#include "virbuffer.h"
#include "viralloc.h"
+#include "virfile.h"
#include "virutil.h"
#include "virxml.h"
/*
* virsh-snapshot.c: Commands to manage domain snapshot
*
- * Copyright (C) 2005, 2007-2012 Red Hat, Inc.
+ * Copyright (C) 2005, 2007-2013 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
#include "internal.h"
#include "virbuffer.h"
#include "viralloc.h"
+#include "virfile.h"
#include "virsh-domain.h"
#include "virxml.h"
#include "conf/snapshot_conf.h"