iohelper: move runIO function to virfile.c

where it can be reused by other helpers.
No changes other than the move.

Note that this makes iohelper now dependent on -lutil and -lacl.

Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
Signed-off-by: Claudio Fontana <cfontana@suse.de>
This commit is contained in:
Claudio Fontana 2022-05-06 15:10:47 +02:00 committed by Daniel P. Berrangé
parent 42dc978c28
commit 49d7a3a756
4 changed files with 193 additions and 175 deletions

View File

@ -41,181 +41,6 @@
#define VIR_FROM_THIS VIR_FROM_STORAGE
#ifndef O_DIRECT
# define O_DIRECT 0
#endif
struct runIOParams {
bool isBlockDev;
bool isDirect;
bool isWrite;
int fdin;
const char *fdinname;
int fdout;
const char *fdoutname;
};
/**
* runIOCopy: execute the IO copy based on the passed parameters
* @p: the IO parameters
*
* Execute the copy based on the passed parameters.
*
* Returns: size transfered, or < 0 on error.
*/
static off_t
runIOCopy(const struct runIOParams p)
{
g_autofree void *base = NULL; /* Location to be freed */
char *buf = NULL; /* Aligned location within base */
size_t buflen = 1024*1024;
intptr_t alignMask = 64*1024 - 1;
off_t total = 0;
#if WITH_POSIX_MEMALIGN
if (posix_memalign(&base, alignMask + 1, buflen))
abort();
buf = base;
#else
buf = g_new0(char, buflen + alignMask);
base = buf;
buf = (char *) (((intptr_t) base + alignMask) & ~alignMask);
#endif
while (1) {
ssize_t got;
/* 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 (!p.isWrite && p.isDirect) {
if ((got = read(p.fdin, buf, buflen)) < 0 &&
errno == EINTR)
continue;
} else {
got = saferead(p.fdin, buf, buflen);
}
if (got < 0) {
virReportSystemError(errno, _("Unable to read %s"), p.fdinname);
return -2;
}
if (got == 0)
break;
total += got;
/* handle last write size align in direct case */
if (got < buflen && p.isDirect && p.isWrite) {
ssize_t aligned_got = (got + alignMask) & ~alignMask;
memset(buf + got, 0, aligned_got - got);
if (safewrite(p.fdout, buf, aligned_got) < 0) {
virReportSystemError(errno, _("Unable to write %s"), p.fdoutname);
return -3;
}
if (!p.isBlockDev && ftruncate(p.fdout, total) < 0) {
virReportSystemError(errno, _("Unable to truncate %s"), p.fdoutname);
return -4;
}
break;
}
if (safewrite(p.fdout, buf, got) < 0) {
virReportSystemError(errno, _("Unable to write %s"), p.fdoutname);
return -3;
}
}
return total;
}
static int
runIO(const char *path, int fd, int oflags)
{
int ret = -1;
off_t total = 0;
struct stat sb;
struct runIOParams p;
if (fstat(fd, &sb) < 0) {
virReportSystemError(errno,
_("Unable to access file descriptor %d path %s"),
fd, path);
goto cleanup;
}
p.isBlockDev = S_ISBLK(sb.st_mode);
p.isDirect = O_DIRECT && (oflags & O_DIRECT);
switch (oflags & O_ACCMODE) {
case O_RDONLY:
p.isWrite = false;
p.fdin = fd;
p.fdinname = path;
p.fdout = STDOUT_FILENO;
p.fdoutname = "stdout";
break;
case O_WRONLY:
p.isWrite = true;
p.fdin = STDIN_FILENO;
p.fdinname = "stdin";
p.fdout = fd;
p.fdoutname = path;
break;
case O_RDWR:
default:
virReportSystemError(EINVAL,
_("Unable to process file with flags %d"),
(oflags & O_ACCMODE));
goto cleanup;
}
/* To make the implementation simpler, we give up on any
* attempt to use O_DIRECT in a non-trivial manner. */
if (!p.isBlockDev && p.isDirect) {
off_t off;
if (p.isWrite) {
if ((off = lseek(fd, 0, SEEK_END)) != 0) {
virReportSystemError(off < 0 ? errno : EINVAL, "%s",
_("O_DIRECT write needs empty seekable file"));
goto cleanup;
}
} else if ((off = lseek(fd, 0, SEEK_CUR)) != 0) {
virReportSystemError(off < 0 ? errno : EINVAL, "%s",
_("O_DIRECT read needs entire seekable file"));
goto cleanup;
}
}
total = runIOCopy(p);
if (total < 0)
goto cleanup;
/* Ensure all data is written */
if (virFileDataSync(p.fdout) < 0) {
if (errno != EINVAL && errno != EROFS) {
/* fdatasync() may fail on some special FDs, e.g. pipes */
virReportSystemError(errno, _("unable to fsync %s"), p.fdoutname);
goto cleanup;
}
}
ret = 0;
cleanup:
if (VIR_CLOSE(fd) < 0 &&
ret == 0) {
virReportSystemError(errno, _("Unable to close %s"), path);
ret = -1;
}
return ret;
}
static const char *program_name;
G_GNUC_NORETURN static void

View File

