diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 1cf40ba863..418e049227 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -2275,6 +2275,9 @@ saferead; safewrite; safezero; virBuildPathInternal; +virCloseRange; +virCloseRangeInit; +virCloseRangeIsSupported; virDirClose; virDirCreate; virDirIsEmpty; diff --git a/src/util/virfile.c b/src/util/virfile.c index fe456596ae..e84af93c8d 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -87,6 +87,7 @@ #include "virlog.h" #include "virprocess.h" #include "virstring.h" +#include "virthread.h" #include "virutil.h" #include "virsocket.h" @@ -109,6 +110,9 @@ VIR_LOG_INIT("util.file"); # define O_DIRECT 0 #endif +static virOnceControl virCloseRangeOnce = VIR_ONCE_CONTROL_INITIALIZER; +static bool virCloseRangeSupported; + int virFileClose(int *fdptr, virFileCloseFlags flags) { int saved_errno = 0; @@ -176,6 +180,91 @@ FILE *virFileFdopen(int *fdptr, const char *mode) } +static int +virCloseRangeImpl(unsigned int first G_GNUC_UNUSED, + unsigned int last G_GNUC_UNUSED) +{ +#if defined(WITH_SYS_SYSCALL_H) && defined(__NR_close_range) + return syscall(__NR_close_range, first, last, 0); +#endif + + errno = ENOSYS; + return -1; +} + + +static void +virCloseRangeOnceInit(void) +{ + int fd[2] = {-1, -1}; + + if (virPipeQuiet(fd) < 0) + return; + + VIR_FORCE_CLOSE(fd[1]); + if (virCloseRangeImpl(fd[0], fd[0]) < 0) { + VIR_FORCE_CLOSE(fd[0]); + return; + } + + virCloseRangeSupported = true; +} + + +/** + * virCloseRange: + * + * Closes all open file descriptors from @first to @last (included). + * + * Returns: 0 on success, + * -1 on failure (with errno set). + */ +int +virCloseRange(unsigned int first, + unsigned int last) +{ + if (virCloseRangeInit() < 0) + return -1; + + if (!virCloseRangeSupported) { + errno = ENOSYS; + return -1; + } + + return virCloseRangeImpl(first, last); +} + + +/** + * virCloseRangeInit: + * + * Detects whether close_range() is available and cache the result. + */ +int +virCloseRangeInit(void) +{ + if (virOnce(&virCloseRangeOnce, virCloseRangeOnceInit) < 0) + return -1; + + return 0; +} + + +/** + * virCloseRangeIsSupported: + * + * Returns whether close_range() is supported or not. + */ +bool +virCloseRangeIsSupported(void) +{ + if (virCloseRangeInit() < 0) + return false; + + return virCloseRangeSupported; +} + + /** * virFileDirectFdFlag: * diff --git a/src/util/virfile.h b/src/util/virfile.h index 60bb1d64e7..be0b02fdf0 100644 --- a/src/util/virfile.h +++ b/src/util/virfile.h @@ -61,6 +61,10 @@ static inline void virForceCloseHelper(int *fd) ignore_value(virFileClose(fd, VIR_FILE_CLOSE_PRESERVE_ERRNO)); } +int virCloseRange(unsigned int from, unsigned int to); +int virCloseRangeInit(void); +bool virCloseRangeIsSupported(void); + /* For use on normal paths; caller must check return value, and failure sets errno per close. */ #define VIR_CLOSE(FD) virFileClose(&(FD), 0)