#include "conf-files.h"
#include "conf-parser.h"
#include "constants.h"
+#include "creds-util.h"
#include "cryptsetup-util.h"
#include "device-util.h"
#include "devnum-util.h"
static ImagePolicy *arg_image_policy = NULL;
static Architecture arg_architecture = _ARCHITECTURE_INVALID;
static int arg_offline = -1;
+static bool arg_oem = false;
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
bool dropped;
bool factory_reset;
+ int oem;
int32_t priority;
uint32_t weight, padding_weight;
.no_auto = -1,
.read_only = -1,
.growfs = -1,
+ .oem = -1,
};
return p;
p->verity = VERITY_OFF;
}
+static bool partition_is_oem(const Partition *p) {
+ return p->oem > 0 || (p->oem < 0 && !p->factory_reset);
+}
+
static bool partition_exclude(const Partition *p) {
assert(p);
+ if (arg_oem && !partition_is_oem(p))
+ return true;
+
if (arg_filter_partitions_type == FILTER_PARTITIONS_NONE)
return false;
{ "Partition", "GrowFileSystem", config_parse_tristate, 0, &p->growfs },
{ "Partition", "SplitName", config_parse_string, 0, &p->split_name_format },
{ "Partition", "Minimize", config_parse_minimize, 0, &p->minimize },
+ { "Partition", "OEM", config_parse_tristate, 0, &p->oem },
{}
};
int r;
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Type= not defined, refusing.");
- if ((p->copy_blocks_path || p->copy_blocks_auto) &&
- (p->format || !strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)))
- return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
- "Format=/CopyFiles=/MakeDirectories= and CopyBlocks= cannot be combined, refusing.");
-
if ((!strv_isempty(p->copy_files) || !strv_isempty(p->make_directories)) && streq_ptr(p->format, "swap"))
return log_syntax(NULL, LOG_ERR, path, 1, SYNTHETIC_ERRNO(EINVAL),
"Format=swap and CopyFiles= cannot be combined, refusing.");
dev_t devno, found = 0;
int r;
- /* Enforce some security restrictions: CopyBlocks=auto should not be an avenue to get outside of the
- * --root=/--image= confinement. Specifically, refuse CopyBlocks= in combination with --root= at all,
- * and restrict block device references in the --image= case to loopback block device we set up.
- *
- * restrict_devno contain the dev_t of the loop back device we operate on in case of --image=, and
- * thus declares which device (and its partition subdevices) we shall limit access to. If
- * restrict_devno is zero no device probing access shall be allowed at all (used for --root=) and if
- * it is (dev_t) -1 then free access shall be allowed (if neither switch is used). */
-
- if (restrict_devno == 0)
- return log_error_errno(SYNTHETIC_ERRNO(EPERM),
- "Automatic discovery of backing block devices not permitted in --root= mode, refusing.");
-
/* Handles CopyBlocks=auto, and finds the right source partition to copy from. We look for matching
* partitions in the host, using the appropriate directory as key and ensuring that the partition
* type matches. */
found = devno;
}
- if (found == 0)
- return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
- "Unable to automatically discover suitable partition to copy blocks from.");
-
if (ret_devno)
*ret_devno = found;
if (ret_uuid)
*ret_uuid = found_uuid;
- return 0;
+ return found != 0;
}
static int context_open_copy_block_paths(
} else if (p->copy_blocks_auto) {
dev_t devno = 0; /* Fake initialization to appease gcc. */
+ /* Enforce some security restrictions: CopyBlocks=auto should not be an avenue to get
+ * outside of the --root=/--image= confinement. Specifically, refuse CopyBlocks= in
+ * combination with --root= at all, and restrict block device references in the
+ * --image= case to loopback block device we set up.
+ *
+ * restrict_devno contain the dev_t of the loop back device we operate on in case of
+ * --image=, and thus declares which device (and its partition subdevices) we shall
+ * limit access to. If restrict_devno is zero no device probing access shall be
+ * allowed at all (used for --root=) and if it is (dev_t) -1 then free access shall
+ * be allowed (if neither switch is used). */
+
+ if (restrict_devno == 0) {
+ if (!p->format && strv_isempty(p->copy_files) && strv_isempty(p->make_directories))
+ return log_error_errno(SYNTHETIC_ERRNO(EPERM),
+ "Automatic discovery of backing block devices not permitted in --root= mode, refusing.");
+
+ continue;
+ }
+
r = resolve_copy_blocks_auto(p->type, p->copy_blocks_root, restrict_devno, &devno, &uuid);
if (r < 0)
return r;
+ if (r == 0) {
+ if (!p->format && strv_isempty(p->copy_files) && strv_isempty(p->make_directories))
+ return log_error_errno(SYNTHETIC_ERRNO(ENXIO),
+ "Unable to automatically discover suitable partition to copy blocks from.");
+
+ continue;
+ }
assert(devno != 0);
source_fd = r = device_open_from_devnum(S_IFBLK, devno, O_RDONLY|O_CLOEXEC|O_NONBLOCK, &opened);
if (!p->format)
continue;
+ if (p->copy_blocks_fd >= 0)
+ continue;
+
if (p->minimize == MINIMIZE_OFF)
continue;
if (fstype_is_ro(p->format)) {
struct stat st;
- if (stat(temp, &st) < 0)
+ assert(fd < 0);
+
+ fd = open(temp, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open temporary file %s: %m", temp);
+
+ if (fstat(fd, &st) < 0)
return log_error_errno(errno, "Failed to stat temporary file: %m");
log_info("Minimal partition size of %s filesystem of partition %s is %s",
p->copy_blocks_path = TAKE_PTR(temp);
p->copy_blocks_path_is_our_file = true;
+ p->copy_blocks_fd = TAKE_FD(fd);
+ p->copy_blocks_size = st.st_size;
continue;
}
return r;
}
+ assert(fd >= 0);
+
p->copy_blocks_path = TAKE_PTR(temp);
p->copy_blocks_path_is_our_file = true;
+ p->copy_blocks_fd = TAKE_FD(fd);
+ p->copy_blocks_size = fsz;
}
/* Now that we've done the data partitions, do the verity hash partitions. We do these in a separate
LIST_FOREACH(partitions, p, context->partitions) {
_cleanup_(unlink_and_freep) char *temp = NULL;
_cleanup_free_ char *hint = NULL;
+ _cleanup_close_ int fd = -EBADF;
struct stat st;
Partition *dp;
if (r < 0)
return r;
- if (stat(temp, &st) < 0)
+ fd = open(temp, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
+ if (fd < 0)
+ return log_error_errno(errno, "Failed to open temporary file %s: %m", temp);
+
+ if (fstat(fd, &st) < 0)
return log_error_errno(r, "Failed to stat temporary file: %m");
log_info("Minimal partition size of verity hash partition %s is %s",
p->copy_blocks_path = TAKE_PTR(temp);
p->copy_blocks_path_is_our_file = true;
+ p->copy_blocks_fd = TAKE_FD(fd);
+ p->copy_blocks_size = st.st_size;
}
return 0;
" --sector-size=SIZE Set the logical sector size for the image\n"
" --architecture=ARCH Set the generic architecture for the image\n"
" --offline=BOOL Whether to build the image offline\n"
+ " --oem=BOOL Whether to only include OEM partitions\n"
"\nSee the %s for details.\n",
program_invocation_short_name,
ansi_highlight(),
return 0;
}
+static int parse_credentials(void) {
+ int r;
+
+ r = read_credential_bool("repart.oem");
+ if (r < 0)
+ return log_error_errno(r, "Failed to read repart.oem credential: %m");
+ arg_oem = r;
+
+ return 0;
+}
+
static int parse_argv(int argc, char *argv[]) {
enum {
ARG_SKIP_PARTITIONS,
ARG_ARCHITECTURE,
ARG_OFFLINE,
+ ARG_OEM,
};
static const struct option options[] = {
{ "sector-size", required_argument, NULL, ARG_SECTOR_SIZE },
{ "architecture", required_argument, NULL, ARG_ARCHITECTURE },
{ "offline", required_argument, NULL, ARG_OFFLINE },
+ { "oem", required_argument, NULL, ARG_OEM },
{}
};
break;
+ case ARG_OEM:
+ r = parse_boolean_argument("--oem=", optarg, &arg_oem);
+ if (r < 0)
+ return r;
+
+ break;
+
case '?':
return -EINVAL;
log_parse_environment();
log_open();
+ r = parse_credentials();
+ if (r < 0)
+ return r;
+
r = parse_argv(argc, argv);
if (r <= 0)
return r;
if (r < 0)
return r;
- /* We might have gotten more copy blocks paths to open during the minimize process, so let's make
- * sure we open those as well. These should all be regular files, so don't allow any block devices. */
- r = context_open_copy_block_paths(context, 0);
- if (r < 0)
- return r;
-
if (arg_size_auto) {
r = determine_auto_size(context);
if (r < 0)
int ifindex;
int iflink;
int iftype;
- int vf_representor_id;
- const char *phys_port_name;
} LinkInfo;
/* skip intermediate virtio devices */
return -ENOENT;
}
-static bool is_valid_onboard_index(unsigned long idx) {
+static int get_dev_port(sd_device *dev, bool fallback_to_dev_id, unsigned *ret) {
+ unsigned v;
+ int r;
+
+ assert(dev);
+ assert(ret);
+
+ /* Get kernel provided port index for the case when multiple ports on a single PCI function. */
+
+ r = device_get_sysattr_unsigned(dev, "dev_port", &v);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ /* Found a positive index. Let's use it. */
+ *ret = v;
+ return 1; /* positive */
+ }
+ assert(v == 0);
+
+ /* With older kernels IP-over-InfiniBand network interfaces sometimes erroneously provide the port
+ * number in the 'dev_id' sysfs attribute instead of 'dev_port', which thus stays initialized as 0. */
+
+ if (fallback_to_dev_id) {
+ unsigned iftype;
+
+ r = device_get_sysattr_unsigned(dev, "type", &iftype);
+ if (r < 0)
+ return r;
+
+ fallback_to_dev_id = (iftype == ARPHRD_INFINIBAND);
+ }
+
+ if (fallback_to_dev_id)
+ return device_get_sysattr_unsigned(dev, "dev_id", ret);
+
+ /* Otherwise, return the original index 0. */
+ *ret = 0;
+ return 0; /* zero */
+}
+
+static int get_port_specifier(sd_device *dev, bool fallback_to_dev_id, char **ret) {
+ const char *phys_port_name;
+ unsigned dev_port;
+ char *buf;
+ int r;
+
+ assert(dev);
+ assert(ret);
+
+ /* First, try to use the kernel provided front panel port name for multiple port PCI device. */
+ r = sd_device_get_sysattr_value(dev, "phys_port_name", &phys_port_name);
+ if (r >= 0 && !isempty(phys_port_name)) {
+ if (naming_scheme_has(NAMING_SR_IOV_R)) {
+ int vf_id = -1;
+
+ /* Check if phys_port_name indicates virtual device representor. */
+ (void) sscanf(phys_port_name, "pf%*uvf%d", &vf_id);
+
+ if (vf_id >= 0) {
+ /* For VF representor append 'r<VF_NUM>'. */
+ if (asprintf(&buf, "r%d", vf_id) < 0)
+ return -ENOMEM;
+
+ *ret = buf;
+ return 1;
+ }
+ }
+
+ /* Otherwise, use phys_port_name as is. */
+ if (asprintf(&buf, "n%s", phys_port_name) < 0)
+ return -ENOMEM;
+
+ *ret = buf;
+ return 1;
+ }
+
+ /* Then, try to use the kernel provided port index for the case when multiple ports on a single PCI
+ * function. */
+ r = get_dev_port(dev, fallback_to_dev_id, &dev_port);
+ if (r < 0)
+ return r;
+ if (r > 0) {
+ assert(dev_port > 0);
+ if (asprintf(&buf, "d%u", dev_port) < 0)
+ return -ENOMEM;
+
+ *ret = buf;
+ return 1;
+ }
+
+ *ret = NULL;
+ return 0;
+}
+
+static bool is_valid_onboard_index(unsigned idx) {
/* Some BIOSes report rubbish indexes that are excessively high (2^24-1 is an index VMware likes to
* report for example). Let's define a cut-off where we don't consider the index reliable anymore. We
* pick some arbitrary cut-off, which is somewhere beyond the realistic number of physical network
return idx <= (naming_scheme_has(NAMING_16BIT_INDEX) ? ONBOARD_16BIT_INDEX_MAX : ONBOARD_14BIT_INDEX_MAX);
}
-/* retrieve on-board index number and label from firmware */
-static int dev_pci_onboard(sd_device *dev, const LinkInfo *info, NetNames *names) {
- unsigned long idx, dev_port = 0;
- const char *attr;
- size_t l;
- char *s;
+static int pci_get_onboard_index(sd_device *dev, unsigned *ret) {
+ unsigned idx;
int r;
assert(dev);
- assert(info);
- assert(names);
+ assert(ret);
/* ACPI _DSM — device specific method for naming a PCI or PCI Express device */
- if (sd_device_get_sysattr_value(names->pcidev, "acpi_index", &attr) >= 0)
- log_device_debug(names->pcidev, "acpi_index=%s", attr);
- else {
+ r = device_get_sysattr_unsigned(dev, "acpi_index", &idx);
+ if (r < 0)
/* SMBIOS type 41 — Onboard Devices Extended Information */
- r = sd_device_get_sysattr_value(names->pcidev, "index", &attr);
- if (r < 0)
- return r;
- log_device_debug(names->pcidev, "index=%s", attr);
- }
-
- r = safe_atolu(attr, &idx);
+ r = device_get_sysattr_unsigned(dev, "index", &idx);
if (r < 0)
- return log_device_debug_errno(names->pcidev, r,
- "Failed to parse onboard index \"%s\": %m", attr);
+ return r;
+
if (idx == 0 && !naming_scheme_has(NAMING_ZERO_ACPI_INDEX))
- return log_device_debug_errno(names->pcidev, SYNTHETIC_ERRNO(EINVAL),
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(EINVAL),
"Naming scheme does not allow onboard index==0.");
if (!is_valid_onboard_index(idx))
- return log_device_debug_errno(names->pcidev, SYNTHETIC_ERRNO(ENOENT),
- "Not a valid onboard index: %lu", idx);
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENOENT),
+ "Not a valid onboard index: %u", idx);
- /* kernel provided port index for multiple ports on a single PCI function */
- if (sd_device_get_sysattr_value(dev, "dev_port", &attr) >= 0) {
- r = safe_atolu_full(attr, 10, &dev_port);
- if (r < 0)
- log_device_debug_errno(dev, r, "Failed to parse dev_port, ignoring: %m");
- log_device_debug(dev, "dev_port=%lu", dev_port);
- }
+ *ret = idx;
+ return 0;
+}
- s = names->pci_onboard;
- l = sizeof(names->pci_onboard);
- l = strpcpyf(&s, l, "o%lu", idx);
- if (naming_scheme_has(NAMING_SR_IOV_R) && info->vf_representor_id >= 0)
- /* For VF representor append 'r<VF_NUM>' and not phys_port_name */
- l = strpcpyf(&s, l, "r%d", info->vf_representor_id);
- else if (!isempty(info->phys_port_name))
- /* kernel provided front panel port name for multiple port PCI device */
- l = strpcpyf(&s, l, "n%s", info->phys_port_name);
- else if (dev_port > 0)
- l = strpcpyf(&s, l, "d%lu", dev_port);
- if (l == 0)
+static int dev_pci_onboard(sd_device *dev, const LinkInfo *info, NetNames *names) {
+ _cleanup_free_ char *port = NULL;
+ unsigned idx = 0; /* avoid false maybe-uninitialized warning */
+ int r;
+
+ assert(dev);
+ assert(info);
+ assert(names);
+
+ /* retrieve on-board index number and label from firmware */
+ r = pci_get_onboard_index(names->pcidev, &idx);
+ if (r < 0)
+ return r;
+
+ r = get_port_specifier(dev, /* fallback_to_dev_id = */ false, &port);
+ if (r < 0)
+ return r;
+
+ if (!snprintf_ok(names->pci_onboard, sizeof(names->pci_onboard), "o%u%s", idx, strempty(port)))
names->pci_onboard[0] = '\0';
- log_device_debug(dev, "Onboard index identifier: index=%lu phys_port=%s dev_port=%lu %s %s",
- idx, strempty(info->phys_port_name), dev_port,
+
+ log_device_debug(dev, "Onboard index identifier: index=%u port=%s %s %s",
+ idx, strna(port),
special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), empty_to_na(names->pci_onboard));
if (sd_device_get_sysattr_value(names->pcidev, "label", &names->pci_onboard_label) >= 0)
return 1; /* Found. We should ignore domain part. */
}
-static int dev_pci_slot(sd_device *dev, const LinkInfo *info, NetNames *names) {
- const char *sysname, *attr;
- _cleanup_(sd_device_unrefp) sd_device *pci = NULL;
- _cleanup_closedir_ DIR *dir = NULL;
- unsigned domain, bus, slot, func;
- sd_device *hotplug_slot_dev;
- unsigned long dev_port = 0;
- uint32_t hotplug_slot = 0;
- size_t l;
- char *s;
+static int pci_get_hotplug_slot_from_address(
+ sd_device *dev,
+ sd_device *pci,
+ DIR *dir,
+ uint32_t *ret) {
+
+ const char *sysname;
int r;
assert(dev);
- assert(info);
- assert(names);
+ assert(pci);
+ assert(dir);
+ assert(ret);
- r = sd_device_get_sysname(names->pcidev, &sysname);
+ r = sd_device_get_sysname(dev, &sysname);
if (r < 0)
- return log_device_debug_errno(names->pcidev, r, "Failed to get sysname: %m");
+ return log_device_debug_errno(dev, r, "Failed to get sysname: %m");
- r = sscanf(sysname, "%x:%x:%x.%u", &domain, &bus, &slot, &func);
- log_device_debug(dev, "Parsing slot information from PCI device sysname \"%s\": %s",
- sysname, r == 4 ? "success" : "failure");
- if (r != 4)
- return -ENOENT;
+ rewinddir(dir);
+ FOREACH_DIRENT_ALL(de, dir, break) {
+ _cleanup_free_ char *path = NULL;
+ const char *address;
+ uint32_t slot;
- if (naming_scheme_has(NAMING_NPAR_ARI) &&
- is_pci_ari_enabled(names->pcidev))
- /* ARI devices support up to 256 functions on a single device ("slot"), and interpret the
- * traditional 5-bit slot and 3-bit function number as a single 8-bit function number,
- * where the slot makes up the upper 5 bits. */
- func += slot * 8;
+ if (dot_or_dot_dot(de->d_name))
+ continue;
+
+ if (de->d_type != DT_DIR)
+ continue;
- /* kernel provided port index for multiple ports on a single PCI function */
- if (sd_device_get_sysattr_value(dev, "dev_port", &attr) >= 0) {
- log_device_debug(dev, "dev_port=%s", attr);
+ r = safe_atou32(de->d_name, &slot);
+ if (r < 0 || slot <= 0)
+ continue;
- r = safe_atolu_full(attr, 10, &dev_port);
- if (r < 0)
- log_device_debug_errno(dev, r, "Failed to parse attribute dev_port, ignoring: %m");
+ path = path_join("slots", de->d_name, "address");
+ if (!path)
+ return -ENOMEM;
- /* With older kernels IP-over-InfiniBand network interfaces sometimes erroneously
- * provide the port number in the 'dev_id' sysfs attribute instead of 'dev_port',
- * which thus stays initialized as 0. */
- if (dev_port == 0 &&
- info->iftype == ARPHRD_INFINIBAND &&
- sd_device_get_sysattr_value(dev, "dev_id", &attr) >= 0) {
- log_device_debug(dev, "dev_id=%s", attr);
+ if (sd_device_get_sysattr_value(pci, path, &address) < 0)
+ continue;
- r = safe_atolu_full(attr, 10, &dev_port);
- if (r < 0)
- log_device_debug_errno(dev, r, "Failed to parse attribute dev_id, ignoring: %m");
- }
+ /* match slot address with device by stripping the function */
+ if (!startswith(sysname, address))
+ continue;
+
+ *ret = slot;
+ return 1; /* found */
}
- /* compose a name based on the raw kernel's PCI bus, slot numbers */
- s = names->pci_path;
- l = sizeof(names->pci_path);
- if (domain > 0)
- l = strpcpyf(&s, l, "P%u", domain);
- l = strpcpyf(&s, l, "p%us%u", bus, slot);
- if (func > 0 || is_pci_multifunction(names->pcidev) > 0)
- l = strpcpyf(&s, l, "f%u", func);
- if (naming_scheme_has(NAMING_SR_IOV_R) && info->vf_representor_id >= 0)
- /* For VF representor append 'r<VF_NUM>' and not phys_port_name */
- l = strpcpyf(&s, l, "r%d", info->vf_representor_id);
- else if (!isempty(info->phys_port_name))
- /* kernel provided front panel port name for multi-port PCI device */
- l = strpcpyf(&s, l, "n%s", info->phys_port_name);
- else if (dev_port > 0)
- l = strpcpyf(&s, l, "d%lu", dev_port);
- if (l == 0)
- names->pci_path[0] = '\0';
+ *ret = 0;
+ return 0; /* not found */
+}
- log_device_debug(dev, "PCI path identifier: domain=%u bus=%u slot=%u func=%u phys_port=%s dev_port=%lu %s %s",
- domain, bus, slot, func, strempty(info->phys_port_name), dev_port,
- special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), empty_to_na(names->pci_path));
+static int pci_get_hotplug_slot(sd_device *dev, uint32_t *ret) {
+ _cleanup_(sd_device_unrefp) sd_device *pci = NULL;
+ _cleanup_closedir_ DIR *dir = NULL;
+ int r;
+
+ assert(dev);
+ assert(ret);
/* ACPI _SUN — slot user number */
r = sd_device_new_from_subsystem_sysname(&pci, "subsystem", "pci");
if (r < 0)
- return log_debug_errno(r, "sd_device_new_from_subsystem_sysname() failed: %m");
+ return log_debug_errno(r, "Failed to create sd_device object for pci subsystem: %m");
r = device_opendir(pci, "slots", &dir);
if (r < 0)
- return log_device_debug_errno(dev, r, "Cannot access 'slots' subdirectory: %m");
+ return log_device_debug_errno(dev, r, "Cannot open 'slots' subdirectory: %m");
+
+ for (sd_device *slot_dev = dev; slot_dev; ) {
+ uint32_t slot = 0; /* avoid false maybe-uninitialized warning */
- hotplug_slot_dev = names->pcidev;
- while (hotplug_slot_dev) {
- r = parse_hotplug_slot_from_function_id(hotplug_slot_dev, dirfd(dir), &hotplug_slot);
+ r = parse_hotplug_slot_from_function_id(slot_dev, dirfd(dir), &slot);
if (r < 0)
- return 0;
+ return r;
if (r > 0) {
- domain = 0; /* See comments in parse_hotplug_slot_from_function_id(). */
- break;
+ *ret = slot;
+ return 1; /* domain should be ignored. */
}
- r = sd_device_get_sysname(hotplug_slot_dev, &sysname);
+ r = pci_get_hotplug_slot_from_address(slot_dev, pci, dir, &slot);
if (r < 0)
- return log_device_debug_errno(hotplug_slot_dev, r, "Failed to get sysname: %m");
+ return r;
+ if (r > 0) {
+ /* We found the match between PCI device and slot. However, we won't use the slot
+ * index if the device is a PCI bridge, because it can have other child devices that
+ * will try to claim the same index and that would create name collision. */
+ if (naming_scheme_has(NAMING_BRIDGE_NO_SLOT) && is_pci_bridge(slot_dev)) {
+ if (naming_scheme_has(NAMING_BRIDGE_MULTIFUNCTION_SLOT) && is_pci_multifunction(dev) <= 0)
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ESTALE),
+ "Not using slot information because the PCI device associated with "
+ "the hotplug slot is a bridge and the PCI device has a single function.");
+
+ if (!naming_scheme_has(NAMING_BRIDGE_MULTIFUNCTION_SLOT))
+ return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ESTALE),
+ "Not using slot information because the PCI device is a bridge.");
+ }
- FOREACH_DIRENT_ALL(de, dir, break) {
- _cleanup_free_ char *path = NULL;
- const char *address;
- uint32_t i;
+ *ret = slot;
+ return 0; /* domain can be still used. */
+ }
- if (dot_or_dot_dot(de->d_name))
- continue;
+ if (sd_device_get_parent_with_subsystem_devtype(slot_dev, "pci", NULL, &slot_dev) < 0)
+ break;
+ }
- r = safe_atou32(de->d_name, &i);
- if (r < 0 || i <= 0)
- continue;
+ return -ENOENT;
+}
- path = path_join("slots", de->d_name, "address");
- if (!path)
- return -ENOMEM;
+static int get_pci_slot_specifiers(
+ sd_device *dev,
+ char **ret_domain,
+ char **ret_bus_and_slot,
+ char **ret_func) {
- if (sd_device_get_sysattr_value(pci, path, &address) < 0)
- continue;
+ _cleanup_free_ char *domain_spec = NULL, *bus_and_slot_spec = NULL, *func_spec = NULL;
+ unsigned domain, bus, slot, func;
+ const char *sysname;
+ int r;
- /* match slot address with device by stripping the function */
- if (!startswith(sysname, address))
- continue;
+ assert(dev);
+ assert(ret_domain);
+ assert(ret_bus_and_slot);
+ assert(ret_func);
- hotplug_slot = i;
+ r = sd_device_get_sysname(dev, &sysname);
+ if (r < 0)
+ return log_device_debug_errno(dev, r, "Failed to get sysname: %m");
- /* We found the match between PCI device and slot. However, we won't use the slot
- * index if the device is a PCI bridge, because it can have other child devices that
- * will try to claim the same index and that would create name collision. */
- if (naming_scheme_has(NAMING_BRIDGE_NO_SLOT) && is_pci_bridge(hotplug_slot_dev)) {
- if (naming_scheme_has(NAMING_BRIDGE_MULTIFUNCTION_SLOT) && is_pci_multifunction(names->pcidev) <= 0) {
- log_device_debug(dev,
- "Not using slot information because the PCI device associated with "
- "the hotplug slot is a bridge and the PCI device has a single function.");
- return 0;
- }
-
- if (!naming_scheme_has(NAMING_BRIDGE_MULTIFUNCTION_SLOT)) {
- log_device_debug(dev, "Not using slot information because the PCI device is a bridge.");
- return 0;
- }
- }
+ r = sscanf(sysname, "%x:%x:%x.%u", &domain, &bus, &slot, &func);
+ log_device_debug(dev, "Parsing slot information from PCI device sysname \"%s\": %s",
+ sysname, r == 4 ? "success" : "failure");
+ if (r != 4)
+ return -EINVAL;
- break;
- }
- if (hotplug_slot > 0)
- break;
- if (sd_device_get_parent_with_subsystem_devtype(hotplug_slot_dev, "pci", NULL, &hotplug_slot_dev) < 0)
- break;
- rewinddir(dir);
- }
+ if (naming_scheme_has(NAMING_NPAR_ARI) &&
+ is_pci_ari_enabled(dev))
+ /* ARI devices support up to 256 functions on a single device ("slot"), and interpret the
+ * traditional 5-bit slot and 3-bit function number as a single 8-bit function number,
+ * where the slot makes up the upper 5 bits. */
+ func += slot * 8;
- if (hotplug_slot > 0) {
- s = names->pci_slot;
- l = sizeof(names->pci_slot);
- if (domain > 0)
- l = strpcpyf(&s, l, "P%u", domain);
- l = strpcpyf(&s, l, "s%"PRIu32, hotplug_slot);
- if (func > 0 || is_pci_multifunction(names->pcidev) > 0)
- l = strpcpyf(&s, l, "f%u", func);
- if (naming_scheme_has(NAMING_SR_IOV_R) && info->vf_representor_id >= 0)
- /* For VF representor append 'r<VF_NUM>' and not phys_port_name */
- l = strpcpyf(&s, l, "r%d", info->vf_representor_id);
- else if (!isempty(info->phys_port_name))
- l = strpcpyf(&s, l, "n%s", info->phys_port_name);
- else if (dev_port > 0)
- l = strpcpyf(&s, l, "d%lu", dev_port);
- if (l == 0)
- names->pci_slot[0] = '\0';
-
- log_device_debug(dev, "Slot identifier: domain=%u slot=%"PRIu32" func=%u phys_port=%s dev_port=%lu %s %s",
- domain, hotplug_slot, func, strempty(info->phys_port_name), dev_port,
- special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), empty_to_na(names->pci_slot));
- }
+ if (domain > 0 && asprintf(&domain_spec, "P%u", domain) < 0)
+ return -ENOMEM;
+
+ if (asprintf(&bus_and_slot_spec, "p%us%u", bus, slot) < 0)
+ return -ENOMEM;
+
+ if ((func > 0 || is_pci_multifunction(dev) > 0) &&
+ asprintf(&func_spec, "f%u", func) < 0)
+ return -ENOMEM;
+
+ *ret_domain = TAKE_PTR(domain_spec);
+ *ret_bus_and_slot = TAKE_PTR(bus_and_slot_spec);
+ *ret_func = TAKE_PTR(func_spec);
+ return 0;
+}
+
+static int dev_pci_slot(sd_device *dev, const LinkInfo *info, NetNames *names) {
+ _cleanup_free_ char *domain = NULL, *bus_and_slot = NULL, *func = NULL, *port = NULL;
+ uint32_t hotplug_slot = 0; /* avoid false maybe-uninitialized warning */
+ int r;
+
+ assert(dev);
+ assert(info);
+ assert(names);
+
+ r = get_pci_slot_specifiers(names->pcidev, &domain, &bus_and_slot, &func);
+ if (r < 0)
+ return r;
+
+ r = get_port_specifier(dev, /* fallback_to_dev_id = */ true, &port);
+ if (r < 0)
+ return r;
+
+ /* compose a name based on the raw kernel's PCI bus, slot numbers */
+ if (!snprintf_ok(names->pci_path, sizeof(names->pci_path), "%s%s%s%s",
+ strempty(domain), bus_and_slot, strempty(func), strempty(port)))
+ names->pci_path[0] = '\0';
+
+ log_device_debug(dev, "PCI path identifier: domain=%s bus_and_slot=%s func=%s port=%s %s %s",
+ strna(domain), bus_and_slot, strna(func), strna(port),
+ special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), empty_to_na(names->pci_path));
+
+ r = pci_get_hotplug_slot(names->pcidev, &hotplug_slot);
+ if (r < 0)
+ return r;
+ if (r > 0)
+ /* If the hotplug slot is found through the function ID, then drop the domain from the name.
+ * See comments in parse_hotplug_slot_from_function_id(). */
+ domain = mfree(domain);
+
+ if (!snprintf_ok(names->pci_slot, sizeof(names->pci_slot), "%ss%"PRIu32"%s%s",
+ strempty(domain), hotplug_slot, strempty(func), strempty(port)))
+ names->pci_slot[0] = '\0';
+
+ log_device_debug(dev, "Slot identifier: domain=%s slot=%"PRIu32" func=%s port=%s %s %s",
+ strna(domain), hotplug_slot, strna(func), strna(port),
+ special_glyph(SPECIAL_GLYPH_ARROW_RIGHT), empty_to_na(names->pci_slot));
return 0;
}
if (r < 0)
return r;
- r = sd_device_get_sysattr_value(dev, "phys_port_name", &info->phys_port_name);
- if (r >= 0)
- /* Check if phys_port_name indicates virtual device representor */
- (void) sscanf(info->phys_port_name, "pf%*uvf%d", &info->vf_representor_id);
-
return 0;
}
sd_device *dev = ASSERT_PTR(ASSERT_PTR(event)->dev);
const char *prefix;
NetNames names = {};
- LinkInfo info = {
- .vf_representor_id = -1,
- };
+ LinkInfo info = {};
int r;
r = get_link_info(dev, &info);