@ -175,6 +175,7 @@ keycode_dep = declare_dependency(
io_helper_sources = [
'iohelper.c',
'virfile.c',
]
virt_util_lib = static_library(
@ -213,6 +214,10 @@ if conf.has('WITH_LIBVIRTD')
files(io_helper_sources),
dtrace_gen_headers,
],
'deps': [
acl_dep,
libutil_dep,
],
}
endif

View File

@ -4577,3 +4577,189 @@ virFileSetCOW(const char *path,
return 0;
#endif /* ! __linux__ */
}
#ifndef WIN32
struct runIOParams {
bool isBlockDev;
bool isDirect;
bool isWrite;
int fdin;
const char *fdinname;
int fdout;
const char *fdoutname;
};
/**
* runIOCopy: execute the IO copy based on the passed parameters
* @p: the IO parameters
*
* Execute the copy based on the passed parameters.
*
* Returns: size transfered, or < 0 on error.
*/
static off_t
runIOCopy(const struct runIOParams p)
{
g_autofree void *base = NULL; /* Location to be freed */
char *buf = NULL; /* Aligned location within base */
size_t buflen = 1024*1024;
intptr_t alignMask = 64*1024 - 1;
off_t total = 0;
# if WITH_POSIX_MEMALIGN
if (posix_memalign(&base, alignMask + 1, buflen))
abort();
buf = base;
# else
buf = g_new0(char, buflen + alignMask);
base = buf;
buf = (char *) (((intptr_t) base + alignMask) & ~alignMask);
# endif
while (1) {
ssize_t got;
/* 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 (!p.isWrite && p.isDirect) {
if ((got = read(p.fdin, buf, buflen)) < 0 &&
errno == EINTR)
continue;
} else {
got = saferead(p.fdin, buf, buflen);
}
if (got < 0) {
virReportSystemError(errno, _("Unable to read %s"), p.fdinname);
return -2;
}
if (got == 0)
break;
total += got;
/* handle last write size align in direct case */
if (got < buflen && p.isDirect && p.isWrite) {
ssize_t aligned_got = (got + alignMask) & ~alignMask;
memset(buf + got, 0, aligned_got - got);
if (safewrite(p.fdout, buf, aligned_got) < 0) {
virReportSystemError(errno, _("Unable to write %s"), p.fdoutname);
return -3;
}
if (!p.isBlockDev && ftruncate(p.fdout, total) < 0) {
virReportSystemError(errno, _("Unable to truncate %s"), p.fdoutname);
return -4;
}
break;
}
if (safewrite(p.fdout, buf, got) < 0) {
virReportSystemError(errno, _("Unable to write %s"), p.fdoutname);
return -3;
}
}
return total;
}
off_t
runIO(const char *path, int fd, int oflags)
{
int ret = -1;
off_t total = 0;
struct stat sb;
struct runIOParams p;
if (fstat(fd, &sb) < 0) {
virReportSystemError(errno,
_("Unable to access file descriptor %d path %s"),
fd, path);
goto cleanup;
}
p.isBlockDev = S_ISBLK(sb.st_mode);
p.isDirect = O_DIRECT && (oflags & O_DIRECT);
switch (oflags & O_ACCMODE) {
case O_RDONLY:
p.isWrite = false;
p.fdin = fd;
p.fdinname = path;
p.fdout = STDOUT_FILENO;
p.fdoutname = "stdout";
break;
case O_WRONLY:
p.isWrite = true;
p.fdin = STDIN_FILENO;
p.fdinname = "stdin";
p.fdout = fd;
p.fdoutname = path;
break;
case O_RDWR:
default:
virReportSystemError(EINVAL,
_("Unable to process file with flags %d"),
(oflags & O_ACCMODE));
goto cleanup;
}
/* To make the implementation simpler, we give up on any
* attempt to use O_DIRECT in a non-trivial manner. */
if (!p.isBlockDev && p.isDirect) {
off_t off;
if (p.isWrite) {
if ((off = lseek(fd, 0, SEEK_END)) != 0) {
virReportSystemError(off < 0 ? errno : EINVAL, "%s",
_("O_DIRECT write needs empty seekable file"));
goto cleanup;
}
} else if ((off = lseek(fd, 0, SEEK_CUR)) != 0) {
virReportSystemError(off < 0 ? errno : EINVAL, "%s",
_("O_DIRECT read needs entire seekable file"));
goto cleanup;
}
}
total = runIOCopy(p);
if (total < 0)
goto cleanup;
/* Ensure all data is written */
if (virFileDataSync(p.fdout) < 0) {
if (errno != EINVAL && errno != EROFS) {
/* fdatasync() may fail on some special FDs, e.g. pipes */
virReportSystemError(errno, _("unable to fsync %s"), p.fdoutname);
goto cleanup;
}
}
ret = 0;
cleanup:
if (VIR_CLOSE(fd) < 0 &&
ret == 0) {
virReportSystemError(errno, _("Unable to close %s"), path);
ret = -1;
}
return ret;
}
#else /* WIN32 */
off_t
runIO(const char *path G_GNUC_UNUSED,
int fd G_GNUC_UNUSED,
int oflags G_GNUC_UNUSED)
{
virReportError(VIR_ERR_INTERNAL_ERROR, "%s",
_("runIO unsupported on this platform"));
return -1;
}
#endif /* WIN32 */

View File

@ -383,3 +383,5 @@ int virFileDataSync(int fd);
int virFileSetCOW(const char *path,
virTristateBool state);
off_t runIO(const char *path, int fd, int oflags);