mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-12-23 14:15:28 +00:00
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:
parent
42dc978c28
commit
49d7a3a756
@ -41,181 +41,6 @@
|
|||||||
|
|
||||||
#define VIR_FROM_THIS VIR_FROM_STORAGE
|
#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;
|
static const char *program_name;
|
||||||
|
|
||||||
G_GNUC_NORETURN static void
|
G_GNUC_NORETURN static void
|
||||||
|
@ -175,6 +175,7 @@ keycode_dep = declare_dependency(
|
|||||||
|
|
||||||
io_helper_sources = [
|
io_helper_sources = [
|
||||||
'iohelper.c',
|
'iohelper.c',
|
||||||
|
'virfile.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
virt_util_lib = static_library(
|
virt_util_lib = static_library(
|
||||||
@ -213,6 +214,10 @@ if conf.has('WITH_LIBVIRTD')
|
|||||||
files(io_helper_sources),
|
files(io_helper_sources),
|
||||||
dtrace_gen_headers,
|
dtrace_gen_headers,
|
||||||
],
|
],
|
||||||
|
'deps': [
|
||||||
|
acl_dep,
|
||||||
|
libutil_dep,
|
||||||
|
],
|
||||||
}
|
}
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -4577,3 +4577,189 @@ virFileSetCOW(const char *path,
|
|||||||
return 0;
|
return 0;
|
||||||
#endif /* ! __linux__ */
|
#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 */
|
||||||
|
@ -383,3 +383,5 @@ int virFileDataSync(int fd);
|
|||||||
|
|
||||||
int virFileSetCOW(const char *path,
|
int virFileSetCOW(const char *path,
|
||||||
virTristateBool state);
|
virTristateBool state);
|
||||||
|
|
||||||
|
off_t runIO(const char *path, int fd, int oflags);
|
||||||
|
Loading…
Reference in New Issue
Block a user