virfile: Introduce virCloseRange()

Linux gained new close_range() syscall (in v5.9) that allows
closing a range of FDs in a single syscall. Ideally, we would use
it to close FDs when spawning a process (e.g. via virCommand
module).

Glibc has close_range() wrapper over the syscall, which falls
back to iterative closing of all FDs inside the range if running
under older kernel. We don't wane that as in that case we might
just close opened FDs (see Linux version of
virCommandMassClose()). And musl doesn't have close_range() at
all. Therefore, call syscall directly.

Now, mass close of FDs happens in a fork()-ed off child. While it
could detect whether the kernel does support close_range(), it
has no way of passing this info back to the parent and thus each
child would need to query it again and again.

Since this can't change while we are running we can cache the
information - hence virCloseRangeInit().

Signed-off-by: Michal Privoznik <mprivozn@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
Reviewed-by: Kristina Hanicova <khanicov@redhat.com>
This commit is contained in:
Michal Privoznik 2023-08-22 08:49:10 +02:00
parent 6a0b645537
commit d69237caa3
3 changed files with 96 additions and 0 deletions

View File

@ -2275,6 +2275,9 @@ saferead;
safewrite;
safezero;
virBuildPathInternal;
virCloseRange;
virCloseRangeInit;
virCloseRangeIsSupported;
virDirClose;
virDirCreate;
virDirIsEmpty;

View File

@ -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:
*

View File

@ -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)