Print a warning when opening a file O_DIRECT on tmpfs fails with EINVAL. This saves users a lot of time trying to figure out the EINVAL error.
Reported-by: Deepak C Shetty <deepa...@linux.vnet.ibm.com> Suggested-by: Eric Blake <ebl...@redhat.com> Suggested-by: Daniel P. Berrange <berra...@redhat.com> Signed-off-by: Stefan Hajnoczi <stefa...@redhat.com> --- util/osdep.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/util/osdep.c b/util/osdep.c index 685c8ae..cbee2b7 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -148,6 +148,23 @@ static int qemu_parse_fdset(const char *param) #endif /* + * Open a file with O_CLOEXEC semantics + */ +static int open_cloexec(const char *name, int flags, int mode) +{ + int ret; +#ifdef O_CLOEXEC + ret = open(name, flags | O_CLOEXEC, mode); +#else + ret = open(name, flags, mode); + if (ret >= 0) { + qemu_set_cloexec(ret); + } +#endif + return ret; +} + +/* * Opens a file with FD_CLOEXEC set */ int qemu_open(const char *name, int flags, ...) @@ -198,14 +215,45 @@ int qemu_open(const char *name, int flags, ...) va_end(ap); } -#ifdef O_CLOEXEC - ret = open(name, flags | O_CLOEXEC, mode); -#else - ret = open(name, flags, mode); - if (ret >= 0) { - qemu_set_cloexec(ret); + /* Some file systems do not support O_DIRECT and fail with EINVAL. Confirm + * the problem by trying again without O_DIRECT and printing a warning. + * + * Sounds easy enough but we need to be careful with O_CREAT since this + * function should not create a file when an error is returned. + */ + if (flags & (O_CREAT | O_DIRECT) == O_CREAT | O_DIRECT) { + ret = open_cloexec(name, flags | O_EXCL, mode); + + if (ret >= 0) { + return ret; + } + + if (errno == EINVAL) { + error_report("file system may not support O_DIRECT"); + errno = EINVAL; /* in case it was clobbered */ + return ret; + } else if (errno == EEXIST && (flags & O_EXCL) == 0) { + /* We know the file existed, drop O_CREAT so the following open + * attempts do not create a file if O_DIRECT produces EINVAL. Note + * there is a race condition here if the file is deleted while we + * perform our open calls. + */ + flags &= O_CREAT; + } else { + return ret; + } + } + + ret = open_cloexec(name, flags, mode); + + if (ret == -1 && errno == EINVAL && (flags & O_DIRECT)) { + int fd = open_cloexec(name, flags & ~O_DIRECT, mode); + if (fd >= 0) { + close(fd); + error_report("file system does not support O_DIRECT"); + } + errno = EINVAL; /* in case it was clobbered */ } -#endif return ret; } -- 1.8.3.1