From bfe7721d5064e00ac733157a4601da5042a29ab7 Mon Sep 17 00:00:00 2001 From: Laine Stump Date: Thu, 9 May 2013 14:59:04 -0400 Subject: [PATCH] util: move virFile* functions from virutil.c to virfile.c These all existed before virfile.c was created, and for some reason weren't moved. This is mostly straightfoward, although the syntax rule prohibiting write() had to be changed to have an exception for virfile.c instead of virutil.c. This movement pointed out that there is a function called virBuildPath(), and another almost identical function called virFileBuildPath(). They really should be a single function, which I'll take care of as soon as I figure out what the arglist should look like. --- cfg.mk | 2 +- src/esx/esx_driver.c | 5 +- src/esx/esx_storage_backend_vmfs.c | 1 + src/libvirt.c | 1 + src/libvirt_private.syms | 66 +- src/node_device/node_device_driver.c | 3 +- src/node_device/node_device_udev.c | 1 + src/parallels/parallels_driver.c | 1 + src/parallels/parallels_network.c | 1 + src/parallels/parallels_storage.c | 1 + src/rpc/virnetsshsession.c | 3 +- src/rpc/virnettlscontext.c | 1 + src/security/security_dac.c | 1 + src/storage/parthelper.c | 3 +- src/storage/storage_backend_disk.c | 1 + src/util/virebtables.c | 1 + src/util/virfile.c | 1448 ++++++++++++++++++++++- src/util/virfile.h | 121 +- src/util/virhook.c | 2 +- src/util/viriptables.c | 1 + src/util/virkeyfile.c | 3 +- src/util/virnetdevveth.c | 3 +- src/util/virsysinfo.c | 3 +- src/util/virusb.c | 3 +- src/util/virutil.c | 1603 ++------------------------ src/util/virutil.h | 111 -- src/vbox/vbox_XPCOMCGlue.c | 1 + src/vmx/vmx.c | 1 + src/xen/xm_internal.c | 1 + tests/eventtest.c | 3 +- tests/libvirtdconftest.c | 3 +- tests/securityselinuxtest.c | 1 + tests/virlockspacetest.c | 3 +- tests/virportallocatortest.c | 1 + tests/virstoragetest.c | 1 + tools/virsh-interface.c | 3 +- tools/virsh-network.c | 3 +- tools/virsh-nodedev.c | 1 + tools/virsh-nwfilter.c | 3 +- tools/virsh-pool.c | 3 +- tools/virsh-secret.c | 3 +- tools/virsh-snapshot.c | 3 +- 42 files changed, 1746 insertions(+), 1678 deletions(-) diff --git a/cfg.mk b/cfg.mk index 227c18b601..0bf5bfc1b6 100644 --- a/cfg.mk +++ b/cfg.mk @@ -841,7 +841,7 @@ $(srcdir)/src/remote/remote_client_bodies.h: $(srcdir)/src/remote/remote_protoco # 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$$ diff --git a/src/esx/esx_driver.c b/src/esx/esx_driver.c index 6979fbffde..b23e7f6cf2 100644 --- a/src/esx/esx_driver.c +++ b/src/esx/esx_driver.c @@ -2,7 +2,7 @@ /* * 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 * Copyright (C) 2009 Maximilian Wilhelm * @@ -29,6 +29,7 @@ #include "snapshot_conf.h" #include "virauth.h" #include "viralloc.h" +#include "virfile.h" #include "virlog.h" #include "viruuid.h" #include "vmx.h" @@ -44,8 +45,8 @@ #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 diff --git a/src/esx/esx_storage_backend_vmfs.c b/src/esx/esx_storage_backend_vmfs.c index 63974abcf6..3b4d73a126 100644 --- a/src/esx/esx_storage_backend_vmfs.c +++ b/src/esx/esx_storage_backend_vmfs.c @@ -32,6 +32,7 @@ #include "internal.h" #include "md5.h" #include "viralloc.h" +#include "virfile.h" #include "virlog.h" #include "viruuid.h" #include "storage_conf.h" diff --git a/src/libvirt.c b/src/libvirt.c index ab75110821..2b3515e32c 100644 --- a/src/libvirt.c +++ b/src/libvirt.c @@ -59,6 +59,7 @@ # include "rpc/virnettlscontext.h" #endif #include "vircommand.h" +#include "virfile.h" #include "virrandom.h" #include "viruri.h" #include "virthread.h" diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index bb70595e0b..2d7fe5b40f 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -1271,18 +1271,51 @@ virEventPollUpdateTimeout; # 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 @@ -1861,44 +1894,11 @@ virUSBDeviceSetUsedBy; # 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; diff --git a/src/node_device/node_device_driver.c b/src/node_device/node_device_driver.c index c596901747..52586b898a 100644 --- a/src/node_device/node_device_driver.c +++ b/src/node_device/node_device_driver.c @@ -1,7 +1,7 @@ /* * 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 * @@ -32,6 +32,7 @@ #include "virerror.h" #include "datatypes.h" #include "viralloc.h" +#include "virfile.h" #include "virlog.h" #include "virstring.h" #include "node_device_conf.h" diff --git a/src/node_device/node_device_udev.c b/src/node_device/node_device_udev.c index 7036c8ca02..4aeaed5a8f 100644 --- a/src/node_device/node_device_udev.c +++ b/src/node_device/node_device_udev.c @@ -37,6 +37,7 @@ #include "viralloc.h" #include "viruuid.h" #include "virbuffer.h" +#include "virfile.h" #include "virpci.h" #include "virstring.h" diff --git a/src/parallels/parallels_driver.c b/src/parallels/parallels_driver.c index 74a52f0d7a..b7c4ec4ff3 100644 --- a/src/parallels/parallels_driver.c +++ b/src/parallels/parallels_driver.c @@ -45,6 +45,7 @@ #include "virlog.h" #include "vircommand.h" #include "configmake.h" +#include "virfile.h" #include "virstoragefile.h" #include "nodeinfo.h" #include "c-ctype.h" diff --git a/src/parallels/parallels_network.c b/src/parallels/parallels_network.c index 2e5523b031..8990609095 100644 --- a/src/parallels/parallels_network.c +++ b/src/parallels/parallels_network.c @@ -27,6 +27,7 @@ #include "dirname.h" #include "viralloc.h" #include "virerror.h" +#include "virfile.h" #include "md5.h" #include "parallels_utils.h" #include "virstring.h" diff --git a/src/parallels/parallels_storage.c b/src/parallels/parallels_storage.c index 6a02fd6523..3d55e82e05 100644 --- a/src/parallels/parallels_storage.c +++ b/src/parallels/parallels_storage.c @@ -36,6 +36,7 @@ #include "configmake.h" #include "virstoragefile.h" #include "virerror.h" +#include "virfile.h" #include "parallels_utils.h" #include "virstring.h" diff --git a/src/rpc/virnetsshsession.c b/src/rpc/virnetsshsession.c index 27750fc66b..189d9289aa 100644 --- a/src/rpc/virnetsshsession.c +++ b/src/rpc/virnetsshsession.c @@ -1,7 +1,7 @@ /* * 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 @@ -33,6 +33,7 @@ #include "virthread.h" #include "virutil.h" #include "virerror.h" +#include "virfile.h" #include "virobject.h" #include "virstring.h" diff --git a/src/rpc/virnettlscontext.c b/src/rpc/virnettlscontext.c index 7f5975dcff..1a7ccb8732 100644 --- a/src/rpc/virnettlscontext.c +++ b/src/rpc/virnettlscontext.c @@ -33,6 +33,7 @@ #include "viralloc.h" #include "virerror.h" +#include "virfile.h" #include "virutil.h" #include "virlog.h" #include "virthread.h" diff --git a/src/security/security_dac.c b/src/security/security_dac.c index cd214d8860..16cce0ea14 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -25,6 +25,7 @@ #include "security_dac.h" #include "virerror.h" +#include "virfile.h" #include "viralloc.h" #include "virlog.h" #include "virpci.h" diff --git a/src/storage/parthelper.c b/src/storage/parthelper.c index 038487ad8a..c04f1bdd3d 100644 --- a/src/storage/parthelper.c +++ b/src/storage/parthelper.c @@ -10,7 +10,7 @@ * 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 @@ -42,6 +42,7 @@ #include #include "virutil.h" +#include "virfile.h" #include "c-ctype.h" #include "configmake.h" #include "virstring.h" diff --git a/src/storage/storage_backend_disk.c b/src/storage/storage_backend_disk.c index 52bd572651..09c2d2c302 100644 --- a/src/storage/storage_backend_disk.c +++ b/src/storage/storage_backend_disk.c @@ -32,6 +32,7 @@ #include "storage_backend_disk.h" #include "viralloc.h" #include "vircommand.h" +#include "virfile.h" #include "configmake.h" #include "virstring.h" diff --git a/src/util/virebtables.c b/src/util/virebtables.c index 6bc6fed3ca..3834ea88a2 100644 --- a/src/util/virebtables.c +++ b/src/util/virebtables.c @@ -46,6 +46,7 @@ #include "vircommand.h" #include "viralloc.h" #include "virerror.h" +#include "virfile.h" #include "virlog.h" #include "virthread.h" #include "virstring.h" diff --git a/src/util/virfile.c b/src/util/virfile.c index 25a0501cdf..1491f27ada 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -1,7 +1,7 @@ /* * 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 @@ -25,24 +25,36 @@ #include #include "internal.h" -#include "virfile.h" - +#include #include +#include #include +#include +#include +#include #include #include +#include +#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R +# include +#endif +#include #if defined(__linux__) && HAVE_DECL_LO_FLAGS_AUTOCLEAR # include # include #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 @@ -728,3 +740,1431 @@ cleanup: 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; +} diff --git a/src/util/virfile.h b/src/util/virfile.h index 5f0dd2ba2a..bd353315f9 100644 --- a/src/util/virfile.h +++ b/src/util/virfile.h @@ -1,7 +1,7 @@ /* * 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 @@ -23,8 +23,8 @@ */ -#ifndef __VIR_FILES_H_ -# define __VIR_FILES_H_ +#ifndef __VIR_FILE_H_ +# define __VIR_FILE_H_ # include @@ -36,6 +36,12 @@ typedef enum virFileCloseFlags { 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; @@ -110,4 +116,111 @@ int virFileLoopDeviceAssociate(const char *file, 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 */ diff --git a/src/util/virhook.c b/src/util/virhook.c index 097afba936..508c26832a 100644 --- a/src/util/virhook.c +++ b/src/util/virhook.c @@ -1,7 +1,7 @@ /* * 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 diff --git a/src/util/viriptables.c b/src/util/viriptables.c index 06a1356cf2..b0fe2b03a0 100644 --- a/src/util/viriptables.c +++ b/src/util/viriptables.c @@ -44,6 +44,7 @@ #include "vircommand.h" #include "viralloc.h" #include "virerror.h" +#include "virfile.h" #include "virlog.h" #include "virthread.h" #include "virstring.h" diff --git a/src/util/virkeyfile.c b/src/util/virkeyfile.c index d77e95d79c..1732d0c13c 100644 --- a/src/util/virkeyfile.c +++ b/src/util/virkeyfile.c @@ -1,7 +1,7 @@ /* * 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 @@ -28,6 +28,7 @@ #include "c-ctype.h" #include "virlog.h" #include "viralloc.h" +#include "virfile.h" #include "virutil.h" #include "virhash.h" #include "virkeyfile.h" diff --git a/src/util/virnetdevveth.c b/src/util/virnetdevveth.c index 5daf21ef7b..e73c31d709 100644 --- a/src/util/virnetdevveth.c +++ b/src/util/virnetdevveth.c @@ -1,5 +1,5 @@ /* - * 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 @@ -30,6 +30,7 @@ #include "virlog.h" #include "vircommand.h" #include "virerror.h" +#include "virfile.h" #include "virstring.h" #include "virutil.h" diff --git a/src/util/virsysinfo.c b/src/util/virsysinfo.c index 2efe634957..686cd49ef0 100644 --- a/src/util/virsysinfo.c +++ b/src/util/virsysinfo.c @@ -1,7 +1,7 @@ /* * 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 @@ -35,6 +35,7 @@ #include "virlog.h" #include "viralloc.h" #include "vircommand.h" +#include "virfile.h" #include "virstring.h" #define VIR_FROM_THIS VIR_FROM_SYSINFO diff --git a/src/util/virusb.c b/src/util/virusb.c index 3192634f4e..27ba9c773b 100644 --- a/src/util/virusb.c +++ b/src/util/virusb.c @@ -1,7 +1,7 @@ /* * 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 @@ -38,6 +38,7 @@ #include "viralloc.h" #include "virutil.h" #include "virerror.h" +#include "virfile.h" #include "virstring.h" #define USB_SYSFS "/sys/bus/usb" diff --git a/src/util/virutil.c b/src/util/virutil.c index 6a4bc14902..43814dfd2f 100644 --- a/src/util/virutil.c +++ b/src/util/virutil.c @@ -29,7 +29,6 @@ #include #include #include -#include #include #include #include @@ -37,14 +36,11 @@ #include #include #include -#include #if HAVE_MMAP # include #endif #include -#include #include -#include #include #if HAVE_LIBDEVMAPPER_H @@ -63,9 +59,6 @@ # include # include #endif -#if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R -# include -#endif #ifdef WIN32 # ifdef HAVE_WINSOCK2_H @@ -76,1482 +69,152 @@ #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. */ @@ -2777,50 +1440,6 @@ virSetUIDGIDWithCaps(uid_t uid, gid_t gid, #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) { @@ -2847,34 +1466,6 @@ 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) diff --git a/src/util/virutil.h b/src/util/virutil.h index 4c0a9a5c4a..00ee0c3d0b 100644 --- a/src/util/virutil.h +++ b/src/util/virutil.h @@ -38,12 +38,6 @@ # 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; @@ -56,104 +50,6 @@ int virSetUIDGID(uid_t uid, gid_t gid); 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; @@ -224,13 +120,6 @@ int virGetUserID(const char *name, 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); diff --git a/src/vbox/vbox_XPCOMCGlue.c b/src/vbox/vbox_XPCOMCGlue.c index e0b606fe4a..121382c1c2 100644 --- a/src/vbox/vbox_XPCOMCGlue.c +++ b/src/vbox/vbox_XPCOMCGlue.c @@ -41,6 +41,7 @@ #include "virutil.h" #include "virlog.h" #include "virerror.h" +#include "virfile.h" #include "virstring.h" #define VIR_FROM_THIS VIR_FROM_VBOX diff --git a/src/vmx/vmx.c b/src/vmx/vmx.c index 6eac1fa01e..5464d1330e 100644 --- a/src/vmx/vmx.c +++ b/src/vmx/vmx.c @@ -26,6 +26,7 @@ #include "internal.h" #include "virerror.h" +#include "virfile.h" #include "virconf.h" #include "viralloc.h" #include "virlog.h" diff --git a/src/xen/xm_internal.c b/src/xen/xm_internal.c index 66bd28942f..66a6c4cde5 100644 --- a/src/xen/xm_internal.c +++ b/src/xen/xm_internal.c @@ -36,6 +36,7 @@ #include #include "virerror.h" +#include "virfile.h" #include "datatypes.h" #include "xm_internal.h" #include "xen_driver.h" diff --git a/tests/eventtest.c b/tests/eventtest.c index 16a693c2f6..700ea08f5f 100644 --- a/tests/eventtest.c +++ b/tests/eventtest.c @@ -1,7 +1,7 @@ /* * 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 @@ -28,6 +28,7 @@ #include "testutils.h" #include "internal.h" +#include "virfile.h" #include "virthread.h" #include "virlog.h" #include "virutil.h" diff --git a/tests/libvirtdconftest.c b/tests/libvirtdconftest.c index df8d3d8dbc..e1dd82d15c 100644 --- a/tests/libvirtdconftest.c +++ b/tests/libvirtdconftest.c @@ -1,5 +1,5 @@ /* - * 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 @@ -27,6 +27,7 @@ #include "virutil.h" #include "c-ctype.h" #include "virerror.h" +#include "virfile.h" #include "virlog.h" #include "virconf.h" #include "virstring.h" diff --git a/tests/securityselinuxtest.c b/tests/securityselinuxtest.c index 0d56769cc8..f276e6d746 100644 --- a/tests/securityselinuxtest.c +++ b/tests/securityselinuxtest.c @@ -33,6 +33,7 @@ #include "viralloc.h" #include "virlog.h" #include "virerror.h" +#include "virfile.h" #include "security/security_manager.h" #include "virstring.h" diff --git a/tests/virlockspacetest.c b/tests/virlockspacetest.c index 76783960ad..b659f61d50 100644 --- a/tests/virlockspacetest.c +++ b/tests/virlockspacetest.c @@ -1,5 +1,5 @@ /* - * 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 @@ -28,6 +28,7 @@ #include "virutil.h" #include "virerror.h" #include "viralloc.h" +#include "virfile.h" #include "virlog.h" #include "virlockspace.h" diff --git a/tests/virportallocatortest.c b/tests/virportallocatortest.c index a52850687d..1a0cdfafd7 100644 --- a/tests/virportallocatortest.c +++ b/tests/virportallocatortest.c @@ -53,6 +53,7 @@ int bind(int sockfd ATTRIBUTE_UNUSED, # include "virutil.h" # include "virerror.h" # include "viralloc.h" +# include "virfile.h" # include "virlog.h" # include "virportallocator.h" # include "virstring.h" diff --git a/tests/virstoragetest.c b/tests/virstoragetest.c index 0016052e33..fef4b372fc 100644 --- a/tests/virstoragetest.c +++ b/tests/virstoragetest.c @@ -25,6 +25,7 @@ #include "testutils.h" #include "vircommand.h" #include "virerror.h" +#include "virfile.h" #include "virlog.h" #include "virstoragefile.h" #include "virstring.h" diff --git a/tools/virsh-interface.c b/tools/virsh-interface.c index 706074faad..f75c572fd9 100644 --- a/tools/virsh-interface.c +++ b/tools/virsh-interface.c @@ -1,7 +1,7 @@ /* * 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 @@ -34,6 +34,7 @@ #include "internal.h" #include "virbuffer.h" #include "viralloc.h" +#include "virfile.h" #include "virutil.h" #include "virxml.h" #include "virstring.h" diff --git a/tools/virsh-network.c b/tools/virsh-network.c index 81267bcaa8..a80cbb5a7a 100644 --- a/tools/virsh-network.c +++ b/tools/virsh-network.c @@ -1,7 +1,7 @@ /* * 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 @@ -34,6 +34,7 @@ #include "internal.h" #include "virbuffer.h" #include "viralloc.h" +#include "virfile.h" #include "virxml.h" #include "conf/network_conf.h" diff --git a/tools/virsh-nodedev.c b/tools/virsh-nodedev.c index 8fa67fcc86..592fa4439a 100644 --- a/tools/virsh-nodedev.c +++ b/tools/virsh-nodedev.c @@ -34,6 +34,7 @@ #include "internal.h" #include "virbuffer.h" #include "viralloc.h" +#include "virfile.h" #include "virxml.h" #include "conf/node_device_conf.h" diff --git a/tools/virsh-nwfilter.c b/tools/virsh-nwfilter.c index 3b1e1e4641..5a360c2c70 100644 --- a/tools/virsh-nwfilter.c +++ b/tools/virsh-nwfilter.c @@ -1,7 +1,7 @@ /* * 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 @@ -34,6 +34,7 @@ #include "internal.h" #include "virbuffer.h" #include "viralloc.h" +#include "virfile.h" #include "virutil.h" #include "virxml.h" diff --git a/tools/virsh-pool.c b/tools/virsh-pool.c index f154366b39..c5944aea32 100644 --- a/tools/virsh-pool.c +++ b/tools/virsh-pool.c @@ -1,7 +1,7 @@ /* * 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 @@ -34,6 +34,7 @@ #include "internal.h" #include "virbuffer.h" #include "viralloc.h" +#include "virfile.h" #include "virxml.h" #include "conf/storage_conf.h" #include "virstring.h" diff --git a/tools/virsh-secret.c b/tools/virsh-secret.c index 72015229df..044629dcf3 100644 --- a/tools/virsh-secret.c +++ b/tools/virsh-secret.c @@ -1,7 +1,7 @@ /* * 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 @@ -35,6 +35,7 @@ #include "base64.h" #include "virbuffer.h" #include "viralloc.h" +#include "virfile.h" #include "virutil.h" #include "virxml.h" diff --git a/tools/virsh-snapshot.c b/tools/virsh-snapshot.c index 4f5fa156da..7e7577276c 100644 --- a/tools/virsh-snapshot.c +++ b/tools/virsh-snapshot.c @@ -1,7 +1,7 @@ /* * 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 @@ -36,6 +36,7 @@ #include "internal.h" #include "virbuffer.h" #include "viralloc.h" +#include "virfile.h" #include "virsh-domain.h" #include "virxml.h" #include "conf/snapshot_conf.h" -- GitLab