diff --git a/cfg.mk b/cfg.mk index 227c18b60196db8391c60353aa2075ebb4acc64f..0bf5bfc1b629b65cd70a8124c43a3593674a292d 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 6979fbffde336ac54773627ea80b182b85844337..b23e7f6cf2ec96a90d39e5a16a1356ef42898f9b 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 63974abcf6f7f640e55e870a90c4769894c9ba6c..3b4d73a126e542c3c061aac524c0fb809cade0c5 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 ab75110821914fa55066b5bcc2e353c9bbd6d736..2b3515e32c5cc1706b4343f38a5aabf1bf8fa4b6 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 bb70595e0b4a258f3b567019cac671e80222b3b2..2d7fe5b40f4b2b28648d0da6c5fe1ee954b0ce00 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 c596901747493d82e006eec946a3cbee705e1755..52586b898a3ecfd8e74fa36b808f8397d6dd68e4 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 7036c8ca02dcff8442ccc95fc948ad48e9f54cbb..4aeaed5a8fa471c94fc139a26f6611cb494d9742 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 74a52f0d7adb7f65d92313385a756900d4557a55..b7c4ec4ff3af50909358e921d17434d8f16c2d24 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 2e5523b0310c1717828fbd842df29cc96e6e0c76..8990609095ba49c2b2ae0cd52486b75c14ec0080 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 6a02fd6523981bafaa20f38deb244ad6bc76ddf4..3d55e82e058679fd71cb97879f4d7b207497f567 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 27750fc66bbba69c5f2df45105a6842387dd1212..189d9289aa8ab321b6cb3ba4bb0a21dafb51877c 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 7f5975dcffc58fe2154d37710ccf419f3875f32d..1a7ccb8732f23461f81a87225e0ba06a5b6ffc7e 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 cd214d886063f9fc11d80dd5f851262d6a798985..16cce0ea14750ef7bd188d5ce6714e4cfcbd320a 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 038487ad8acbd6507d0603e21e0623cc24c7815f..c04f1bdd3d53109462fa069de18231f989e49b5b 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 52bd57265151bf6141bac56e1d19d3a58001ca22..09c2d2c3029a4a3b4b584693486b77337e81e6b0 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 6bc6fed3ca88679438f6ddd790e61e80897b6395..3834ea88a29ef927bc7b1d9b994d796e56b521b1 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 25a0501cdf179b1700fc9d5117ce7cad127f1440..1491f27adacaed0c29707433c7386344f784c741 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 5f0dd2ba2a3553c0fc045bd8c307cb6b590b557e..bd353315f9cf358b38e9f33367902930a86068c6 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 097afba93675d424bd574c9583c17bb7ed16ea07..508c26832a66b1ba362dded54535c80a528d502f 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 06a1356cf21f07e364f099369c83725f56d575dd..b0fe2b03a0ca4706592d66d23a8428e189f9412c 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 d77e95d79cac83bb643f9df3e2e71bcfe33eae96..1732d0c13ceef34b416167c5afead351bbb18655 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 5daf21ef7b23c4add76ea42d077d4c96ba913acd..e73c31d7099378832fe4398a3fe29bfba56a7599 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 2efe634957c15febc9427b7e8cfb72082f4ca83c..686cd49ef008573bbce1ba2acaca2cefca31f9b0 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 3192634f4ef4fc49bdf6474cb6a92657a27ea6d1..27ba9c773b31e2b3efcfb8881b3f76eeeb358910 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 6a4bc14902571bd9ca00c804091bd07ab55d1e0c..43814dfd2fe9f9ac2c9c4dad70d957c94875e0cc 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 4c0a9a5c4a12bde9e4a72a3b394ba2b6a6b2c73d..00ee0c3d0bb11e30b02b21f49777a3c336f15f0d 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 e0b606fe4ac777c5f5ae3258b46cde4f1fc38f28..121382c1c28e6f1290f778c52613110228450be9 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 6eac1fa01e66b542934b5410e4eb3b9e6b7176a2..5464d1330efcc8c9ff81c02f173d06e3efe36488 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 66bd28942fda43850a69bd20152fc4dca2f42538..66a6c4cde53df6054aef7b146652cc61fbd202c8 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 16a693c2f656aa0317501e16a2e1c72878fe5510..700ea08f5fd5da8b1b1144cf7840927351d86970 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 df8d3d8dbcac6d7a469f2a8449cb9a43f7d5b8b6..e1dd82d15c1334c13243f064a05f728c319e596c 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 0d56769cc80f783ee88f0a319031d2ef09a939ee..f276e6d746ba9fa3a0784d96b25e307f736d8eec 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 76783960adcc53ff27ee686979d1755b2fba37e9..b659f61d5032210f337d8dd17751397126ef9fa8 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 a52850687d77cbbd10b2a942272cd711e4b13826..1a0cdfafd7dd1ceb342750d3e71728a5e808c86d 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 0016052e3385fbadb029e4c735ad362deaa380e2..fef4b372fc5b1ced5f6f90b018292ba8d5fe265b 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 706074faad0ccb3357488143cb7f9a1dd34ac716..f75c572fd9837a642dca85cbebf147ca46eede57 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 81267bcaa8f3d25484eb78095e1b28e4dbdaa068..a80cbb5a7a419e7ae17524e2bdee9742988fa975 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 8fa67fcc865cddbd4c9f432ca918864105a49c17..592fa4439a36f76de72aa4cb437f1932db7e7241 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 3b1e1e4641da2f76f65737605707dec26f64ae54..5a360c2c70348e13d629dcaf72ccdd90ed3178ec 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 f154366b396999e17927f50cd5fd05913693e0af..c5944aea327573d721a4183d4622f319bd0cbd4e 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 72015229dfeb1eaf5666790526ee65e1d3165fce..044629dcf331669d3b2f4c429777d85b3d1c71a9 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 4f5fa156da51a29cc2606e3388474b4e9cbc1e7f..7e7577276cf18255d60031a4ea0b604932dc174a 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"