2013-10-23 13:44:40 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2013 Red Hat, Inc.
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library. If not, see
|
|
|
|
* <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
2018-04-30 14:04:39 +00:00
|
|
|
#if defined(__linux__) || defined(__FreeBSD__)
|
2016-05-13 10:32:00 +00:00
|
|
|
# include "virmock.h"
|
2013-10-23 13:44:40 +00:00
|
|
|
# include <unistd.h>
|
|
|
|
# include <fcntl.h>
|
|
|
|
# include <sys/stat.h>
|
|
|
|
# include <stdarg.h>
|
2013-12-24 18:07:27 +00:00
|
|
|
# include <dirent.h>
|
2013-10-23 13:44:40 +00:00
|
|
|
# include "viralloc.h"
|
|
|
|
# include "virstring.h"
|
|
|
|
# include "virfile.h"
|
2013-10-23 15:55:02 +00:00
|
|
|
# include "dirname.h"
|
2013-10-23 13:44:40 +00:00
|
|
|
|
2016-05-13 10:32:00 +00:00
|
|
|
static int (*real_access)(const char *path, int mode);
|
|
|
|
static int (*real_open)(const char *path, int flags, ...);
|
2019-08-20 10:30:08 +00:00
|
|
|
# ifdef __GLIBC__
|
2019-08-15 14:37:17 +00:00
|
|
|
static int (*real___open_2)(const char *path, int flags);
|
2019-08-20 10:30:08 +00:00
|
|
|
# endif /* ! __GLIBC__ */
|
2016-05-13 10:32:00 +00:00
|
|
|
static int (*real_close)(int fd);
|
|
|
|
static DIR * (*real_opendir)(const char *name);
|
2018-05-03 08:01:04 +00:00
|
|
|
static char *(*real_virFileCanonicalizePath)(const char *path);
|
2013-10-23 13:44:40 +00:00
|
|
|
|
|
|
|
/* Don't make static, since it causes problems with clang
|
|
|
|
* when passed as an arg to virAsprintf()
|
|
|
|
* vircgroupmock.c:462:22: error: static variable 'fakesysfsdir' is used in an inline function with external linkage [-Werror,-Wstatic-in-inline]
|
|
|
|
*/
|
2015-12-04 09:05:56 +00:00
|
|
|
char *fakerootdir;
|
2013-10-23 13:44:40 +00:00
|
|
|
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
/* To add a new mocked prefix in virpcimock:
|
|
|
|
* - add the prefix here as a define to make it easier to track what we
|
|
|
|
* are mocking;
|
|
|
|
* - add it to the 'pathPrefixIsMocked()' helper;
|
|
|
|
* - (optional) edit 'getrealpath()' if you need the resulting mocked
|
|
|
|
* path to be different than <fakerootdir>/path
|
|
|
|
*/
|
2015-12-04 13:51:08 +00:00
|
|
|
# define SYSFS_PCI_PREFIX "/sys/bus/pci/"
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
# define SYSFS_KERNEL_PREFIX "/sys/kernel/"
|
|
|
|
# define DEV_VFIO_PREFIX "/dev/vfio/"
|
2013-10-23 13:44:40 +00:00
|
|
|
|
2017-11-03 12:09:47 +00:00
|
|
|
# define STDERR(...) \
|
|
|
|
fprintf(stderr, "%s %zu: ", __FUNCTION__, (size_t) __LINE__); \
|
|
|
|
fprintf(stderr, __VA_ARGS__); \
|
|
|
|
fprintf(stderr, "\n"); \
|
|
|
|
|
|
|
|
# define ABORT(...) \
|
|
|
|
do { \
|
|
|
|
STDERR(__VA_ARGS__); \
|
|
|
|
abort(); \
|
2013-10-23 13:44:40 +00:00
|
|
|
} while (0)
|
|
|
|
|
2017-11-03 12:09:47 +00:00
|
|
|
# define ABORT_OOM() \
|
2013-10-23 13:44:40 +00:00
|
|
|
ABORT("Out of memory")
|
|
|
|
/*
|
|
|
|
* The plan:
|
|
|
|
*
|
|
|
|
* Mock some file handling functions. Redirect them into a stub tree passed via
|
2015-12-04 12:38:16 +00:00
|
|
|
* LIBVIRT_FAKE_ROOT_DIR env variable. All files and links within stub tree is
|
2013-10-23 15:55:02 +00:00
|
|
|
* created by us. There are some actions that we must take if some special
|
|
|
|
* files are written to. Here's the list of files we watch:
|
|
|
|
*
|
|
|
|
* /sys/bus/pci/drivers/<driver>/bind
|
|
|
|
* Check if driver supports the device and bind driver to it (create symlink
|
|
|
|
* called 'driver' pointing to the /sys/but/pci/drivers/<driver>).
|
|
|
|
* Data in format "DDDD:BB:DD.F" (Domain:Bus:Device.Function).
|
|
|
|
*
|
|
|
|
* /sys/bus/pci/drivers/<driver>/unbind
|
|
|
|
* Unbind driver from the device.
|
|
|
|
* Data in format "DDDD:BB:DD.F" (Domain:Bus:Device.Function).
|
|
|
|
*
|
2014-01-16 11:28:12 +00:00
|
|
|
* /sys/bus/pci/drivers_probe
|
|
|
|
* Probe for a driver that handles the specified device.
|
|
|
|
* Data in format "DDDD:BB:DD.F" (Domain:Bus:Device.Function).
|
|
|
|
*
|
2019-06-17 14:42:23 +00:00
|
|
|
* /sys/bus/pci/devices/<device>/driver_override
|
|
|
|
* Name of a driver that overrides preferred driver can be written
|
|
|
|
* here. The device will be attached to it on drivers_probe event.
|
|
|
|
* Writing an empty string (or "\n") clears the override.
|
|
|
|
*
|
2013-10-23 15:55:02 +00:00
|
|
|
* As a little hack, we are not mocking write to these files, but close()
|
|
|
|
* instead. The advantage is we don't need any self growing array to hold the
|
|
|
|
* partial writes and construct them back. We can let all the writes finish,
|
|
|
|
* and then just read the file content back.
|
2013-10-23 13:44:40 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Functions to model kernel behavior
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
struct pciDriver {
|
|
|
|
char *name;
|
|
|
|
int *vendor; /* List of vendor:device IDs the driver can handle */
|
|
|
|
int *device;
|
|
|
|
size_t len; /* @len is used for both @vendor and @device */
|
|
|
|
};
|
|
|
|
|
2019-08-29 19:18:58 +00:00
|
|
|
struct pciIommuGroup {
|
|
|
|
int iommu;
|
|
|
|
size_t nDevicesBoundToVFIO; /* Indicates the devices in the group */
|
|
|
|
};
|
|
|
|
|
2019-08-13 11:22:58 +00:00
|
|
|
struct pciDeviceAddress {
|
|
|
|
unsigned int domain;
|
|
|
|
unsigned int bus;
|
|
|
|
unsigned int device;
|
|
|
|
unsigned int function;
|
|
|
|
};
|
|
|
|
# define ADDR_STR_FMT "%04x:%02x:%02x.%d"
|
|
|
|
|
2013-10-23 13:44:40 +00:00
|
|
|
struct pciDevice {
|
2019-08-13 11:22:58 +00:00
|
|
|
struct pciDeviceAddress addr;
|
2013-10-23 13:44:40 +00:00
|
|
|
int vendor;
|
|
|
|
int device;
|
2019-03-12 08:08:00 +00:00
|
|
|
int klass;
|
2017-06-01 16:48:52 +00:00
|
|
|
int iommuGroup;
|
2019-08-29 19:18:59 +00:00
|
|
|
const char *physfn;
|
2013-10-23 15:55:02 +00:00
|
|
|
struct pciDriver *driver; /* Driver attached. NULL if attached to no driver */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct fdCallback {
|
|
|
|
int fd;
|
|
|
|
char *path;
|
2013-10-23 13:44:40 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct pciDevice **pciDevices = NULL;
|
2014-03-13 11:59:32 +00:00
|
|
|
size_t nPCIDevices = 0;
|
2013-10-23 13:44:40 +00:00
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
struct pciDriver **pciDrivers = NULL;
|
2014-03-13 11:59:32 +00:00
|
|
|
size_t nPCIDrivers = 0;
|
2013-10-23 15:55:02 +00:00
|
|
|
|
2019-08-29 19:18:58 +00:00
|
|
|
struct pciIommuGroup **pciIommuGroups = NULL;
|
|
|
|
size_t npciIommuGroups = 0;
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
struct fdCallback *callbacks = NULL;
|
|
|
|
size_t nCallbacks = 0;
|
|
|
|
|
2013-10-23 13:44:40 +00:00
|
|
|
static void init_env(void);
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
static int pci_device_autobind(struct pciDevice *dev);
|
2013-10-23 13:44:40 +00:00
|
|
|
static void pci_device_new_from_stub(const struct pciDevice *data);
|
2019-08-13 11:22:58 +00:00
|
|
|
static struct pciDevice *pci_device_find_by_id(struct pciDeviceAddress const *addr);
|
2013-10-23 15:55:02 +00:00
|
|
|
static struct pciDevice *pci_device_find_by_content(const char *path);
|
|
|
|
|
2019-08-20 11:52:55 +00:00
|
|
|
static void pci_driver_new(const char *name, ...);
|
2013-10-23 15:55:02 +00:00
|
|
|
static struct pciDriver *pci_driver_find_by_dev(struct pciDevice *dev);
|
|
|
|
static struct pciDriver *pci_driver_find_by_path(const char *path);
|
2019-06-17 14:42:23 +00:00
|
|
|
static struct pciDriver *pci_driver_find_by_driver_override(struct pciDevice *dev);
|
2013-10-23 15:55:02 +00:00
|
|
|
static int pci_driver_bind(struct pciDriver *driver, struct pciDevice *dev);
|
|
|
|
static int pci_driver_unbind(struct pciDriver *driver, struct pciDevice *dev);
|
|
|
|
static int pci_driver_handle_change(int fd, const char *path);
|
|
|
|
static int pci_driver_handle_bind(const char *path);
|
|
|
|
static int pci_driver_handle_unbind(const char *path);
|
2013-10-23 13:44:40 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Helper functions
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
make_file(const char *path,
|
|
|
|
const char *name,
|
2013-11-27 21:59:52 +00:00
|
|
|
const char *value,
|
|
|
|
ssize_t len)
|
2013-10-23 13:44:40 +00:00
|
|
|
{
|
|
|
|
int fd = -1;
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) filepath = NULL;
|
2013-11-27 21:59:52 +00:00
|
|
|
if (value && len == -1)
|
|
|
|
len = strlen(value);
|
2013-10-23 13:44:40 +00:00
|
|
|
|
|
|
|
if (virAsprintfQuiet(&filepath, "%s/%s", path, name) < 0)
|
|
|
|
ABORT_OOM();
|
|
|
|
|
2016-05-13 10:32:00 +00:00
|
|
|
if ((fd = real_open(filepath, O_CREAT|O_WRONLY, 0666)) < 0)
|
2013-10-23 13:44:40 +00:00
|
|
|
ABORT("Unable to open: %s", filepath);
|
|
|
|
|
2013-11-27 21:59:52 +00:00
|
|
|
if (value && safewrite(fd, value, len) != len)
|
2013-10-23 13:44:40 +00:00
|
|
|
ABORT("Unable to write: %s", filepath);
|
|
|
|
|
|
|
|
VIR_FORCE_CLOSE(fd);
|
|
|
|
}
|
|
|
|
|
2019-08-13 08:51:05 +00:00
|
|
|
static void
|
|
|
|
make_dir(const char *path,
|
|
|
|
const char *name)
|
|
|
|
{
|
|
|
|
VIR_AUTOFREE(char *) dirpath = NULL;
|
|
|
|
|
|
|
|
if (virAsprintfQuiet(&dirpath, "%s/%s", path, name) < 0)
|
|
|
|
ABORT_OOM();
|
|
|
|
|
|
|
|
if (virFileMakePath(dirpath) < 0)
|
|
|
|
ABORT("Unable to create: %s", dirpath);
|
|
|
|
}
|
|
|
|
|
2017-06-01 16:48:52 +00:00
|
|
|
static void
|
|
|
|
make_symlink(const char *path,
|
|
|
|
const char *name,
|
|
|
|
const char *target)
|
|
|
|
{
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) filepath = NULL;
|
2017-06-01 16:48:52 +00:00
|
|
|
|
|
|
|
if (virAsprintfQuiet(&filepath, "%s/%s", path, name) < 0)
|
|
|
|
ABORT_OOM();
|
|
|
|
|
|
|
|
if (symlink(target, filepath) < 0)
|
|
|
|
ABORT("Unable to create symlink filepath -> target");
|
|
|
|
}
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
static int
|
|
|
|
pci_read_file(const char *path,
|
|
|
|
char *buf,
|
2019-06-17 14:42:23 +00:00
|
|
|
size_t buf_size,
|
|
|
|
bool truncate)
|
2013-10-23 15:55:02 +00:00
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
int fd = -1;
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) newpath = NULL;
|
2013-10-23 15:55:02 +00:00
|
|
|
|
2019-08-13 08:37:08 +00:00
|
|
|
if (virAsprintfQuiet(&newpath, "%s/%s", fakerootdir, path) < 0) {
|
2013-10-23 15:55:02 +00:00
|
|
|
errno = ENOMEM;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2016-05-13 10:32:00 +00:00
|
|
|
if ((fd = real_open(newpath, O_RDWR)) < 0)
|
2013-10-23 15:55:02 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
bzero(buf, buf_size);
|
|
|
|
if (saferead(fd, buf, buf_size - 1) < 0) {
|
|
|
|
STDERR("Unable to read from %s", newpath);
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
2019-06-17 14:42:23 +00:00
|
|
|
if (truncate &&
|
|
|
|
ftruncate(fd, 0) < 0)
|
2013-10-23 15:55:02 +00:00
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
ret = 0;
|
2014-03-25 06:53:44 +00:00
|
|
|
cleanup:
|
2016-05-13 10:32:00 +00:00
|
|
|
real_close(fd);
|
2013-10-23 15:55:02 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
static bool
|
|
|
|
pathPrefixIsMocked(const char *path)
|
|
|
|
{
|
|
|
|
return STRPREFIX(path, SYSFS_PCI_PREFIX) ||
|
|
|
|
STRPREFIX(path, SYSFS_KERNEL_PREFIX) ||
|
|
|
|
STRPREFIX(path, DEV_VFIO_PREFIX);
|
|
|
|
}
|
|
|
|
|
2013-10-23 13:44:40 +00:00
|
|
|
static int
|
|
|
|
getrealpath(char **newpath,
|
|
|
|
const char *path)
|
|
|
|
{
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
if (!fakerootdir && pathPrefixIsMocked(path))
|
|
|
|
init_env();
|
2013-10-23 13:44:40 +00:00
|
|
|
|
2015-12-04 13:51:08 +00:00
|
|
|
if (STRPREFIX(path, SYSFS_PCI_PREFIX)) {
|
2019-08-13 08:37:08 +00:00
|
|
|
if (virAsprintfQuiet(newpath, "%s/sys/bus/pci/%s",
|
|
|
|
fakerootdir,
|
2015-12-04 13:51:08 +00:00
|
|
|
path + strlen(SYSFS_PCI_PREFIX)) < 0) {
|
2013-10-23 13:44:40 +00:00
|
|
|
errno = ENOMEM;
|
|
|
|
return -1;
|
|
|
|
}
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
} else if (pathPrefixIsMocked(path)) {
|
2019-08-29 19:18:58 +00:00
|
|
|
if (virAsprintfQuiet(newpath, "%s/%s",
|
|
|
|
fakerootdir,
|
|
|
|
path) < 0) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
return -1;
|
|
|
|
}
|
2013-10-23 13:44:40 +00:00
|
|
|
} else {
|
|
|
|
if (VIR_STRDUP_QUIET(*newpath, path) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
static bool
|
|
|
|
find_fd(int fd, size_t *indx)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < nCallbacks; i++) {
|
|
|
|
if (callbacks[i].fd == fd) {
|
|
|
|
if (indx)
|
|
|
|
*indx = i;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
add_fd(int fd, const char *path)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (find_fd(fd, &i)) {
|
|
|
|
struct fdCallback cb = callbacks[i];
|
|
|
|
ABORT("FD %d %s already present in the array as %d %s",
|
|
|
|
fd, path, cb.fd, cb.path);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N_QUIET(callbacks, nCallbacks + 1) < 0 ||
|
|
|
|
VIR_STRDUP_QUIET(callbacks[nCallbacks].path, path) < 0) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
callbacks[nCallbacks++].fd = fd;
|
|
|
|
ret = 0;
|
2014-03-25 06:53:44 +00:00
|
|
|
cleanup:
|
2013-10-23 15:55:02 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
remove_fd(int fd)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (find_fd(fd, &i)) {
|
|
|
|
struct fdCallback cb = callbacks[i];
|
|
|
|
|
|
|
|
if (pci_driver_handle_change(cb.fd, cb.path) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
VIR_FREE(cb.path);
|
|
|
|
if (VIR_DELETE_ELEMENT(callbacks, i, nCallbacks) < 0) {
|
|
|
|
errno = EINVAL;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
2014-03-25 06:53:44 +00:00
|
|
|
cleanup:
|
2013-10-23 15:55:02 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2013-10-23 13:44:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* PCI Device functions
|
|
|
|
*/
|
2019-08-13 11:22:58 +00:00
|
|
|
static char *
|
|
|
|
pci_address_format(struct pciDeviceAddress const *addr)
|
|
|
|
{
|
|
|
|
char *ret;
|
|
|
|
|
|
|
|
ignore_value(virAsprintfQuiet(&ret, ADDR_STR_FMT,
|
|
|
|
addr->domain, addr->bus,
|
|
|
|
addr->device, addr->function));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pci_address_parse(struct pciDeviceAddress *addr,
|
|
|
|
const char *buf)
|
|
|
|
{
|
|
|
|
if (sscanf(buf, ADDR_STR_FMT,
|
|
|
|
&addr->domain, &addr->bus,
|
|
|
|
&addr->device, &addr->function) != 4)
|
|
|
|
return -1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-08-13 13:31:09 +00:00
|
|
|
static char *
|
|
|
|
pci_device_get_path(const struct pciDevice *dev,
|
|
|
|
const char *file,
|
|
|
|
bool faked)
|
|
|
|
{
|
|
|
|
char *ret = NULL;
|
|
|
|
const char *prefix = "";
|
2019-08-13 11:22:58 +00:00
|
|
|
VIR_AUTOFREE(char *) devid = NULL;
|
2019-08-13 13:31:09 +00:00
|
|
|
|
|
|
|
if (faked)
|
|
|
|
prefix = fakerootdir;
|
|
|
|
|
2019-08-13 11:22:58 +00:00
|
|
|
if (!(devid = pci_address_format(&dev->addr)))
|
|
|
|
return NULL;
|
|
|
|
|
2019-08-13 09:05:35 +00:00
|
|
|
/* PCI devices really do live under /sys/devices/pciDDDD:BB
|
|
|
|
* and then they are just symlinked to /sys/bus/pci/devices/
|
|
|
|
*/
|
2019-08-13 13:31:09 +00:00
|
|
|
if (file) {
|
2019-08-13 09:05:35 +00:00
|
|
|
ignore_value(virAsprintfQuiet(&ret, "%s/sys/devices/pci%04x:%02x/%s/%s",
|
|
|
|
prefix, dev->addr.domain, dev->addr.bus,
|
|
|
|
devid, file));
|
2019-08-13 13:31:09 +00:00
|
|
|
} else {
|
2019-08-13 09:05:35 +00:00
|
|
|
ignore_value(virAsprintfQuiet(&ret, "%s/sys/devices/pci%04x:%02x/%s",
|
|
|
|
prefix, dev->addr.domain, dev->addr.bus,
|
|
|
|
devid));
|
2019-08-13 13:31:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-08-13 15:10:50 +00:00
|
|
|
static void
|
|
|
|
pci_device_create_iommu(const struct pciDevice *dev,
|
|
|
|
const char *devid)
|
|
|
|
{
|
2019-08-29 19:18:58 +00:00
|
|
|
struct pciIommuGroup *iommuGroup;
|
2019-08-13 15:10:50 +00:00
|
|
|
VIR_AUTOFREE(char *) iommuPath = NULL;
|
|
|
|
char tmp[256];
|
2019-08-29 19:18:58 +00:00
|
|
|
size_t i;
|
2019-08-13 15:10:50 +00:00
|
|
|
|
|
|
|
if (virAsprintfQuiet(&iommuPath, "%s/sys/kernel/iommu_groups/%d/devices/",
|
|
|
|
fakerootdir, dev->iommuGroup) < 0)
|
|
|
|
ABORT_OOM();
|
|
|
|
|
|
|
|
if (virFileMakePath(iommuPath) < 0)
|
|
|
|
ABORT("Unable to create: %s", iommuPath);
|
|
|
|
|
|
|
|
if (snprintf(tmp, sizeof(tmp),
|
|
|
|
"../../../../devices/pci%04x:%02x/%s",
|
|
|
|
dev->addr.domain, dev->addr.bus, devid) < 0) {
|
|
|
|
ABORT("@tmp overflow");
|
|
|
|
}
|
|
|
|
|
|
|
|
make_symlink(iommuPath, devid, tmp);
|
2019-08-29 19:18:58 +00:00
|
|
|
|
|
|
|
/* pci_device_create_iommu can be called more than one for the
|
|
|
|
* same iommuGroup. Bail out here if the iommuGroup was already
|
|
|
|
* created beforehand. */
|
|
|
|
for (i = 0; i < npciIommuGroups; i++) {
|
|
|
|
if (pciIommuGroups[i]->iommu == dev->iommuGroup)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VIR_ALLOC_QUIET(iommuGroup) < 0)
|
|
|
|
ABORT_OOM();
|
|
|
|
|
|
|
|
iommuGroup->iommu = dev->iommuGroup;
|
|
|
|
iommuGroup->nDevicesBoundToVFIO = 0; /* No device bound to VFIO by default */
|
|
|
|
|
|
|
|
if (VIR_APPEND_ELEMENT_QUIET(pciIommuGroups, npciIommuGroups,
|
|
|
|
iommuGroup) < 0)
|
|
|
|
ABORT_OOM();
|
2019-08-13 15:10:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-10-23 13:44:40 +00:00
|
|
|
static void
|
|
|
|
pci_device_new_from_stub(const struct pciDevice *data)
|
|
|
|
{
|
|
|
|
struct pciDevice *dev;
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) devpath = NULL;
|
2019-08-13 09:05:35 +00:00
|
|
|
VIR_AUTOFREE(char *) devsympath = NULL;
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) id = NULL;
|
2019-08-13 11:22:58 +00:00
|
|
|
VIR_AUTOFREE(char *) devid = NULL;
|
2014-01-22 21:26:48 +00:00
|
|
|
char *c;
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) configSrc = NULL;
|
2017-06-01 16:48:52 +00:00
|
|
|
char tmp[256];
|
2013-11-05 14:07:49 +00:00
|
|
|
struct stat sb;
|
2018-04-30 14:04:39 +00:00
|
|
|
bool configSrcExists = false;
|
2013-10-23 13:44:40 +00:00
|
|
|
|
2019-08-13 11:22:58 +00:00
|
|
|
if (!(devid = pci_address_format(&data->addr)) ||
|
|
|
|
VIR_STRDUP_QUIET(id, devid) < 0)
|
2014-01-22 21:26:48 +00:00
|
|
|
ABORT_OOM();
|
|
|
|
|
|
|
|
/* Replace ':' with '-' to create the config filename from the
|
|
|
|
* device ID. The device ID cannot be used directly as filename
|
|
|
|
* because it contains ':' and Windows does not allow ':' in
|
|
|
|
* filenames. */
|
|
|
|
c = strchr(id, ':');
|
|
|
|
|
|
|
|
while (c) {
|
|
|
|
*c = '-';
|
|
|
|
c = strchr(c, ':');
|
|
|
|
}
|
|
|
|
|
2013-10-23 13:44:40 +00:00
|
|
|
if (VIR_ALLOC_QUIET(dev) < 0 ||
|
2013-11-27 21:31:53 +00:00
|
|
|
virAsprintfQuiet(&configSrc, "%s/virpcitestdata/%s.config",
|
2019-08-13 13:31:09 +00:00
|
|
|
abs_srcdir, id) < 0)
|
2013-10-23 13:44:40 +00:00
|
|
|
ABORT_OOM();
|
|
|
|
|
|
|
|
memcpy(dev, data, sizeof(*dev));
|
|
|
|
|
2019-08-13 13:31:09 +00:00
|
|
|
if (!(devpath = pci_device_get_path(dev, NULL, true)))
|
|
|
|
ABORT_OOM();
|
|
|
|
|
2013-10-23 13:44:40 +00:00
|
|
|
if (virFileMakePath(devpath) < 0)
|
|
|
|
ABORT("Unable to create: %s", devpath);
|
|
|
|
|
tests: fix mocking of stat() / lstat() functions
Quite a few of the tests have a need to mock the stat() / lstat()
functions and they are taking somewhat different & inconsistent
approaches none of which are actually fully correct. This is shown
by fact that 'make check' fails on 32-bit hosts. Investigation
revealed that the code was calling into the native C library impl,
not getting intercepted by our mocks.
The POSIX stat() function might resolve to any number of different
symbols in the C library.
The may be an additional stat64() function exposed by the headers
too.
On 64-bit hosts the stat & stat64 functions are identical, always
refering to the 64-bit ABI.
On 32-bit hosts they refer to the 32-bit & 64-bit ABIs respectively.
Libvirt uses _FILE_OFFSET_BITS=64 on 32-bit hosts, which causes the
C library to transparently rewrite stat() calls to be stat64() calls.
Libvirt will never see the 32-bit ABI from the traditional stat()
call. We cannot assume this rewriting is done using a macro. It might
be, but on GLibC it is done with a magic __asm__ statement to apply
the rewrite at link time instead of at preprocessing.
In GLibC there may be two additional functions exposed by the headers,
__xstat() and __xstat64(). When these exist, stat() and stat64() are
transparently rewritten to call __xstat() and __xstat64() respectively.
The former symbols will not actally exist in the library at all, only
the header. The leading "__" indicates the symbols are a private impl
detail of the C library that applications should not care about.
Unfortunately, because we are trying to mock replace the C library,
we need to know about this internal impl detail.
With all this in mind the list of functions we have to mock will depend
on several factors
- If _FILE_OFFSET_BITS is set, then we are on a 32-bit host, and we
only need to mock stat64 and __xstat64. The other stat / __xstat
functions exist, but we'll never call them so they can be ignored
for mocking.
- If _FILE_OFFSET_BITS is not set, then we are on a 64-bit host and
we should mock stat, stat64, __xstat & __xstat64. Either may be
called by app code.
- If __xstat & __xstat64 exist, then stat & stat64 will not exist
as symbols in the library, so the latter should not be mocked.
The same all applies to lstat()
These rules are complex enough that we don't want to duplicate them
across every mock file, so this centralizes all the logic in a helper
file virmockstathelper.c that should be #included when needed. The
code merely need to provide a filename rewriting callback called
virMockStatRedirect(). Optionally VIR_MOCK_STAT_HOOK can be defined
as a macro if further processing is needed inline.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-04-01 16:47:25 +00:00
|
|
|
if (stat(configSrc, &sb) == 0)
|
2018-04-30 14:04:39 +00:00
|
|
|
configSrcExists = true;
|
|
|
|
|
2013-11-05 14:07:49 +00:00
|
|
|
/* If there is a config file for the device within virpcitestdata dir,
|
|
|
|
* symlink it. Otherwise create a dummy config file. */
|
2018-04-30 14:04:39 +00:00
|
|
|
if (configSrcExists) {
|
2013-11-27 21:59:52 +00:00
|
|
|
/* On success, copy @configSrc into the destination (a copy,
|
|
|
|
* rather than a symlink, is required since we write into the
|
|
|
|
* file, and parallel VPATH builds must not stomp on the
|
|
|
|
* original; besides, 'make distcheck' requires the original
|
|
|
|
* to be read-only */
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) buf = NULL;
|
2013-11-27 21:59:52 +00:00
|
|
|
ssize_t len;
|
|
|
|
|
|
|
|
if ((len = virFileReadAll(configSrc, 4096, &buf)) < 0)
|
|
|
|
ABORT("Unable to read config file '%s'", configSrc);
|
|
|
|
|
|
|
|
make_file(devpath, "config", buf, len);
|
2013-11-05 14:07:49 +00:00
|
|
|
} else {
|
|
|
|
/* If there's no config data in the virpcitestdata dir, create a dummy
|
|
|
|
* config file */
|
2013-11-27 21:59:52 +00:00
|
|
|
make_file(devpath, "config", "some dummy config", -1);
|
2013-11-05 14:07:49 +00:00
|
|
|
}
|
2013-10-23 13:44:40 +00:00
|
|
|
|
|
|
|
if (snprintf(tmp, sizeof(tmp), "0x%.4x", dev->vendor) < 0)
|
|
|
|
ABORT("@tmp overflow");
|
2013-11-27 21:59:52 +00:00
|
|
|
make_file(devpath, "vendor", tmp, -1);
|
2013-10-23 13:44:40 +00:00
|
|
|
|
|
|
|
if (snprintf(tmp, sizeof(tmp), "0x%.4x", dev->device) < 0)
|
|
|
|
ABORT("@tmp overflow");
|
2013-11-27 21:59:52 +00:00
|
|
|
make_file(devpath, "device", tmp, -1);
|
2013-10-23 13:44:40 +00:00
|
|
|
|
2019-03-12 08:08:00 +00:00
|
|
|
if (snprintf(tmp, sizeof(tmp), "0x%.4x", dev->klass) < 0)
|
2013-12-24 18:07:27 +00:00
|
|
|
ABORT("@tmp overflow");
|
|
|
|
make_file(devpath, "class", tmp, -1);
|
|
|
|
|
2019-06-17 14:42:23 +00:00
|
|
|
make_file(devpath, "driver_override", NULL, -1);
|
|
|
|
|
2019-08-13 15:10:50 +00:00
|
|
|
pci_device_create_iommu(dev, devid);
|
2017-06-01 16:48:52 +00:00
|
|
|
|
|
|
|
if (snprintf(tmp, sizeof(tmp),
|
|
|
|
"../../../kernel/iommu_groups/%d", dev->iommuGroup) < 0) {
|
|
|
|
ABORT("@tmp overflow");
|
|
|
|
}
|
|
|
|
make_symlink(devpath, "iommu_group", tmp);
|
|
|
|
|
2019-08-13 09:05:35 +00:00
|
|
|
if (snprintf(tmp, sizeof(tmp),
|
|
|
|
"../../../devices/pci%04x:%02x/%s",
|
|
|
|
dev->addr.domain, dev->addr.bus, devid) < 0) {
|
|
|
|
ABORT("@tmp overflow");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (virAsprintfQuiet(&devsympath, "%s" SYSFS_PCI_PREFIX "devices", fakerootdir) < 0)
|
|
|
|
ABORT_OOM();
|
|
|
|
|
|
|
|
make_symlink(devsympath, devid, tmp);
|
|
|
|
|
2019-08-29 19:18:59 +00:00
|
|
|
if (dev->physfn) {
|
|
|
|
if (snprintf(tmp, sizeof(tmp),
|
|
|
|
"%s%s/devices/%s", fakerootdir,
|
|
|
|
SYSFS_PCI_PREFIX, dev->physfn) < 0) {
|
|
|
|
ABORT("@tmp overflow");
|
|
|
|
}
|
|
|
|
make_symlink(devpath, "physfn", tmp);
|
|
|
|
}
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
if (pci_device_autobind(dev) < 0)
|
2019-08-13 11:22:58 +00:00
|
|
|
ABORT("Unable to bind: %s", devid);
|
2013-10-23 15:55:02 +00:00
|
|
|
|
2014-03-13 11:59:32 +00:00
|
|
|
if (VIR_APPEND_ELEMENT_QUIET(pciDevices, nPCIDevices, dev) < 0)
|
2013-10-23 13:44:40 +00:00
|
|
|
ABORT_OOM();
|
|
|
|
}
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
static struct pciDevice *
|
2019-08-13 11:22:58 +00:00
|
|
|
pci_device_find_by_id(struct pciDeviceAddress const *addr)
|
2013-10-23 15:55:02 +00:00
|
|
|
{
|
|
|
|
size_t i;
|
2014-03-13 11:59:32 +00:00
|
|
|
for (i = 0; i < nPCIDevices; i++) {
|
2013-10-23 15:55:02 +00:00
|
|
|
struct pciDevice *dev = pciDevices[i];
|
|
|
|
|
2019-08-13 11:22:58 +00:00
|
|
|
if (!memcmp(&dev->addr, addr, sizeof(*addr)))
|
2013-10-23 15:55:02 +00:00
|
|
|
return dev;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pciDevice *
|
|
|
|
pci_device_find_by_content(const char *path)
|
|
|
|
{
|
|
|
|
char tmp[32];
|
2019-08-13 11:22:58 +00:00
|
|
|
struct pciDeviceAddress addr;
|
2013-10-23 15:55:02 +00:00
|
|
|
|
2019-08-13 11:22:58 +00:00
|
|
|
if (pci_read_file(path, tmp, sizeof(tmp), true) < 0 ||
|
|
|
|
pci_address_parse(&addr, tmp) < 0)
|
2013-10-23 15:55:02 +00:00
|
|
|
return NULL;
|
|
|
|
|
2019-08-13 11:22:58 +00:00
|
|
|
return pci_device_find_by_id(&addr);
|
2013-10-23 15:55:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pci_device_autobind(struct pciDevice *dev)
|
|
|
|
{
|
2019-06-17 14:42:23 +00:00
|
|
|
struct pciDriver *driver = pci_driver_find_by_driver_override(dev);
|
|
|
|
|
|
|
|
if (!driver)
|
|
|
|
driver = pci_driver_find_by_dev(dev);
|
2013-10-23 15:55:02 +00:00
|
|
|
|
|
|
|
if (!driver) {
|
|
|
|
/* No driver found. Nothing to do */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pci_driver_bind(driver, dev);
|
|
|
|
}
|
|
|
|
|
2019-08-29 19:18:58 +00:00
|
|
|
static int
|
|
|
|
pci_vfio_release_iommu(struct pciDevice *device)
|
|
|
|
{
|
|
|
|
VIR_AUTOFREE(char *) vfiopath = NULL;
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
for (i = 0; i < npciIommuGroups; i++) {
|
|
|
|
if (device->iommuGroup != pciIommuGroups[i]->iommu)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pciIommuGroups[i]->nDevicesBoundToVFIO == 0) {
|
|
|
|
errno = EXDEV;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
pciIommuGroups[i]->nDevicesBoundToVFIO--;
|
|
|
|
|
|
|
|
if (!pciIommuGroups[i]->nDevicesBoundToVFIO) {
|
|
|
|
if (virAsprintfQuiet(&vfiopath, "%s/dev/vfio/%d",
|
|
|
|
fakerootdir,
|
|
|
|
device->iommuGroup) < 0) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (unlink(vfiopath) < 0)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pci_vfio_lock_iommu(struct pciDevice *device)
|
|
|
|
{
|
|
|
|
VIR_AUTOFREE(char *) vfiopath = NULL;
|
|
|
|
int ret = -1;
|
|
|
|
size_t i = 0;
|
|
|
|
int fd = -1;
|
|
|
|
|
|
|
|
for (i = 0; i < npciIommuGroups; i++) {
|
|
|
|
if (device->iommuGroup != pciIommuGroups[i]->iommu)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pciIommuGroups[i]->nDevicesBoundToVFIO == 0) {
|
|
|
|
if (virAsprintfQuiet(&vfiopath, "%s/dev/vfio/%d",
|
|
|
|
fakerootdir,
|
|
|
|
device->iommuGroup) < 0) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
if ((fd = real_open(vfiopath, O_CREAT)) < 0)
|
|
|
|
goto cleanup;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
pciIommuGroups[i]->nDevicesBoundToVFIO++;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = 0;
|
|
|
|
cleanup:
|
|
|
|
if (fd != -1)
|
|
|
|
real_close(fd);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-10-23 15:55:02 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* PCI Driver functions
|
|
|
|
*/
|
2019-08-13 14:11:17 +00:00
|
|
|
static char *
|
|
|
|
pci_driver_get_path(const struct pciDriver *driver,
|
|
|
|
const char *file,
|
|
|
|
bool faked)
|
|
|
|
{
|
|
|
|
char *ret = NULL;
|
|
|
|
const char *prefix = "";
|
|
|
|
|
|
|
|
if (faked)
|
|
|
|
prefix = fakerootdir;
|
|
|
|
|
|
|
|
if (file) {
|
|
|
|
ignore_value(virAsprintfQuiet(&ret, "%s" SYSFS_PCI_PREFIX "drivers/%s/%s",
|
|
|
|
prefix, driver->name, file));
|
|
|
|
} else {
|
|
|
|
ignore_value(virAsprintfQuiet(&ret, "%s" SYSFS_PCI_PREFIX "drivers/%s",
|
|
|
|
prefix, driver->name));
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
static void
|
2019-08-20 11:52:55 +00:00
|
|
|
pci_driver_new(const char *name, ...)
|
2013-10-23 15:55:02 +00:00
|
|
|
{
|
|
|
|
struct pciDriver *driver;
|
|
|
|
va_list args;
|
|
|
|
int vendor, device;
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) driverpath = NULL;
|
2013-10-23 15:55:02 +00:00
|
|
|
|
|
|
|
if (VIR_ALLOC_QUIET(driver) < 0 ||
|
|
|
|
VIR_STRDUP_QUIET(driver->name, name) < 0 ||
|
2019-08-13 14:11:17 +00:00
|
|
|
!(driverpath = pci_driver_get_path(driver, NULL, true)))
|
2013-10-23 15:55:02 +00:00
|
|
|
ABORT_OOM();
|
|
|
|
|
|
|
|
if (virFileMakePath(driverpath) < 0)
|
|
|
|
ABORT("Unable to create: %s", driverpath);
|
|
|
|
|
2019-08-20 11:52:55 +00:00
|
|
|
va_start(args, name);
|
2013-10-23 15:55:02 +00:00
|
|
|
|
|
|
|
while ((vendor = va_arg(args, int)) != -1) {
|
|
|
|
if ((device = va_arg(args, int)) == -1)
|
|
|
|
ABORT("Invalid vendor device pair for driver %s", name);
|
|
|
|
|
|
|
|
if (VIR_REALLOC_N_QUIET(driver->vendor, driver->len + 1) < 0 ||
|
|
|
|
VIR_REALLOC_N_QUIET(driver->device, driver->len + 1) < 0)
|
|
|
|
ABORT_OOM();
|
|
|
|
|
|
|
|
driver->vendor[driver->len] = vendor;
|
|
|
|
driver->device[driver->len] = device;
|
|
|
|
driver->len++;
|
|
|
|
}
|
|
|
|
|
2013-11-05 15:37:38 +00:00
|
|
|
va_end(args);
|
|
|
|
|
2013-11-27 21:59:52 +00:00
|
|
|
make_file(driverpath, "bind", NULL, -1);
|
|
|
|
make_file(driverpath, "unbind", NULL, -1);
|
2013-10-23 15:55:02 +00:00
|
|
|
|
2014-03-13 11:59:32 +00:00
|
|
|
if (VIR_APPEND_ELEMENT_QUIET(pciDrivers, nPCIDrivers, driver) < 0)
|
2013-10-23 15:55:02 +00:00
|
|
|
ABORT_OOM();
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pciDriver *
|
|
|
|
pci_driver_find_by_dev(struct pciDevice *dev)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
2014-03-13 11:59:32 +00:00
|
|
|
for (i = 0; i < nPCIDrivers; i++) {
|
2013-10-23 15:55:02 +00:00
|
|
|
struct pciDriver *driver = pciDrivers[i];
|
|
|
|
size_t j;
|
|
|
|
|
|
|
|
for (j = 0; j < driver->len; j++) {
|
|
|
|
if (driver->vendor[j] == dev->vendor &&
|
|
|
|
driver->device[j] == dev->device)
|
|
|
|
return driver;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct pciDriver *
|
|
|
|
pci_driver_find_by_path(const char *path)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
2014-03-13 11:59:32 +00:00
|
|
|
for (i = 0; i < nPCIDrivers; i++) {
|
2013-10-23 15:55:02 +00:00
|
|
|
struct pciDriver *driver = pciDrivers[i];
|
|
|
|
|
|
|
|
if (strstr(path, driver->name))
|
|
|
|
return driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-06-17 14:42:23 +00:00
|
|
|
static struct pciDriver *
|
|
|
|
pci_driver_find_by_driver_override(struct pciDevice *dev)
|
|
|
|
{
|
|
|
|
VIR_AUTOFREE(char *) path = NULL;
|
|
|
|
char tmp[32];
|
|
|
|
size_t i;
|
|
|
|
|
2019-08-13 13:31:09 +00:00
|
|
|
if (!(path = pci_device_get_path(dev, "driver_override", false)))
|
2019-06-17 14:42:23 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (pci_read_file(path, tmp, sizeof(tmp), false) < 0)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (i = 0; i < nPCIDrivers; i++) {
|
|
|
|
struct pciDriver *driver = pciDrivers[i];
|
|
|
|
|
|
|
|
if (STREQ(tmp, driver->name))
|
|
|
|
return driver;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
static int
|
|
|
|
pci_driver_bind(struct pciDriver *driver,
|
|
|
|
struct pciDevice *dev)
|
|
|
|
{
|
2019-08-13 11:22:58 +00:00
|
|
|
VIR_AUTOFREE(char *) devid = NULL;
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) devpath = NULL;
|
|
|
|
VIR_AUTOFREE(char *) driverpath = NULL;
|
2013-10-23 15:55:02 +00:00
|
|
|
|
2019-06-17 15:33:06 +00:00
|
|
|
if (dev->driver) {
|
|
|
|
/* Device already bound */
|
2013-10-23 15:55:02 +00:00
|
|
|
errno = ENODEV;
|
2019-08-13 11:50:48 +00:00
|
|
|
return -1;
|
2013-10-23 15:55:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Make symlink under device tree */
|
2019-08-13 13:31:09 +00:00
|
|
|
if (!(devpath = pci_device_get_path(dev, "driver", true)) ||
|
2019-08-13 14:11:17 +00:00
|
|
|
!(driverpath = pci_driver_get_path(driver, NULL, true))) {
|
2013-10-23 15:55:02 +00:00
|
|
|
errno = ENOMEM;
|
2019-08-13 11:50:48 +00:00
|
|
|
return -1;
|
2013-10-23 15:55:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (symlink(driverpath, devpath) < 0)
|
2019-08-13 11:50:48 +00:00
|
|
|
return -1;
|
2013-10-23 15:55:02 +00:00
|
|
|
|
|
|
|
/* Make symlink under driver tree */
|
|
|
|
VIR_FREE(devpath);
|
|
|
|
VIR_FREE(driverpath);
|
2019-08-13 11:22:58 +00:00
|
|
|
if (!(devid = pci_address_format(&dev->addr)) ||
|
|
|
|
!(devpath = pci_device_get_path(dev, NULL, true)) ||
|
|
|
|
!(driverpath = pci_driver_get_path(driver, devid, true))) {
|
2013-10-23 15:55:02 +00:00
|
|
|
errno = ENOMEM;
|
2019-08-13 11:50:48 +00:00
|
|
|
return -1;
|
2013-10-23 15:55:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (symlink(devpath, driverpath) < 0)
|
2019-08-13 11:50:48 +00:00
|
|
|
return -1;
|
2013-10-23 15:55:02 +00:00
|
|
|
|
2019-08-29 19:18:58 +00:00
|
|
|
if (STREQ(driver->name, "vfio-pci") &&
|
|
|
|
pci_vfio_lock_iommu(dev) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
dev->driver = driver;
|
2019-08-13 11:50:48 +00:00
|
|
|
return 0;
|
2013-10-23 15:55:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pci_driver_unbind(struct pciDriver *driver,
|
|
|
|
struct pciDevice *dev)
|
|
|
|
{
|
2019-08-13 11:22:58 +00:00
|
|
|
VIR_AUTOFREE(char *) devid = NULL;
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) devpath = NULL;
|
|
|
|
VIR_AUTOFREE(char *) driverpath = NULL;
|
2013-10-23 15:55:02 +00:00
|
|
|
|
2019-06-17 15:33:06 +00:00
|
|
|
if (dev->driver != driver) {
|
|
|
|
/* Device not bound to the @driver */
|
2013-10-23 15:55:02 +00:00
|
|
|
errno = ENODEV;
|
2019-08-13 11:50:48 +00:00
|
|
|
return -1;
|
2013-10-23 15:55:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Make symlink under device tree */
|
2019-08-13 11:22:58 +00:00
|
|
|
if (!(devid = pci_address_format(&dev->addr)) ||
|
|
|
|
!(devpath = pci_device_get_path(dev, "driver", true)) ||
|
|
|
|
!(driverpath = pci_driver_get_path(driver, devid, true))) {
|
2013-10-23 15:55:02 +00:00
|
|
|
errno = ENOMEM;
|
2019-08-13 11:50:48 +00:00
|
|
|
return -1;
|
2013-10-23 15:55:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (unlink(devpath) < 0 ||
|
|
|
|
unlink(driverpath) < 0)
|
2019-08-13 11:50:48 +00:00
|
|
|
return -1;
|
2013-10-23 15:55:02 +00:00
|
|
|
|
2019-08-29 19:18:58 +00:00
|
|
|
if (STREQ(driver->name, "vfio-pci") &&
|
|
|
|
pci_vfio_release_iommu(dev) < 0)
|
|
|
|
return -1;
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
dev->driver = NULL;
|
2019-08-13 11:50:48 +00:00
|
|
|
return 0;
|
2013-10-23 15:55:02 +00:00
|
|
|
}
|
|
|
|
|
2014-01-16 11:28:12 +00:00
|
|
|
static int
|
|
|
|
pci_driver_handle_drivers_probe(const char *path)
|
|
|
|
{
|
|
|
|
struct pciDevice *dev;
|
|
|
|
|
|
|
|
if (!(dev = pci_device_find_by_content(path))) {
|
|
|
|
errno = ENODEV;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dev->driver)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return pci_device_autobind(dev);
|
|
|
|
}
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
static int
|
|
|
|
pci_driver_handle_change(int fd ATTRIBUTE_UNUSED, const char *path)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
const char *file = last_component(path);
|
|
|
|
|
2014-01-16 11:28:12 +00:00
|
|
|
if (STREQ(file, "bind"))
|
2013-10-23 15:55:02 +00:00
|
|
|
ret = pci_driver_handle_bind(path);
|
2014-01-16 11:28:12 +00:00
|
|
|
else if (STREQ(file, "unbind"))
|
2013-10-23 15:55:02 +00:00
|
|
|
ret = pci_driver_handle_unbind(path);
|
2014-01-16 11:28:12 +00:00
|
|
|
else if (STREQ(file, "drivers_probe"))
|
|
|
|
ret = pci_driver_handle_drivers_probe(path);
|
2019-06-17 14:42:23 +00:00
|
|
|
else if (STREQ(file, "driver_override"))
|
|
|
|
ret = 0; /* nada */
|
2014-01-16 11:28:12 +00:00
|
|
|
else
|
2013-10-23 15:55:02 +00:00
|
|
|
ABORT("Not handled write to: %s", path);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pci_driver_handle_bind(const char *path)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct pciDevice *dev = pci_device_find_by_content(path);
|
|
|
|
struct pciDriver *driver = pci_driver_find_by_path(path);
|
|
|
|
|
2019-08-20 11:52:55 +00:00
|
|
|
if (!driver || !dev) {
|
2019-06-17 15:33:06 +00:00
|
|
|
/* No driver, no device or failing driver requested */
|
2013-10-23 15:55:02 +00:00
|
|
|
errno = ENODEV;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pci_driver_bind(driver, dev);
|
2014-03-25 06:53:44 +00:00
|
|
|
cleanup:
|
2013-10-23 15:55:02 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
pci_driver_handle_unbind(const char *path)
|
|
|
|
{
|
|
|
|
int ret = -1;
|
|
|
|
struct pciDevice *dev = pci_device_find_by_content(path);
|
|
|
|
|
2019-08-20 11:52:55 +00:00
|
|
|
if (!dev || !dev->driver) {
|
2019-06-17 15:33:06 +00:00
|
|
|
/* No device, device not binded or failing driver requested */
|
2013-10-23 15:55:02 +00:00
|
|
|
errno = ENODEV;
|
|
|
|
goto cleanup;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = pci_driver_unbind(dev->driver, dev);
|
2014-03-25 06:53:44 +00:00
|
|
|
cleanup:
|
2013-10-23 15:55:02 +00:00
|
|
|
return ret;
|
|
|
|
}
|
2019-06-17 15:42:13 +00:00
|
|
|
|
2013-10-23 13:44:40 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Functions to load the symbols and init the environment
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
init_syms(void)
|
|
|
|
{
|
2016-05-13 10:32:00 +00:00
|
|
|
if (real_access)
|
2013-10-23 13:44:40 +00:00
|
|
|
return;
|
|
|
|
|
2016-05-13 10:32:00 +00:00
|
|
|
VIR_MOCK_REAL_INIT(access);
|
|
|
|
VIR_MOCK_REAL_INIT(open);
|
2019-08-20 10:30:08 +00:00
|
|
|
# ifdef __GLIBC__
|
2019-08-15 14:37:17 +00:00
|
|
|
VIR_MOCK_REAL_INIT(__open_2);
|
2019-08-20 10:30:08 +00:00
|
|
|
# endif /* ! __GLIBC__ */
|
2016-05-13 10:32:00 +00:00
|
|
|
VIR_MOCK_REAL_INIT(close);
|
|
|
|
VIR_MOCK_REAL_INIT(opendir);
|
2018-05-03 08:01:04 +00:00
|
|
|
VIR_MOCK_REAL_INIT(virFileCanonicalizePath);
|
2013-10-23 13:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
init_env(void)
|
|
|
|
{
|
2019-08-13 08:44:53 +00:00
|
|
|
VIR_AUTOFREE(char *) tmp = NULL;
|
2019-08-13 08:37:08 +00:00
|
|
|
|
2015-12-04 12:38:16 +00:00
|
|
|
if (!(fakerootdir = getenv("LIBVIRT_FAKE_ROOT_DIR")))
|
|
|
|
ABORT("Missing LIBVIRT_FAKE_ROOT_DIR env variable\n");
|
2013-10-23 13:44:40 +00:00
|
|
|
|
2019-08-13 08:44:53 +00:00
|
|
|
if (virAsprintfQuiet(&tmp, "%s%s",
|
2015-12-04 13:51:08 +00:00
|
|
|
fakerootdir, SYSFS_PCI_PREFIX) < 0)
|
2015-12-04 09:05:56 +00:00
|
|
|
ABORT_OOM();
|
|
|
|
|
2019-08-13 08:44:53 +00:00
|
|
|
if (virFileMakePath(tmp) < 0)
|
|
|
|
ABORT("Unable to create: %s", tmp);
|
2015-12-04 09:05:56 +00:00
|
|
|
|
2019-08-13 08:51:05 +00:00
|
|
|
make_dir(tmp, "devices");
|
|
|
|
make_dir(tmp, "drivers");
|
2019-08-13 08:44:53 +00:00
|
|
|
make_file(tmp, "drivers_probe", NULL, -1);
|
2014-01-16 11:28:12 +00:00
|
|
|
|
2019-08-29 19:18:58 +00:00
|
|
|
/* Create /dev/vfio/ dir and /dev/vfio/vfio file */
|
|
|
|
VIR_FREE(tmp);
|
|
|
|
if (virAsprintfQuiet(&tmp, "%s/dev/vfio", fakerootdir) < 0)
|
|
|
|
ABORT_OOM();
|
|
|
|
|
|
|
|
if (virFileMakePath(tmp) < 0)
|
|
|
|
ABORT("Unable to create: %s", tmp);
|
|
|
|
|
|
|
|
make_file(tmp, "vfio", NULL, -1);
|
|
|
|
|
2017-11-03 12:09:47 +00:00
|
|
|
# define MAKE_PCI_DRIVER(name, ...) \
|
2019-08-20 11:52:55 +00:00
|
|
|
pci_driver_new(name, __VA_ARGS__, -1, -1)
|
2013-10-23 15:55:02 +00:00
|
|
|
|
|
|
|
MAKE_PCI_DRIVER("iwlwifi", 0x8086, 0x0044);
|
|
|
|
MAKE_PCI_DRIVER("i915", 0x8086, 0x0046, 0x8086, 0x0047);
|
2019-08-20 11:52:55 +00:00
|
|
|
MAKE_PCI_DRIVER("vfio-pci", -1, -1);
|
2013-10-23 15:55:02 +00:00
|
|
|
|
2019-08-29 19:18:58 +00:00
|
|
|
# define MAKE_PCI_DEVICE(Id, Vendor, Device, IommuGroup, ...) \
|
2017-11-03 12:09:47 +00:00
|
|
|
do { \
|
2019-08-13 11:22:58 +00:00
|
|
|
struct pciDevice dev = {.vendor = Vendor, \
|
2019-08-29 19:18:58 +00:00
|
|
|
.device = Device, \
|
|
|
|
.iommuGroup = IommuGroup, __VA_ARGS__}; \
|
2019-08-13 11:22:58 +00:00
|
|
|
if (pci_address_parse(&dev.addr, Id) < 0) \
|
|
|
|
ABORT("Unable to parse PCI address " Id); \
|
2017-11-03 12:09:47 +00:00
|
|
|
pci_device_new_from_stub(&dev); \
|
2013-10-23 13:44:40 +00:00
|
|
|
} while (0)
|
|
|
|
|
2019-08-29 19:18:58 +00:00
|
|
|
MAKE_PCI_DEVICE("0000:00:00.0", 0x8086, 0x0044, 0);
|
|
|
|
MAKE_PCI_DEVICE("0000:00:01.0", 0x8086, 0x0044, 1);
|
|
|
|
MAKE_PCI_DEVICE("0000:00:02.0", 0x8086, 0x0046, 2);
|
|
|
|
MAKE_PCI_DEVICE("0000:00:03.0", 0x8086, 0x0048, 3);
|
|
|
|
MAKE_PCI_DEVICE("0001:00:00.0", 0x1014, 0x03b9, 4, .klass = 0x060400);
|
|
|
|
MAKE_PCI_DEVICE("0001:01:00.0", 0x8086, 0x105e, 5);
|
|
|
|
MAKE_PCI_DEVICE("0001:01:00.1", 0x8086, 0x105e, 5);
|
|
|
|
MAKE_PCI_DEVICE("0005:80:00.0", 0x10b5, 0x8112, 6, .klass = 0x060400);
|
|
|
|
MAKE_PCI_DEVICE("0005:90:01.0", 0x1033, 0x0035, 7);
|
|
|
|
MAKE_PCI_DEVICE("0005:90:01.1", 0x1033, 0x0035, 7);
|
|
|
|
MAKE_PCI_DEVICE("0005:90:01.2", 0x1033, 0x00e0, 7);
|
|
|
|
MAKE_PCI_DEVICE("0005:90:01.3", 0x1033, 0x00e0, 7);
|
|
|
|
MAKE_PCI_DEVICE("0000:0a:01.0", 0x8086, 0x0047, 8);
|
|
|
|
MAKE_PCI_DEVICE("0000:0a:02.0", 0x8286, 0x0048, 8);
|
|
|
|
MAKE_PCI_DEVICE("0000:0a:03.0", 0x8386, 0x0048, 8);
|
2019-08-29 19:18:59 +00:00
|
|
|
MAKE_PCI_DEVICE("0000:06:12.0", 0x8086, 0x0047, 9);
|
|
|
|
MAKE_PCI_DEVICE("0000:06:12.1", 0x8086, 0x0047, 10,
|
|
|
|
.physfn = "0000:06:12.0"); /* Virtual Function */
|
|
|
|
MAKE_PCI_DEVICE("0000:06:12.2", 0x8086, 0x0047, 11,
|
|
|
|
.physfn = "0000:06:12.0"); /* Virtual Function */
|
|
|
|
MAKE_PCI_DEVICE("0021:de:1f.0", 0x8086, 0x0047, 12);
|
|
|
|
MAKE_PCI_DEVICE("0021:de:1f.1", 0x8086, 0x0047, 13,
|
|
|
|
.physfn = "0021:de:1f.0"); /* Virtual Function */
|
|
|
|
|
2013-10-23 13:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Mocked functions
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
int
|
|
|
|
access(const char *path, int mode)
|
|
|
|
{
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) newpath = NULL;
|
2013-10-23 13:44:40 +00:00
|
|
|
|
|
|
|
init_syms();
|
|
|
|
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
if (getrealpath(&newpath, path) < 0)
|
2019-08-13 11:50:48 +00:00
|
|
|
return -1;
|
|
|
|
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
return real_access(newpath, mode);
|
2013-10-23 13:44:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
tests: fix mocking of stat() / lstat() functions
Quite a few of the tests have a need to mock the stat() / lstat()
functions and they are taking somewhat different & inconsistent
approaches none of which are actually fully correct. This is shown
by fact that 'make check' fails on 32-bit hosts. Investigation
revealed that the code was calling into the native C library impl,
not getting intercepted by our mocks.
The POSIX stat() function might resolve to any number of different
symbols in the C library.
The may be an additional stat64() function exposed by the headers
too.
On 64-bit hosts the stat & stat64 functions are identical, always
refering to the 64-bit ABI.
On 32-bit hosts they refer to the 32-bit & 64-bit ABIs respectively.
Libvirt uses _FILE_OFFSET_BITS=64 on 32-bit hosts, which causes the
C library to transparently rewrite stat() calls to be stat64() calls.
Libvirt will never see the 32-bit ABI from the traditional stat()
call. We cannot assume this rewriting is done using a macro. It might
be, but on GLibC it is done with a magic __asm__ statement to apply
the rewrite at link time instead of at preprocessing.
In GLibC there may be two additional functions exposed by the headers,
__xstat() and __xstat64(). When these exist, stat() and stat64() are
transparently rewritten to call __xstat() and __xstat64() respectively.
The former symbols will not actally exist in the library at all, only
the header. The leading "__" indicates the symbols are a private impl
detail of the C library that applications should not care about.
Unfortunately, because we are trying to mock replace the C library,
we need to know about this internal impl detail.
With all this in mind the list of functions we have to mock will depend
on several factors
- If _FILE_OFFSET_BITS is set, then we are on a 32-bit host, and we
only need to mock stat64 and __xstat64. The other stat / __xstat
functions exist, but we'll never call them so they can be ignored
for mocking.
- If _FILE_OFFSET_BITS is not set, then we are on a 64-bit host and
we should mock stat, stat64, __xstat & __xstat64. Either may be
called by app code.
- If __xstat & __xstat64 exist, then stat & stat64 will not exist
as symbols in the library, so the latter should not be mocked.
The same all applies to lstat()
These rules are complex enough that we don't want to duplicate them
across every mock file, so this centralizes all the logic in a helper
file virmockstathelper.c that should be #included when needed. The
code merely need to provide a filename rewriting callback called
virMockStatRedirect(). Optionally VIR_MOCK_STAT_HOOK can be defined
as a macro if further processing is needed inline.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-04-01 16:47:25 +00:00
|
|
|
static int
|
|
|
|
virMockStatRedirect(const char *path, char **newpath)
|
2013-10-23 15:55:02 +00:00
|
|
|
{
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
if (getrealpath(newpath, path) < 0)
|
|
|
|
return -1;
|
|
|
|
|
tests: fix mocking of stat() / lstat() functions
Quite a few of the tests have a need to mock the stat() / lstat()
functions and they are taking somewhat different & inconsistent
approaches none of which are actually fully correct. This is shown
by fact that 'make check' fails on 32-bit hosts. Investigation
revealed that the code was calling into the native C library impl,
not getting intercepted by our mocks.
The POSIX stat() function might resolve to any number of different
symbols in the C library.
The may be an additional stat64() function exposed by the headers
too.
On 64-bit hosts the stat & stat64 functions are identical, always
refering to the 64-bit ABI.
On 32-bit hosts they refer to the 32-bit & 64-bit ABIs respectively.
Libvirt uses _FILE_OFFSET_BITS=64 on 32-bit hosts, which causes the
C library to transparently rewrite stat() calls to be stat64() calls.
Libvirt will never see the 32-bit ABI from the traditional stat()
call. We cannot assume this rewriting is done using a macro. It might
be, but on GLibC it is done with a magic __asm__ statement to apply
the rewrite at link time instead of at preprocessing.
In GLibC there may be two additional functions exposed by the headers,
__xstat() and __xstat64(). When these exist, stat() and stat64() are
transparently rewritten to call __xstat() and __xstat64() respectively.
The former symbols will not actally exist in the library at all, only
the header. The leading "__" indicates the symbols are a private impl
detail of the C library that applications should not care about.
Unfortunately, because we are trying to mock replace the C library,
we need to know about this internal impl detail.
With all this in mind the list of functions we have to mock will depend
on several factors
- If _FILE_OFFSET_BITS is set, then we are on a 32-bit host, and we
only need to mock stat64 and __xstat64. The other stat / __xstat
functions exist, but we'll never call them so they can be ignored
for mocking.
- If _FILE_OFFSET_BITS is not set, then we are on a 64-bit host and
we should mock stat, stat64, __xstat & __xstat64. Either may be
called by app code.
- If __xstat & __xstat64 exist, then stat & stat64 will not exist
as symbols in the library, so the latter should not be mocked.
The same all applies to lstat()
These rules are complex enough that we don't want to duplicate them
across every mock file, so this centralizes all the logic in a helper
file virmockstathelper.c that should be #included when needed. The
code merely need to provide a filename rewriting callback called
virMockStatRedirect(). Optionally VIR_MOCK_STAT_HOOK can be defined
as a macro if further processing is needed inline.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-04-01 16:47:25 +00:00
|
|
|
return 0;
|
2013-10-23 15:55:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-10-23 13:44:40 +00:00
|
|
|
int
|
|
|
|
open(const char *path, int flags, ...)
|
|
|
|
{
|
|
|
|
int ret;
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) newpath = NULL;
|
2013-10-23 13:44:40 +00:00
|
|
|
|
|
|
|
init_syms();
|
|
|
|
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
if (getrealpath(&newpath, path) < 0)
|
2013-10-23 13:44:40 +00:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (flags & O_CREAT) {
|
|
|
|
va_list ap;
|
|
|
|
mode_t mode;
|
|
|
|
va_start(ap, flags);
|
2018-04-30 15:30:12 +00:00
|
|
|
mode = (mode_t) va_arg(ap, int);
|
2013-10-23 13:44:40 +00:00
|
|
|
va_end(ap);
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
ret = real_open(newpath, flags, mode);
|
2013-10-23 13:44:40 +00:00
|
|
|
} else {
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
ret = real_open(newpath, flags);
|
2013-10-23 13:44:40 +00:00
|
|
|
}
|
2013-10-23 15:55:02 +00:00
|
|
|
|
|
|
|
/* Catch both: /sys/bus/pci/drivers/... and
|
|
|
|
* /sys/bus/pci/device/.../driver/... */
|
2015-12-04 13:51:08 +00:00
|
|
|
if (ret >= 0 && STRPREFIX(path, SYSFS_PCI_PREFIX) &&
|
2013-10-23 15:55:02 +00:00
|
|
|
strstr(path, "driver") && add_fd(ret, path) < 0) {
|
2016-05-13 10:32:00 +00:00
|
|
|
real_close(ret);
|
2013-10-23 15:55:02 +00:00
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
2013-10-23 13:44:40 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2019-08-15 14:37:17 +00:00
|
|
|
|
2019-08-20 10:30:08 +00:00
|
|
|
# ifdef __GLIBC__
|
2019-08-16 07:41:14 +00:00
|
|
|
/* in some cases this function may not be present in headers, so we need
|
|
|
|
* a declaration to silence the complier */
|
|
|
|
int
|
|
|
|
__open_2(const char *path, int flags);
|
|
|
|
|
2019-08-15 14:37:17 +00:00
|
|
|
int
|
|
|
|
__open_2(const char *path, int flags)
|
|
|
|
{
|
|
|
|
VIR_AUTOFREE(char *) newpath = NULL;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
init_syms();
|
|
|
|
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
if (getrealpath(&newpath, path) < 0)
|
2019-08-15 14:37:17 +00:00
|
|
|
return -1;
|
|
|
|
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
ret = real___open_2(newpath, flags);
|
2019-08-15 14:37:17 +00:00
|
|
|
|
|
|
|
/* Catch both: /sys/bus/pci/drivers/... and
|
|
|
|
* /sys/bus/pci/device/.../driver/... */
|
|
|
|
if (ret >= 0 && STRPREFIX(path, SYSFS_PCI_PREFIX) &&
|
|
|
|
strstr(path, "driver") && add_fd(ret, path) < 0) {
|
|
|
|
real_close(ret);
|
|
|
|
ret = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2019-08-20 10:30:08 +00:00
|
|
|
# endif /* ! __GLIBC__ */
|
2019-08-15 14:37:17 +00:00
|
|
|
|
2013-12-24 18:07:27 +00:00
|
|
|
DIR *
|
|
|
|
opendir(const char *path)
|
|
|
|
{
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) newpath = NULL;
|
2013-12-24 18:07:27 +00:00
|
|
|
|
|
|
|
init_syms();
|
|
|
|
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
if (getrealpath(&newpath, path) < 0)
|
2013-12-24 18:07:27 +00:00
|
|
|
return NULL;
|
|
|
|
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
return real_opendir(newpath);
|
2013-12-24 18:07:27 +00:00
|
|
|
}
|
|
|
|
|
2013-10-23 15:55:02 +00:00
|
|
|
int
|
|
|
|
close(int fd)
|
|
|
|
{
|
|
|
|
if (remove_fd(fd) < 0)
|
|
|
|
return -1;
|
2016-05-13 10:32:00 +00:00
|
|
|
return real_close(fd);
|
2013-10-23 15:55:02 +00:00
|
|
|
}
|
2018-05-03 08:01:04 +00:00
|
|
|
|
|
|
|
char *
|
|
|
|
virFileCanonicalizePath(const char *path)
|
|
|
|
{
|
2019-08-13 11:50:48 +00:00
|
|
|
VIR_AUTOFREE(char *) newpath = NULL;
|
2018-05-03 08:01:04 +00:00
|
|
|
|
|
|
|
init_syms();
|
|
|
|
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
if (getrealpath(&newpath, path) < 0)
|
2019-08-13 11:50:48 +00:00
|
|
|
return NULL;
|
2018-05-03 08:01:04 +00:00
|
|
|
|
virpcimock.c: simplify getrealpath() usage
Previous patch had to add '/sys/kernel/' prefix in opendir() because
the path, which is being mocked, wasn't being considered due to
an 'if SYSFS_PCI_PREFIX' guarding the call to getrealpath().
In fact, all current getrealpath() callers are guarding it with a
conditional to ensure that the function will never be called with
a non-mocked path. In this case, an extra non-NULL verification is
needed for the 'newpath' string to use the variable - which is
counterintuitive, given that getrealpath() will always write the
'newpath' string in any non-error conditon.
However, simply removing the guard of all getrealpath() instances
causes an abort in init_env(). This happens because tests will
execute access() to non-mocked paths even before the
LIBVIRT_FAKE_ROOT_DIR variable is declared in the test files. We
don't need 'fakerootdir' to be created at this point though.
This patch does the following changes to simplify getrealpath()
usage:
- getrealpath() will now guard the init_env() call by checking if
both fakeroot isn't created and the required path is being mocked.
This ensures that we're not failing inside init_env() because
we're too early and LIBVIRT_FAKE_ROOT_DIR wasn't defined yet;
- remove all conditional guards to call getrealpath() from
access(), virMockStatRedirect(), open(), open_2(), opendir()
and virFileCanonicalizePath(). As a bonus, remove all ternary
conditionals with 'newpath';
- a new 'pathPrefixIsMocked()' helper to aggregate all the prefixes
we're mocking, making it easier to add/remove them. If a prefix
is added inside this function, we can be sure that all functions
are mocking them.
Signed-off-by: Daniel Henrique Barboza <danielhb413@gmail.com>
Reviewed-by: Michal Privoznik <mprivozn@redhat.com>
2019-08-29 19:19:01 +00:00
|
|
|
return real_virFileCanonicalizePath(newpath);
|
2018-05-03 08:01:04 +00:00
|
|
|
}
|
tests: fix mocking of stat() / lstat() functions
Quite a few of the tests have a need to mock the stat() / lstat()
functions and they are taking somewhat different & inconsistent
approaches none of which are actually fully correct. This is shown
by fact that 'make check' fails on 32-bit hosts. Investigation
revealed that the code was calling into the native C library impl,
not getting intercepted by our mocks.
The POSIX stat() function might resolve to any number of different
symbols in the C library.
The may be an additional stat64() function exposed by the headers
too.
On 64-bit hosts the stat & stat64 functions are identical, always
refering to the 64-bit ABI.
On 32-bit hosts they refer to the 32-bit & 64-bit ABIs respectively.
Libvirt uses _FILE_OFFSET_BITS=64 on 32-bit hosts, which causes the
C library to transparently rewrite stat() calls to be stat64() calls.
Libvirt will never see the 32-bit ABI from the traditional stat()
call. We cannot assume this rewriting is done using a macro. It might
be, but on GLibC it is done with a magic __asm__ statement to apply
the rewrite at link time instead of at preprocessing.
In GLibC there may be two additional functions exposed by the headers,
__xstat() and __xstat64(). When these exist, stat() and stat64() are
transparently rewritten to call __xstat() and __xstat64() respectively.
The former symbols will not actally exist in the library at all, only
the header. The leading "__" indicates the symbols are a private impl
detail of the C library that applications should not care about.
Unfortunately, because we are trying to mock replace the C library,
we need to know about this internal impl detail.
With all this in mind the list of functions we have to mock will depend
on several factors
- If _FILE_OFFSET_BITS is set, then we are on a 32-bit host, and we
only need to mock stat64 and __xstat64. The other stat / __xstat
functions exist, but we'll never call them so they can be ignored
for mocking.
- If _FILE_OFFSET_BITS is not set, then we are on a 64-bit host and
we should mock stat, stat64, __xstat & __xstat64. Either may be
called by app code.
- If __xstat & __xstat64 exist, then stat & stat64 will not exist
as symbols in the library, so the latter should not be mocked.
The same all applies to lstat()
These rules are complex enough that we don't want to duplicate them
across every mock file, so this centralizes all the logic in a helper
file virmockstathelper.c that should be #included when needed. The
code merely need to provide a filename rewriting callback called
virMockStatRedirect(). Optionally VIR_MOCK_STAT_HOOK can be defined
as a macro if further processing is needed inline.
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2019-04-01 16:47:25 +00:00
|
|
|
|
|
|
|
# include "virmockstathelpers.c"
|
|
|
|
|
2013-10-23 13:44:40 +00:00
|
|
|
#else
|
2018-04-30 14:04:39 +00:00
|
|
|
/* Nothing to override on this platform */
|
2013-10-23 13:44:40 +00:00
|
|
|
#endif
|