2010-10-19 14:23:51 +00:00
|
|
|
/*
|
2011-07-19 18:32:58 +00:00
|
|
|
* virfile.c: safer file handling
|
2010-10-19 14:23:51 +00:00
|
|
|
*
|
2012-07-09 21:21:10 +00:00
|
|
|
* Copyright (C) 2010-2012 Red Hat, Inc.
|
2010-10-19 14:23:51 +00:00
|
|
|
* Copyright (C) 2010 IBM Corporation
|
|
|
|
* Copyright (C) 2010 Stefan Berger
|
|
|
|
* Copyright (C) 2010 Eric Blake
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
2012-09-20 22:30:55 +00:00
|
|
|
* License along with this library. If not, see
|
2012-07-21 10:06:23 +00:00
|
|
|
* <http://www.gnu.org/licenses/>.
|
2010-10-19 14:23:51 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
2011-07-11 21:26:33 +00:00
|
|
|
#include "internal.h"
|
2010-10-19 14:23:51 +00:00
|
|
|
|
2011-07-11 21:26:33 +00:00
|
|
|
#include "virfile.h"
|
|
|
|
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <sys/stat.h>
|
2010-10-19 14:23:51 +00:00
|
|
|
#include <unistd.h>
|
2012-07-03 14:06:27 +00:00
|
|
|
#include <dirent.h>
|
|
|
|
|
|
|
|
#ifdef __linux__
|
|
|
|
# include <linux/loop.h>
|
|
|
|
# include <sys/ioctl.h>
|
|
|
|
#endif
|
2010-10-19 14:23:51 +00:00
|
|
|
|
2011-07-11 21:26:33 +00:00
|
|
|
#include "command.h"
|
|
|
|
#include "configmake.h"
|
|
|
|
#include "memory.h"
|
|
|
|
#include "virterror_internal.h"
|
2012-05-30 13:54:18 +00:00
|
|
|
#include "logging.h"
|
2011-07-11 21:26:33 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
2010-10-19 14:23:51 +00:00
|
|
|
|
2012-06-07 13:16:50 +00:00
|
|
|
int virFileClose(int *fdptr, virFileCloseFlags flags)
|
2010-10-19 14:23:51 +00:00
|
|
|
{
|
2011-08-16 17:54:15 +00:00
|
|
|
int saved_errno = 0;
|
2010-10-19 14:23:51 +00:00
|
|
|
int rc = 0;
|
|
|
|
|
2012-05-30 13:54:18 +00:00
|
|
|
if (*fdptr < 0)
|
|
|
|
return 0;
|
|
|
|
|
2012-06-07 13:16:50 +00:00
|
|
|
if (flags & VIR_FILE_CLOSE_PRESERVE_ERRNO)
|
2012-05-30 13:54:18 +00:00
|
|
|
saved_errno = errno;
|
|
|
|
|
|
|
|
rc = close(*fdptr);
|
2012-06-07 13:16:50 +00:00
|
|
|
|
|
|
|
if (!(flags & VIR_FILE_CLOSE_DONT_LOG)) {
|
|
|
|
if (rc < 0) {
|
|
|
|
if (errno == EBADF) {
|
|
|
|
if (!(flags & VIR_FILE_CLOSE_IGNORE_EBADF))
|
|
|
|
VIR_WARN("Tried to close invalid fd %d", *fdptr);
|
|
|
|
} else {
|
|
|
|
char ebuf[1024] ATTRIBUTE_UNUSED;
|
|
|
|
VIR_DEBUG("Failed to close fd %d: %s",
|
|
|
|
*fdptr, virStrerror(errno, ebuf, sizeof(ebuf)));
|
|
|
|
}
|
2012-05-30 13:54:18 +00:00
|
|
|
} else {
|
2012-06-07 13:16:50 +00:00
|
|
|
VIR_DEBUG("Closed fd %d", *fdptr);
|
2012-05-30 13:54:18 +00:00
|
|
|
}
|
2010-10-19 14:23:51 +00:00
|
|
|
}
|
2012-05-30 13:54:18 +00:00
|
|
|
*fdptr = -1;
|
2012-06-07 13:16:50 +00:00
|
|
|
|
|
|
|
if (flags & VIR_FILE_CLOSE_PRESERVE_ERRNO)
|
2012-05-30 13:54:18 +00:00
|
|
|
errno = saved_errno;
|
2010-10-19 14:23:51 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
2010-11-17 02:13:29 +00:00
|
|
|
|
|
|
|
|
2011-07-19 18:32:58 +00:00
|
|
|
int virFileFclose(FILE **file, bool preserve_errno)
|
2010-11-17 02:13:29 +00:00
|
|
|
{
|
2011-08-16 17:54:15 +00:00
|
|
|
int saved_errno = 0;
|
2010-11-17 02:13:29 +00:00
|
|
|
int rc = 0;
|
|
|
|
|
|
|
|
if (*file) {
|
|
|
|
if (preserve_errno)
|
|
|
|
saved_errno = errno;
|
|
|
|
rc = fclose(*file);
|
|
|
|
*file = NULL;
|
|
|
|
if (preserve_errno)
|
|
|
|
errno = saved_errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-19 18:32:58 +00:00
|
|
|
FILE *virFileFdopen(int *fdptr, const char *mode)
|
2010-11-17 02:13:29 +00:00
|
|
|
{
|
|
|
|
FILE *file = NULL;
|
|
|
|
|
|
|
|
if (*fdptr >= 0) {
|
|
|
|
file = fdopen(*fdptr, mode);
|
|
|
|
if (file)
|
|
|
|
*fdptr = -1;
|
|
|
|
} else {
|
|
|
|
errno = EBADF;
|
|
|
|
}
|
|
|
|
|
|
|
|
return file;
|
|
|
|
}
|
2011-07-11 21:26:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virFileDirectFdFlag:
|
|
|
|
*
|
|
|
|
* Returns 0 if the kernel can avoid file system cache pollution
|
|
|
|
* without any additional flags, O_DIRECT if the original fd must be
|
|
|
|
* opened in direct mode, or -1 if there is no support for bypassing
|
|
|
|
* the file system cache.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
virFileDirectFdFlag(void)
|
|
|
|
{
|
|
|
|
/* XXX For now, Linux posix_fadvise is not powerful enough to
|
|
|
|
* avoid O_DIRECT. */
|
|
|
|
return O_DIRECT ? O_DIRECT : -1;
|
|
|
|
}
|
|
|
|
|
2012-02-06 13:40:48 +00:00
|
|
|
/* Opaque type for managing a wrapper around a fd. For now,
|
|
|
|
* read-write is not supported, just a single direction. */
|
|
|
|
struct _virFileWrapperFd {
|
|
|
|
virCommandPtr cmd; /* Child iohelper process to do the I/O. */
|
2012-10-22 15:07:49 +00:00
|
|
|
int err_fd; /* FD to read stderr of @cmd */
|
|
|
|
char *err_msg; /* stderr of @cmd */
|
|
|
|
size_t err_msg_len; /* strlen of err_msg so we don't
|
|
|
|
have to compute it every time */
|
|
|
|
int err_watch; /* ID of watch in the event loop */
|
2012-02-06 13:40:48 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
#ifndef WIN32
|
2012-10-22 15:07:49 +00:00
|
|
|
/**
|
|
|
|
* virFileWrapperFdReadStdErr:
|
|
|
|
* @watch: watch ID
|
|
|
|
* @fd: the read end of pipe to iohelper's stderr
|
|
|
|
* @events: an OR-ed set of events which occurred on @fd
|
|
|
|
* @opaque: virFileWrapperFdPtr
|
|
|
|
*
|
|
|
|
* This is a callback to our eventloop which will read iohelper's
|
|
|
|
* stderr, reallocate @opaque->err_msg and copy data.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
virFileWrapperFdReadStdErr(int watch ATTRIBUTE_UNUSED,
|
|
|
|
int fd, int events, void *opaque)
|
|
|
|
{
|
|
|
|
virFileWrapperFdPtr wfd = (virFileWrapperFdPtr) opaque;
|
|
|
|
char ebuf[1024];
|
|
|
|
ssize_t nread;
|
|
|
|
|
|
|
|
if (events & VIR_EVENT_HANDLE_READABLE) {
|
|
|
|
while ((nread = saferead(fd, ebuf, sizeof(ebuf)))) {
|
|
|
|
if (nread < 0) {
|
|
|
|
if (errno != EAGAIN)
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("unable to read iohelper's stderr"));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N(wfd->err_msg, wfd->err_msg_len + nread + 1) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memcpy(wfd->err_msg + wfd->err_msg_len, ebuf, nread);
|
|
|
|
wfd->err_msg_len += nread;
|
|
|
|
wfd->err_msg[wfd->err_msg_len] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (events & VIR_EVENT_HANDLE_HANGUP) {
|
|
|
|
virEventRemoveHandle(watch);
|
|
|
|
wfd->err_watch = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-11 21:26:33 +00:00
|
|
|
/**
|
2012-02-06 13:40:48 +00:00
|
|
|
* virFileWrapperFdNew:
|
2011-07-11 21:26:33 +00:00
|
|
|
* @fd: pointer to fd to wrap
|
|
|
|
* @name: name of fd, for diagnostics
|
2012-02-06 13:40:48 +00:00
|
|
|
* @flags: bitwise-OR of virFileWrapperFdFlags
|
|
|
|
*
|
|
|
|
* Update @fd so that it meets parameters requested by @flags.
|
|
|
|
*
|
|
|
|
* If VIR_FILE_WRAPPER_BYPASS_CACHE bit is set in @flags, @fd will be updated
|
|
|
|
* in a way that all I/O to that file will bypass the system cache. The
|
|
|
|
* original fd must have been created with virFileDirectFdFlag() among the
|
|
|
|
* flags to open().
|
2011-07-11 21:26:33 +00:00
|
|
|
*
|
2012-02-06 13:40:48 +00:00
|
|
|
* If VIR_FILE_WRAPPER_NON_BLOCKING bit is set in @flags, @fd will be updated
|
|
|
|
* to ensure it properly supports non-blocking I/O, i.e., it will report
|
|
|
|
* EAGAIN.
|
|
|
|
*
|
|
|
|
* This must be called after open() and optional fchown() or fchmod(), but
|
|
|
|
* before any seek or I/O, and only on seekable fd. The file must be O_RDONLY
|
|
|
|
* (to read the entire existing file) or O_WRONLY (to write to an empty file).
|
|
|
|
* In some cases, @fd is changed to a non-seekable pipe; in this case, the
|
|
|
|
* caller must not do anything further with the original fd.
|
2011-07-11 21:26:33 +00:00
|
|
|
*
|
|
|
|
* On success, the new wrapper object is returned, which must be later
|
2012-02-06 13:40:48 +00:00
|
|
|
* freed with virFileWrapperFdFree(). On failure, @fd is unchanged, an
|
2011-07-11 21:26:33 +00:00
|
|
|
* error message is output, and NULL is returned.
|
|
|
|
*/
|
2012-02-06 13:40:48 +00:00
|
|
|
virFileWrapperFdPtr
|
|
|
|
virFileWrapperFdNew(int *fd, const char *name, unsigned int flags)
|
2011-07-11 21:26:33 +00:00
|
|
|
{
|
2012-02-06 13:40:48 +00:00
|
|
|
virFileWrapperFdPtr ret = NULL;
|
2011-07-11 21:26:33 +00:00
|
|
|
bool output = false;
|
|
|
|
int pipefd[2] = { -1, -1 };
|
|
|
|
int mode = -1;
|
|
|
|
|
2012-02-06 13:40:48 +00:00
|
|
|
if (!flags) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("invalid use with no flags"));
|
2012-02-06 13:40:48 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* XXX support posix_fadvise rather than O_DIRECT, if the kernel support
|
|
|
|
* for that is decent enough. In that case, we will also need to
|
|
|
|
* explicitly support VIR_FILE_WRAPPER_NON_BLOCKING since
|
|
|
|
* VIR_FILE_WRAPPER_BYPASS_CACHE alone will no longer require spawning
|
|
|
|
* iohelper.
|
|
|
|
*/
|
2011-07-11 21:26:33 +00:00
|
|
|
|
2012-02-06 13:40:48 +00:00
|
|
|
if ((flags & VIR_FILE_WRAPPER_BYPASS_CACHE) && !O_DIRECT) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("O_DIRECT unsupported on this platform"));
|
2011-07-11 21:26:33 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC(ret) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2012-10-22 15:07:49 +00:00
|
|
|
ret->err_watch = -1;
|
|
|
|
|
2011-07-11 21:26:33 +00:00
|
|
|
mode = fcntl(*fd, F_GETFL);
|
|
|
|
|
|
|
|
if (mode < 0) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, _("invalid fd %d for %s"),
|
|
|
|
*fd, name);
|
2011-07-11 21:26:33 +00:00
|
|
|
goto error;
|
|
|
|
} else if ((mode & O_ACCMODE) == O_WRONLY) {
|
|
|
|
output = true;
|
|
|
|
} else if ((mode & O_ACCMODE) != O_RDONLY) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, _("unexpected mode %x for %s"),
|
|
|
|
mode & O_ACCMODE, name);
|
2011-07-11 21:26:33 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pipe2(pipefd, O_CLOEXEC) < 0) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR,
|
|
|
|
_("unable to create pipe for %s"), name);
|
2011-07-11 21:26:33 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret->cmd = virCommandNewArgList(LIBEXECDIR "/libvirt_iohelper",
|
|
|
|
name, "0", NULL);
|
|
|
|
if (output) {
|
|
|
|
virCommandSetInputFD(ret->cmd, pipefd[0]);
|
|
|
|
virCommandSetOutputFD(ret->cmd, fd);
|
|
|
|
virCommandAddArg(ret->cmd, "1");
|
|
|
|
} else {
|
|
|
|
virCommandSetInputFD(ret->cmd, *fd);
|
|
|
|
virCommandSetOutputFD(ret->cmd, &pipefd[1]);
|
|
|
|
virCommandAddArg(ret->cmd, "0");
|
|
|
|
}
|
|
|
|
|
2012-10-22 15:07:49 +00:00
|
|
|
/* In order to catch iohelper stderr, we must:
|
|
|
|
* - pass a FD to virCommand (-1 to auto-allocate one)
|
|
|
|
* - change iohelper's env so virLog functions print to stderr
|
|
|
|
*/
|
|
|
|
ret->err_fd = -1;
|
|
|
|
virCommandSetErrorFD(ret->cmd, &ret->err_fd);
|
|
|
|
virCommandAddEnvPair(ret->cmd, "LIBVIRT_LOG_OUTPUTS", "1:stderr");
|
|
|
|
|
2011-07-11 21:26:33 +00:00
|
|
|
if (virCommandRunAsync(ret->cmd, NULL) < 0)
|
|
|
|
goto error;
|
|
|
|
|
2012-10-22 15:07:49 +00:00
|
|
|
/* deliberately don't use virCommandNonblockingFDs here as it is all or
|
|
|
|
* nothing. And we want iohelper's stdin and stdout to block (default).
|
|
|
|
* However, stderr is read within event loop and therefore it must be
|
|
|
|
* nonblocking.*/
|
|
|
|
if (virSetNonBlock(ret->err_fd) < 0) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Failed to set non-blocking "
|
|
|
|
"file descriptor flag"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((ret->err_watch = virEventAddHandle(ret->err_fd,
|
|
|
|
VIR_EVENT_HANDLE_READABLE,
|
|
|
|
virFileWrapperFdReadStdErr,
|
|
|
|
ret, NULL)) < 0) {
|
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("unable to register iohelper's "
|
|
|
|
"stderr FD in the eventloop"));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2011-07-11 21:26:33 +00:00
|
|
|
if (VIR_CLOSE(pipefd[!output]) < 0) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", _("unable to close pipe"));
|
2011-07-11 21:26:33 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_FORCE_CLOSE(*fd);
|
|
|
|
*fd = pipefd[output];
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
error:
|
|
|
|
VIR_FORCE_CLOSE(pipefd[0]);
|
|
|
|
VIR_FORCE_CLOSE(pipefd[1]);
|
2012-02-06 13:40:48 +00:00
|
|
|
virFileWrapperFdFree(ret);
|
2011-07-11 21:26:33 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2012-02-06 13:40:48 +00:00
|
|
|
#else
|
|
|
|
virFileWrapperFdPtr
|
|
|
|
virFileWrapperFdNew(int *fd ATTRIBUTE_UNUSED,
|
|
|
|
const char *name ATTRIBUTE_UNUSED,
|
|
|
|
unsigned int fdflags ATTRIBUTE_UNUSED)
|
|
|
|
{
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
2012-02-06 13:40:48 +00:00
|
|
|
_("virFileWrapperFd unsupported on this platform"));
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
#endif
|
2011-07-11 21:26:33 +00:00
|
|
|
|
|
|
|
/**
|
2012-02-06 13:40:48 +00:00
|
|
|
* virFileWrapperFdClose:
|
|
|
|
* @wfd: fd wrapper, or NULL
|
2011-07-11 21:26:33 +00:00
|
|
|
*
|
2012-02-06 13:40:48 +00:00
|
|
|
* If @wfd is valid, then ensure that I/O has completed, which may
|
2011-07-11 21:26:33 +00:00
|
|
|
* include reaping a child process. Return 0 if all data for the
|
|
|
|
* wrapped fd is complete, or -1 on failure with an error emitted.
|
2012-02-06 13:40:48 +00:00
|
|
|
* This function intentionally returns 0 when @wfd is NULL, so that
|
|
|
|
* callers can conditionally create a virFileWrapperFd wrapper but
|
2011-07-11 21:26:33 +00:00
|
|
|
* unconditionally call the cleanup code. To avoid deadlock, only
|
2012-02-06 13:40:48 +00:00
|
|
|
* call this after closing the fd resulting from virFileWrapperFdNew().
|
2011-07-11 21:26:33 +00:00
|
|
|
*/
|
|
|
|
int
|
2012-02-06 13:40:48 +00:00
|
|
|
virFileWrapperFdClose(virFileWrapperFdPtr wfd)
|
2011-07-11 21:26:33 +00:00
|
|
|
{
|
2012-02-06 13:40:48 +00:00
|
|
|
if (!wfd)
|
2011-07-11 21:26:33 +00:00
|
|
|
return 0;
|
|
|
|
|
2012-02-06 13:40:48 +00:00
|
|
|
return virCommandWait(wfd->cmd, NULL);
|
2011-07-11 21:26:33 +00:00
|
|
|
}
|
|
|
|
|
2012-10-22 15:07:49 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* virFileWrapperFdCatchError:
|
|
|
|
* @wfd: fd wrapper, or NULL
|
|
|
|
*
|
|
|
|
* If iohelper reported any error VIR_WARN() about it.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
virFileWrapperFdCatchError(virFileWrapperFdPtr wfd)
|
|
|
|
{
|
|
|
|
if (wfd->err_msg)
|
|
|
|
VIR_WARN("iohelper reports: %s", wfd->err_msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-07-11 21:26:33 +00:00
|
|
|
/**
|
2012-02-06 13:40:48 +00:00
|
|
|
* virFileWrapperFdFree:
|
|
|
|
* @wfd: fd wrapper, or NULL
|
2011-07-11 21:26:33 +00:00
|
|
|
*
|
2012-02-06 13:40:48 +00:00
|
|
|
* Free all remaining resources associated with @wfd. If
|
|
|
|
* virFileWrapperFdClose() was not previously called, then this may
|
2011-07-11 21:26:33 +00:00
|
|
|
* discard some previous I/O. To avoid deadlock, only call this after
|
2012-02-06 13:40:48 +00:00
|
|
|
* closing the fd resulting from virFileWrapperFdNew().
|
2011-07-11 21:26:33 +00:00
|
|
|
*/
|
|
|
|
void
|
2012-02-06 13:40:48 +00:00
|
|
|
virFileWrapperFdFree(virFileWrapperFdPtr wfd)
|
2011-07-11 21:26:33 +00:00
|
|
|
{
|
2012-02-06 13:40:48 +00:00
|
|
|
if (!wfd)
|
2011-07-11 21:26:33 +00:00
|
|
|
return;
|
|
|
|
|
2012-10-22 15:07:49 +00:00
|
|
|
VIR_FORCE_CLOSE(wfd->err_fd);
|
|
|
|
if (wfd->err_watch != -1)
|
|
|
|
virEventRemoveHandle(wfd->err_watch);
|
|
|
|
VIR_FREE(wfd->err_msg);
|
|
|
|
|
2012-02-06 13:40:48 +00:00
|
|
|
virCommandFree(wfd->cmd);
|
|
|
|
VIR_FREE(wfd);
|
2011-07-11 21:26:33 +00:00
|
|
|
}
|
2011-07-06 16:06:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
#ifndef WIN32
|
|
|
|
/**
|
|
|
|
* virFileLock:
|
|
|
|
* @fd: file descriptor to acquire the lock on
|
|
|
|
* @shared: type of lock to acquire
|
|
|
|
* @start: byte offset to start lock
|
|
|
|
* @len: length of lock (0 to acquire entire remaining file from @start)
|
|
|
|
*
|
|
|
|
* Attempt to acquire a lock on the file @fd. If @shared
|
|
|
|
* is true, then a shared lock will be acquired,
|
|
|
|
* otherwise an exclusive lock will be acquired. If
|
|
|
|
* the lock cannot be acquired, an error will be
|
|
|
|
* returned. This will not wait to acquire the lock if
|
|
|
|
* another process already holds it.
|
|
|
|
*
|
|
|
|
* The lock will be released when @fd is closed. The lock
|
|
|
|
* will also be released if *any* other open file descriptor
|
|
|
|
* pointing to the same underlying file is closed. As such
|
|
|
|
* this function should not be relied on in multi-threaded
|
|
|
|
* apps where other threads can be opening/closing arbitrary
|
|
|
|
* files.
|
|
|
|
*
|
|
|
|
* Returns 0 on success, or -errno otherwise
|
|
|
|
*/
|
|
|
|
int virFileLock(int fd, bool shared, off_t start, off_t len)
|
|
|
|
{
|
|
|
|
struct flock fl = {
|
|
|
|
.l_type = shared ? F_RDLCK : F_WRLCK,
|
|
|
|
.l_whence = SEEK_SET,
|
|
|
|
.l_start = start,
|
|
|
|
.l_len = len,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (fcntl(fd, F_SETLK, &fl) < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* virFileUnlock:
|
|
|
|
* @fd: file descriptor to release the lock on
|
|
|
|
* @start: byte offset to start unlock
|
|
|
|
* @len: length of lock (0 to release entire remaining file from @start)
|
|
|
|
*
|
|
|
|
* Release a lock previously acquired with virFileUnlock().
|
|
|
|
* NB the lock will also be released if any open file descriptor
|
|
|
|
* pointing to the same file as @fd is closed
|
|
|
|
*
|
|
|
|
* Returns 0 on succcess, or -errno on error
|
|
|
|
*/
|
|
|
|
int virFileUnlock(int fd, off_t start, off_t len)
|
|
|
|
{
|
|
|
|
struct flock fl = {
|
|
|
|
.l_type = F_UNLCK,
|
|
|
|
.l_whence = SEEK_SET,
|
|
|
|
.l_start = start,
|
|
|
|
.l_len = len,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (fcntl(fd, F_SETLK, &fl) < 0)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
int virFileLock(int fd ATTRIBUTE_UNUSED,
|
|
|
|
bool shared ATTRIBUTE_UNUSED,
|
|
|
|
off_t start ATTRIBUTE_UNUSED,
|
|
|
|
off_t len ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
int virFileUnlock(int fd ATTRIBUTE_UNUSED,
|
|
|
|
off_t start ATTRIBUTE_UNUSED,
|
|
|
|
off_t len ATTRIBUTE_UNUSED)
|
|
|
|
{
|
|
|
|
return -ENOSYS;
|
|
|
|
}
|
|
|
|
#endif
|
2011-10-13 10:17:12 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
virFileRewrite(const char *path,
|
|
|
|
mode_t mode,
|
|
|
|
virFileRewriteFunc rewrite,
|
|
|
|
void *opaque)
|
|
|
|
{
|
|
|
|
char *newfile = NULL;
|
|
|
|
int fd = -1;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if (virAsprintf(&newfile, "%s.new", path) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((fd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC, mode)) < 0) {
|
|
|
|
virReportSystemError(errno, _("cannot create file '%s'"),
|
|
|
|
newfile);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rewrite(fd, opaque) < 0) {
|
|
|
|
virReportSystemError(errno, _("cannot write data to file '%s'"),
|
|
|
|
newfile);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fsync(fd) < 0) {
|
|
|
|
virReportSystemError(errno, _("cannot sync file '%s'"),
|
|
|
|
newfile);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_CLOSE(fd) < 0) {
|
|
|
|
virReportSystemError(errno, _("cannot save file '%s'"),
|
|
|
|
newfile);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rename(newfile, path) < 0) {
|
|
|
|
virReportSystemError(errno, _("cannot rename file '%s' as '%s'"),
|
|
|
|
newfile, path);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
if (newfile) {
|
|
|
|
unlink(newfile);
|
|
|
|
VIR_FREE(newfile);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
2012-01-11 09:58:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
int virFileTouch(const char *path, mode_t mode)
|
|
|
|
{
|
|
|
|
int fd = -1;
|
|
|
|
|
|
|
|
if ((fd = open(path, O_WRONLY | O_CREAT, mode)) < 0) {
|
|
|
|
virReportSystemError(errno, _("cannot create file '%s'"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_CLOSE(fd) < 0) {
|
|
|
|
virReportSystemError(errno, _("cannot save file '%s'"),
|
|
|
|
path);
|
|
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-04-27 13:50:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
#define MODE_BITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
|
|
|
|
|
|
|
|
int virFileUpdatePerm(const char *path,
|
|
|
|
mode_t mode_remove,
|
|
|
|
mode_t mode_add)
|
|
|
|
{
|
|
|
|
struct stat sb;
|
|
|
|
mode_t mode;
|
|
|
|
|
|
|
|
if (mode_remove & ~MODE_BITS || mode_add & ~MODE_BITS) {
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INVALID_ARG, "%s", _("invalid mode"));
|
2012-04-27 13:50:22 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stat(path, &sb) < 0) {
|
|
|
|
virReportSystemError(errno, _("cannot stat '%s'"), path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
mode = sb.st_mode & MODE_BITS;
|
|
|
|
|
|
|
|
if ((mode & mode_remove) == 0 && (mode & mode_add) == mode_add)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
mode &= MODE_BITS ^ mode_remove;
|
|
|
|
mode |= mode_add;
|
|
|
|
|
|
|
|
if (chmod(path, mode) < 0) {
|
|
|
|
virReportSystemError(errno, _("cannot change permission of '%s'"),
|
|
|
|
path);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-07-03 14:06:27 +00:00
|
|
|
|
|
|
|
|
2012-08-21 16:26:18 +00:00
|
|
|
#if defined(__linux__) && HAVE_DECL_LO_FLAGS_AUTOCLEAR
|
2012-07-03 14:06:27 +00:00
|
|
|
static int virFileLoopDeviceOpen(char **dev_name)
|
|
|
|
{
|
|
|
|
int fd = -1;
|
|
|
|
DIR *dh = NULL;
|
|
|
|
struct dirent *de;
|
|
|
|
char *looppath;
|
|
|
|
struct loop_info64 lo;
|
|
|
|
|
|
|
|
VIR_DEBUG("Looking for loop devices in /dev");
|
|
|
|
|
|
|
|
if (!(dh = opendir("/dev"))) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Unable to read /dev"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((de = readdir(dh)) != NULL) {
|
|
|
|
if (!STRPREFIX(de->d_name, "loop"))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (virAsprintf(&looppath, "/dev/%s", de->d_name) < 0) {
|
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_DEBUG("Checking up on device %s", looppath);
|
|
|
|
if ((fd = open(looppath, O_RDWR)) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to open %s"), looppath);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ioctl(fd, LOOP_GET_STATUS64, &lo) < 0) {
|
|
|
|
/* Got a free device, return the fd */
|
|
|
|
if (errno == ENXIO)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to get loop status on %s"),
|
|
|
|
looppath);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Oh well, try the next device */
|
|
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
VIR_FREE(looppath);
|
|
|
|
}
|
|
|
|
|
2012-07-18 10:26:24 +00:00
|
|
|
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
|
|
|
|
_("Unable to find a free loop device in /dev"));
|
2012-07-03 14:06:27 +00:00
|
|
|
|
|
|
|
cleanup:
|
|
|
|
if (fd != -1) {
|
|
|
|
VIR_DEBUG("Got free loop device %s %d", looppath, fd);
|
|
|
|
*dev_name = looppath;
|
|
|
|
} else {
|
|
|
|
VIR_DEBUG("No free loop devices available");
|
|
|
|
VIR_FREE(looppath);
|
|
|
|
}
|
|
|
|
if (dh)
|
|
|
|
closedir(dh);
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int virFileLoopDeviceAssociate(const char *file,
|
|
|
|
char **dev)
|
|
|
|
{
|
|
|
|
int lofd = -1;
|
|
|
|
int fsfd = -1;
|
|
|
|
struct loop_info64 lo;
|
|
|
|
char *loname = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
|
|
|
|
if ((lofd = virFileLoopDeviceOpen(&loname)) < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
memset(&lo, 0, sizeof(lo));
|
|
|
|
lo.lo_flags = LO_FLAGS_AUTOCLEAR;
|
|
|
|
|
|
|
|
if ((fsfd = open(file, O_RDWR)) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to open %s"), file);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ioctl(lofd, LOOP_SET_FD, fsfd) < 0) {
|
|
|
|
virReportSystemError(errno,
|
|
|
|
_("Unable to attach %s to loop device"),
|
|
|
|
file);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ioctl(lofd, LOOP_SET_STATUS64, &lo) < 0) {
|
|
|
|
virReportSystemError(errno, "%s",
|
|
|
|
_("Unable to mark loop device as autoclear"));
|
|
|
|
|
|
|
|
if (ioctl(lofd, LOOP_CLR_FD, 0) < 0)
|
|
|
|
VIR_WARN("Unable to detach %s from loop device", file);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
VIR_DEBUG("Attached loop device %s %d to %s", file, lofd, loname);
|
|
|
|
*dev = loname;
|
|
|
|
loname = NULL;
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
VIR_FREE(loname);
|
|
|
|
VIR_FORCE_CLOSE(fsfd);
|
|
|
|
if (ret == -1)
|
|
|
|
VIR_FORCE_CLOSE(lofd);
|
|
|
|
return lofd;
|
|
|
|
}
|
|
|
|
|
|
|
|
#else /* __linux__ */
|
|
|
|
|
|
|
|
int virFileLoopDeviceAssociate(const char *file,
|
2012-07-11 13:35:43 +00:00
|
|
|
char **dev ATTRIBUTE_UNUSED)
|
2012-07-03 14:06:27 +00:00
|
|
|
{
|
|
|
|
virReportSystemError(ENOSYS,
|
|
|
|
_("Unable to associate file %s with loop device"),
|
|
|
|
file);
|
2012-07-09 21:21:10 +00:00
|
|
|
*dev = NULL;
|
|
|
|
return -1;
|
2012-07-03 14:06:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* __linux__ */
|