2011-02-22 12:05:20 +00:00
|
|
|
/*
|
|
|
|
* iohelper.c: Helper program to perform I/O operations on files
|
|
|
|
*
|
2012-08-03 23:15:00 +00:00
|
|
|
* Copyright (C) 2011-2012 Red Hat, Inc.
|
2011-02-22 12:05:20 +00:00
|
|
|
*
|
|
|
|
* 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/>.
|
2011-02-22 12:05:20 +00:00
|
|
|
*
|
|
|
|
* Current support
|
|
|
|
* - Read existing file
|
|
|
|
* - Write existing file
|
|
|
|
* - Create & write new file
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
2012-12-13 17:44:57 +00:00
|
|
|
#include "virutil.h"
|
2012-12-13 15:49:48 +00:00
|
|
|
#include "virthread.h"
|
2011-07-19 18:32:58 +00:00
|
|
|
#include "virfile.h"
|
2012-12-12 18:06:53 +00:00
|
|
|
#include "viralloc.h"
|
2012-12-13 18:21:53 +00:00
|
|
|
#include "virerror.h"
|
2012-01-25 15:17:46 +00:00
|
|
|
#include "virrandom.h"
|
2013-04-03 10:36:23 +00:00
|
|
|
#include "virstring.h"
|
2016-04-12 22:29:52 +00:00
|
|
|
#include "virgettext.h"
|
2011-02-22 12:05:20 +00:00
|
|
|
|
|
|
|
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
|
|
|
|
2011-07-12 15:17:52 +00:00
|
|
|
static int
|
2017-09-07 07:44:13 +00:00
|
|
|
runIO(const char *path, int fd, int oflags)
|
2011-07-12 15:17:52 +00:00
|
|
|
{
|
2019-10-15 13:16:31 +00:00
|
|
|
g_autofree void *base = NULL; /* Location to be freed */
|
2011-07-12 14:35:05 +00:00
|
|
|
char *buf = NULL; /* Aligned location within base */
|
2011-07-12 15:17:52 +00:00
|
|
|
size_t buflen = 1024*1024;
|
2011-07-12 14:35:05 +00:00
|
|
|
intptr_t alignMask = 64*1024 - 1;
|
2011-07-12 15:17:52 +00:00
|
|
|
int ret = -1;
|
|
|
|
int fdin, fdout;
|
|
|
|
const char *fdinname, *fdoutname;
|
|
|
|
unsigned long long total = 0;
|
2011-07-12 14:35:05 +00:00
|
|
|
bool direct = O_DIRECT && ((oflags & O_DIRECT) != 0);
|
|
|
|
off_t end = 0;
|
2011-07-12 15:17:52 +00:00
|
|
|
|
2011-07-12 14:35:05 +00:00
|
|
|
#if HAVE_POSIX_MEMALIGN
|
|
|
|
if (posix_memalign(&base, alignMask + 1, buflen)) {
|
2011-02-22 12:05:20 +00:00
|
|
|
virReportOOMError();
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2011-07-12 14:35:05 +00:00
|
|
|
buf = base;
|
|
|
|
#else
|
2013-07-04 10:17:18 +00:00
|
|
|
if (VIR_ALLOC_N(buf, buflen + alignMask) < 0)
|
2011-07-12 14:35:05 +00:00
|
|
|
goto cleanup;
|
|
|
|
base = buf;
|
2011-11-24 15:36:50 +00:00
|
|
|
buf = (char *) (((intptr_t) base + alignMask) & ~alignMask);
|
2011-07-12 14:35:05 +00:00
|
|
|
#endif
|
2011-02-22 12:05:20 +00:00
|
|
|
|
2011-07-07 17:57:43 +00:00
|
|
|
switch (oflags & O_ACCMODE) {
|
2011-02-22 12:05:20 +00:00
|
|
|
case O_RDONLY:
|
|
|
|
fdin = fd;
|
|
|
|
fdinname = path;
|
|
|
|
fdout = STDOUT_FILENO;
|
|
|
|
fdoutname = "stdout";
|
2011-07-12 14:35:05 +00:00
|
|
|
/* To make the implementation simpler, we give up on any
|
|
|
|
* attempt to use O_DIRECT in a non-trivial manner. */
|
2017-09-07 07:44:13 +00:00
|
|
|
if (direct && ((end = lseek(fd, 0, SEEK_CUR)) != 0)) {
|
2011-07-12 14:35:05 +00:00
|
|
|
virReportSystemError(end < 0 ? errno : EINVAL, "%s",
|
|
|
|
_("O_DIRECT read needs entire seekable file"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2011-02-22 12:05:20 +00:00
|
|
|
break;
|
|
|
|
case O_WRONLY:
|
|
|
|
fdin = STDIN_FILENO;
|
|
|
|
fdinname = "stdin";
|
|
|
|
fdout = fd;
|
|
|
|
fdoutname = path;
|
2011-07-12 14:35:05 +00:00
|
|
|
/* To make the implementation simpler, we give up on any
|
|
|
|
* attempt to use O_DIRECT in a non-trivial manner. */
|
|
|
|
if (direct && (end = lseek(fd, 0, SEEK_END)) != 0) {
|
|
|
|
virReportSystemError(end < 0 ? errno : EINVAL, "%s",
|
|
|
|
_("O_DIRECT write needs empty seekable file"));
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2011-02-22 12:05:20 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case O_RDWR:
|
|
|
|
default:
|
|
|
|
virReportSystemError(EINVAL,
|
|
|
|
_("Unable to process file with flags %d"),
|
2011-07-07 17:57:43 +00:00
|
|
|
(oflags & O_ACCMODE));
|
2011-02-22 12:05:20 +00:00
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
ssize_t got;
|
|
|
|
|
2017-09-28 07:06:47 +00:00
|
|
|
/* If we read with O_DIRECT from file we can't use saferead as
|
|
|
|
* it can lead to unaligned read after reading last bytes.
|
|
|
|
* If we write with O_DIRECT use should use saferead so that
|
|
|
|
* writes will be aligned.
|
|
|
|
* In other cases using saferead reduces number of syscalls.
|
|
|
|
*/
|
|
|
|
if (fdin == fd && direct) {
|
|
|
|
if ((got = read(fdin, buf, buflen)) < 0 &&
|
|
|
|
errno == EINTR)
|
2017-09-20 15:25:56 +00:00
|
|
|
continue;
|
2017-09-28 07:06:47 +00:00
|
|
|
} else {
|
|
|
|
got = saferead(fdin, buf, buflen);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (got < 0) {
|
2011-02-22 12:05:20 +00:00
|
|
|
virReportSystemError(errno, _("Unable to read %s"), fdinname);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if (got == 0)
|
2017-09-07 07:44:14 +00:00
|
|
|
break;
|
2011-02-22 12:05:20 +00:00
|
|
|
|
|
|
|
total += got;
|
2017-09-07 07:44:14 +00:00
|
|
|
|
|
|
|
/* handle last write size align in direct case */
|
|
|
|
if (got < buflen && direct && fdout == fd) {
|
2017-09-07 07:44:15 +00:00
|
|
|
ssize_t aligned_got = (got + alignMask) & ~alignMask;
|
2017-09-07 07:44:14 +00:00
|
|
|
|
2017-09-07 07:44:15 +00:00
|
|
|
memset(buf + got, 0, aligned_got - got);
|
|
|
|
|
|
|
|
if (safewrite(fdout, buf, aligned_got) < 0) {
|
2017-09-07 07:44:14 +00:00
|
|
|
virReportSystemError(errno, _("Unable to write %s"), fdoutname);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ftruncate(fd, total) < 0) {
|
|
|
|
virReportSystemError(errno, _("Unable to truncate %s"), fdoutname);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2011-07-12 14:35:05 +00:00
|
|
|
}
|
2017-09-07 07:44:14 +00:00
|
|
|
|
2011-02-22 12:05:20 +00:00
|
|
|
if (safewrite(fdout, buf, got) < 0) {
|
|
|
|
virReportSystemError(errno, _("Unable to write %s"), fdoutname);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-30 18:15:48 +00:00
|
|
|
/* Ensure all data is written */
|
|
|
|
if (fdatasync(fdout) < 0) {
|
2012-11-05 14:42:53 +00:00
|
|
|
if (errno != EINVAL && errno != EROFS) {
|
|
|
|
/* fdatasync() may fail on some special FDs, e.g. pipes */
|
|
|
|
virReportSystemError(errno, _("unable to fsync %s"), fdoutname);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
2012-10-30 18:15:48 +00:00
|
|
|
}
|
|
|
|
|
2011-02-22 12:05:20 +00:00
|
|
|
ret = 0;
|
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
cleanup:
|
2011-02-22 12:05:20 +00:00
|
|
|
if (VIR_CLOSE(fd) < 0 &&
|
|
|
|
ret == 0) {
|
|
|
|
virReportSystemError(errno, _("Unable to close %s"), path);
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-07-12 15:17:52 +00:00
|
|
|
static const char *program_name;
|
|
|
|
|
2019-10-14 12:07:39 +00:00
|
|
|
G_GNUC_NORETURN static void
|
2011-07-12 15:17:52 +00:00
|
|
|
usage(int status)
|
|
|
|
{
|
|
|
|
if (status) {
|
|
|
|
fprintf(stderr, _("%s: try --help for more details"), program_name);
|
|
|
|
} else {
|
2019-02-19 15:03:54 +00:00
|
|
|
printf(_("Usage: %s FILENAME FD"), program_name);
|
2011-07-12 15:17:52 +00:00
|
|
|
}
|
|
|
|
exit(status);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
main(int argc, char **argv)
|
2011-02-22 12:05:20 +00:00
|
|
|
{
|
|
|
|
const char *path;
|
2011-07-12 15:17:52 +00:00
|
|
|
int oflags = -1;
|
|
|
|
int fd = -1;
|
|
|
|
|
|
|
|
program_name = argv[0];
|
2011-02-22 12:05:20 +00:00
|
|
|
|
2016-04-12 22:29:52 +00:00
|
|
|
if (virGettextInitialize() < 0 ||
|
2012-08-03 23:15:00 +00:00
|
|
|
virErrorInitialize() < 0) {
|
2019-02-19 15:03:54 +00:00
|
|
|
fprintf(stderr, _("%s: initialization failed"), program_name);
|
2011-02-22 12:05:20 +00:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
path = argv[1];
|
|
|
|
|
2011-07-12 15:17:52 +00:00
|
|
|
if (argc > 1 && STREQ(argv[1], "--help"))
|
|
|
|
usage(EXIT_SUCCESS);
|
2017-09-07 07:44:13 +00:00
|
|
|
if (argc == 3) { /* FILENAME FD */
|
|
|
|
if (virStrToLong_i(argv[2], NULL, 10, &fd) < 0) {
|
2011-07-12 15:17:52 +00:00
|
|
|
fprintf(stderr, _("%s: malformed fd %s"),
|
|
|
|
program_name, argv[3]);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
#ifdef F_GETFL
|
|
|
|
oflags = fcntl(fd, F_GETFL);
|
|
|
|
#else
|
|
|
|
/* Stupid mingw. */
|
|
|
|
if (fd == STDIN_FILENO)
|
|
|
|
oflags = O_RDONLY;
|
|
|
|
else if (fd == STDOUT_FILENO)
|
|
|
|
oflags = O_WRONLY;
|
|
|
|
#endif
|
|
|
|
if (oflags < 0) {
|
|
|
|
fprintf(stderr, _("%s: unable to determine access mode of fd %d"),
|
|
|
|
program_name, fd);
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
} else { /* unknown argc pattern */
|
|
|
|
usage(EXIT_FAILURE);
|
2011-02-22 12:05:20 +00:00
|
|
|
}
|
|
|
|
|
2017-09-07 07:44:13 +00:00
|
|
|
if (fd < 0 || runIO(path, fd, oflags) < 0)
|
2011-02-22 12:05:20 +00:00
|
|
|
goto error;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2014-03-25 06:53:22 +00:00
|
|
|
error:
|
2019-02-05 14:54:55 +00:00
|
|
|
fprintf(stderr, _("%s: failure with %s: %s"),
|
2016-05-19 19:10:19 +00:00
|
|
|
program_name, path, virGetLastErrorMessage());
|
2011-02-22 12:05:20 +00:00
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|