libmount: Reuse the guessed root device

Inside a container, the file /proc/self/mountinfo may contain many lines
with /dev/root. It is also quite likely that /dev/root is not visible
inside the container. This may cause mnt_guess_system_root() to try to use
libblkid before giving up, through mnt_resolve_spec() and
mnt_resolve_tag(), which calls blkid_evaluate_tag(). The call to
blkid_evaluate_tag() may trigger a scan of all block devices, which is
expensive.

For this reason, it doesn't make any sense for kernel_fs_postparse()
to call mnt_guess_system_root() more than once for every call to
mnt_table_parse_stream. Instead, save the result from the first call and
reuse it for all subsequent calls to kernel_fs_postparse(), so that there
is at most one call to mnt_guess_system_root() for every call
to mnt_table_parse_stream().

[kzak@redhat.com: - use sysroot_ prefix for the cached variables
		  - simplify code logic in kernel_fs_postparse()
		  - add free() to parser_cleanup()]

Signed-off-by: Viktor Rosendahl (BMW) <viktor.rosendahl@gmail.com>
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
Viktor Rosendahl (BMW) 2022-10-24 11:36:03 +02:00 committed by Karel Zak
parent 36e824e61e
commit 4e8a8d3598

View file

@ -19,6 +19,8 @@
#include <limits.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include "fileutils.h"
@ -33,6 +35,8 @@ struct libmnt_parser {
char *buf; /* buffer (the current line content) */
size_t bufsiz; /* size of the buffer */
size_t line; /* current line */
int sysroot_rc; /* rc from mnt_guess_system_root() */
char *sysroot; /* guess from mmnt_guess_system_root() */
};
static void parser_cleanup(struct libmnt_parser *pa)
@ -40,6 +44,7 @@ static void parser_cleanup(struct libmnt_parser *pa)
if (!pa)
return;
free(pa->buf);
free(pa->sysroot);
memset(pa, 0, sizeof(*pa));
}
@ -652,9 +657,9 @@ done:
return tid;
}
static int kernel_fs_postparse(struct libmnt_table *tb,
struct libmnt_fs *fs, pid_t *tid,
const char *filename)
static int kernel_fs_postparse(struct libmnt_parser *pa,
struct libmnt_table *tb,
struct libmnt_fs *fs, pid_t *tid)
{
int rc = 0;
const char *src = mnt_fs_get_srcpath(fs);
@ -662,8 +667,8 @@ static int kernel_fs_postparse(struct libmnt_table *tb,
/* This is a filesystem description from /proc, so we're in some process
* namespace. Let's remember the process PID.
*/
if (filename && *tid == -1)
*tid = path_to_tid(filename);
if (pa->filename && *tid == -1)
*tid = path_to_tid(pa->filename);
fs->tid = *tid;
@ -671,13 +676,28 @@ static int kernel_fs_postparse(struct libmnt_table *tb,
* Convert obscure /dev/root to something more usable
*/
if (src && strcmp(src, "/dev/root") == 0) {
char *real = NULL;
rc = mnt_guess_system_root(fs->devno, tb->cache, &real);
/* We will only call mnt_guess_system_root() if it has not
* been called before. Inside a container, mountinfo can contain
* many lines with /dev/root.
*/
if (pa->sysroot_rc == 0 && pa->sysroot == NULL)
pa->sysroot_rc = mnt_guess_system_root(fs->devno,
tb->cache, &pa->sysroot);
rc = pa->sysroot_rc;
if (rc < 0)
return rc;
if (rc == 0 && real) {
/* This means that we already have run the mnt_guess_system_root()
* and that we want to reuse the result.
*/
if (rc == 0 && pa->sysroot != NULL) {
char *real = strdup(pa->sysroot);
if (!real)
return -ENOMEM;
DBG(TAB, ul_debugobj(tb, "canonical root FS: %s", real));
rc = __mnt_fs_set_source_ptr(fs, real);
@ -748,7 +768,7 @@ int mnt_table_parse_stream(struct libmnt_table *tb, FILE *f, const char *filenam
fs->flags |= flags;
if (rc == 0 && tb->fmt == MNT_FMT_MOUNTINFO) {
rc = kernel_fs_postparse(tb, fs, &tid, filename);
rc = kernel_fs_postparse(&pa, tb, fs, &tid);
if (rc)
mnt_table_remove_fs(tb, fs);
}