提交 bfe7721d 编写于 作者: L Laine Stump

util: move virFile* functions from virutil.c to virfile.c

These all existed before virfile.c was created, and for some reason
weren't moved.

This is mostly straightfoward, although the syntax rule prohibiting
write() had to be changed to have an exception for virfile.c instead
of virutil.c.

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