Merge pull request #24731 from yuwata/sd-device-opendir

sd-device: introduce device_opendir()
This commit is contained in:
Daan De Meyer
2022-09-19 17:06:38 +02:00
committed by GitHub
3 changed files with 112 additions and 78 deletions

View File

@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <dirent.h>
#include <inttypes.h>
#include <stdbool.h>
#include <sys/stat.h>
@@ -14,6 +15,8 @@ int device_new_from_mode_and_devnum(sd_device **ret, mode_t mode, dev_t devnum);
int device_new_from_nulstr(sd_device **ret, char *nulstr, size_t len);
int device_new_from_strv(sd_device **ret, char **strv);
int device_opendir(sd_device *device, const char *subdir, DIR **ret);
int device_get_property_bool(sd_device *device, const char *key);
int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value);
int device_get_sysattr_bool(sd_device *device, const char *sysattr);

View File

@@ -1866,42 +1866,32 @@ _public_ const char *sd_device_get_property_next(sd_device *device, const char *
return key;
}
static int device_sysattrs_read_all_internal(sd_device *device, const char *subdir) {
_cleanup_free_ char *path_dir = NULL;
static int device_sysattrs_read_all_internal(sd_device *device, const char *subdir, Set **stack) {
_cleanup_closedir_ DIR *dir = NULL;
const char *syspath;
int r;
r = sd_device_get_syspath(device, &syspath);
assert(device);
assert(stack);
r = device_opendir(device, subdir, &dir);
if (r == -ENOENT && subdir)
return 0; /* Maybe, this is a child device, and is already removed. */
if (r < 0)
return r;
if (subdir) {
_cleanup_free_ char *p = NULL;
p = path_join(syspath, subdir, "uevent");
if (!p)
return -ENOMEM;
if (access(p, F_OK) >= 0)
/* this is a child device, skipping */
return 0;
if (faccessat(dirfd(dir), "uevent", F_OK, 0) >= 0)
return 0; /* this is a child device, skipping */
if (errno != ENOENT) {
log_device_debug_errno(device, errno, "sd-device: Failed to stat %s, ignoring subdir: %m", p);
log_device_debug_errno(device, errno,
"sd-device: Failed to access %s/uevent, ignoring sub-directory %s: %m",
subdir, subdir);
return 0;
}
path_dir = path_join(syspath, subdir);
if (!path_dir)
return -ENOMEM;
}
dir = opendir(path_dir ?: syspath);
if (!dir)
return -errno;
FOREACH_DIRENT_ALL(de, dir, return -errno) {
_cleanup_free_ char *path = NULL, *p = NULL;
_cleanup_free_ char *p = NULL;
struct stat statbuf;
if (dot_or_dot_dot(de->d_name))
@@ -1918,25 +1908,27 @@ static int device_sysattrs_read_all_internal(sd_device *device, const char *subd
}
if (de->d_type == DT_DIR) {
/* read subdirectory */
r = device_sysattrs_read_all_internal(device, p ?: de->d_name);
/* push the sub-directory into the stack, and read it later. */
if (p)
r = set_ensure_consume(stack, &path_hash_ops_free, TAKE_PTR(p));
else
r = set_put_strdup_full(stack, &path_hash_ops_free, de->d_name);
if (r < 0)
return r;
continue;
}
path = path_join(syspath, p ?: de->d_name);
if (!path)
return -ENOMEM;
if (lstat(path, &statbuf) != 0)
if (fstatat(dirfd(dir), de->d_name, &statbuf, AT_SYMLINK_NOFOLLOW) < 0)
continue;
if ((statbuf.st_mode & (S_IRUSR | S_IWUSR)) == 0)
continue;
r = set_put_strdup(&device->sysattrs, p ?: de->d_name);
if (p)
r = set_ensure_consume(&device->sysattrs, &path_hash_ops_free, TAKE_PTR(p));
else
r = set_put_strdup_full(&device->sysattrs, &path_hash_ops_free, de->d_name);
if (r < 0)
return r;
}
@@ -1945,6 +1937,7 @@ static int device_sysattrs_read_all_internal(sd_device *device, const char *subd
}
static int device_sysattrs_read_all(sd_device *device) {
_cleanup_set_free_ Set *stack = NULL;
int r;
assert(device);
@@ -1952,10 +1945,22 @@ static int device_sysattrs_read_all(sd_device *device) {
if (device->sysattrs_read)
return 0;
r = device_sysattrs_read_all_internal(device, NULL);
r = device_sysattrs_read_all_internal(device, NULL, &stack);
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *subdir = NULL;
subdir = set_steal_first(stack);
if (!subdir)
break;
r = device_sysattrs_read_all_internal(device, subdir, &stack);
if (r < 0)
return r;
}
device->sysattrs_read = true;
return 0;
@@ -2453,3 +2458,33 @@ _public_ int sd_device_open(sd_device *device, int flags) {
return TAKE_FD(fd2);
}
int device_opendir(sd_device *device, const char *subdir, DIR **ret) {
_cleanup_closedir_ DIR *d = NULL;
_cleanup_free_ char *path = NULL;
const char *syspath;
int r;
assert(device);
assert(ret);
r = sd_device_get_syspath(device, &syspath);
if (r < 0)
return r;
if (subdir) {
if (!path_is_safe(subdir))
return -EINVAL;
path = path_join(syspath, subdir);
if (!path)
return -ENOMEM;
}
d = opendir(path ?: syspath);
if (!d)
return -errno;
*ret = TAKE_PTR(d);
return 0;
}

View File

@@ -24,6 +24,7 @@
#include "alloc-util.h"
#include "chase-symlinks.h"
#include "device-private.h"
#include "device-util.h"
#include "dirent-util.h"
#include "fd-util.h"
@@ -98,7 +99,7 @@ static sd_device *skip_virtio(sd_device *dev) {
static int get_virtfn_info(sd_device *pcidev, sd_device **ret_physfn_pcidev, char **ret_suffix) {
_cleanup_(sd_device_unrefp) sd_device *physfn_pcidev = NULL;
const char *physfn_syspath, *syspath;
const char *syspath;
_cleanup_closedir_ DIR *dir = NULL;
int r;
@@ -115,15 +116,11 @@ static int get_virtfn_info(sd_device *pcidev, sd_device **ret_physfn_pcidev, cha
if (r < 0)
return r;
r = sd_device_get_syspath(physfn_pcidev, &physfn_syspath);
/* Find the virtual function number by finding the right virtfn link. */
r = device_opendir(physfn_pcidev, NULL, &dir);
if (r < 0)
return r;
/* Find the virtual function number by finding the right virtfn link. */
dir = opendir(physfn_syspath);
if (!dir)
return -errno;
FOREACH_DIRENT_ALL(de, dir, break) {
_cleanup_(sd_device_unrefp) sd_device *virtfn_pcidev = NULL;
const char *n, *s;
@@ -285,9 +282,9 @@ static bool is_pci_bridge(sd_device *dev) {
return b;
}
static int parse_hotplug_slot_from_function_id(sd_device *dev, const char *slots, uint32_t *ret) {
static int parse_hotplug_slot_from_function_id(sd_device *dev, int slots_dirfd, uint32_t *ret) {
uint64_t function_id;
char path[PATH_MAX];
char filename[NAME_MAX+1];
const char *attr;
int r;
@@ -300,7 +297,7 @@ static int parse_hotplug_slot_from_function_id(sd_device *dev, const char *slots
* between PCI function and its hotplug slot. */
assert(dev);
assert(slots);
assert(slots_dirfd >= 0);
assert(ret);
if (!naming_scheme_has(NAMING_SLOT_FUNCTION_ID))
@@ -318,27 +315,27 @@ static int parse_hotplug_slot_from_function_id(sd_device *dev, const char *slots
"Invalid function id (0x%"PRIx64"), ignoring.",
function_id);
if (!snprintf_ok(path, sizeof path, "%s/%08"PRIx64, slots, function_id))
if (!snprintf_ok(filename, sizeof(filename), "%08"PRIx64, function_id))
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENAMETOOLONG),
"PCI slot path is too long, ignoring.");
if (access(path, F_OK) < 0)
return log_device_debug_errno(dev, errno, "Cannot access %s, ignoring: %m", path);
if (faccessat(slots_dirfd, filename, F_OK, 0) < 0)
return log_device_debug_errno(dev, errno, "Cannot access %s under pci slots, ignoring: %m", filename);
*ret = (uint32_t) function_id;
return 1;
}
static int dev_pci_slot(sd_device *dev, const LinkInfo *info, NetNames *names) {
const char *sysname, *attr, *syspath;
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;
char slots[PATH_MAX], *s;
size_t l;
char *s;
int r;
assert(dev);
@@ -409,21 +406,13 @@ static int dev_pci_slot(sd_device *dev, const LinkInfo *info, NetNames *names) {
if (r < 0)
return log_debug_errno(r, "sd_device_new_from_subsystem_sysname() failed: %m");
r = sd_device_get_syspath(pci, &syspath);
r = device_opendir(pci, "slots", &dir);
if (r < 0)
return log_device_debug_errno(pci, r, "sd_device_get_syspath() failed: %m");
if (!snprintf_ok(slots, sizeof slots, "%s/slots", syspath))
return log_device_debug_errno(dev, SYNTHETIC_ERRNO(ENAMETOOLONG),
"Cannot access %s/slots: %m", syspath);
dir = opendir(slots);
if (!dir)
return log_device_debug_errno(dev, errno, "Cannot access %s: %m", slots);
return log_device_debug_errno(dev, r, "Cannot access 'slots' subdirectory: %m");
hotplug_slot_dev = names->pcidev;
while (hotplug_slot_dev) {
r = parse_hotplug_slot_from_function_id(hotplug_slot_dev, slots, &hotplug_slot);
r = parse_hotplug_slot_from_function_id(hotplug_slot_dev, dirfd(dir), &hotplug_slot);
if (r < 0)
return 0;
if (r > 0) {
@@ -436,8 +425,8 @@ static int dev_pci_slot(sd_device *dev, const LinkInfo *info, NetNames *names) {
return log_device_debug_errno(hotplug_slot_dev, r, "Failed to get sysname: %m");
FOREACH_DIRENT_ALL(de, dir, break) {
_cleanup_free_ char *address = NULL;
char str[PATH_MAX];
_cleanup_free_ char *path = NULL;
const char *address;
uint32_t i;
if (dot_or_dot_dot(de->d_name))
@@ -447,30 +436,37 @@ static int dev_pci_slot(sd_device *dev, const LinkInfo *info, NetNames *names) {
if (r < 0 || i <= 0)
continue;
path = path_join("slots", de->d_name, "address");
if (!path)
return -ENOMEM;
if (sd_device_get_sysattr_value(pci, path, &address) < 0)
continue;
/* match slot address with device by stripping the function */
if (snprintf_ok(str, sizeof str, "%s/%s/address", slots, de->d_name) &&
read_one_line_file(str, &address) >= 0 &&
startswith(sysname, address)) {
hotplug_slot = i;
if (!startswith(sysname, address))
continue;
/* 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 single function.");
return 0;
}
hotplug_slot = i;
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;
}
/* 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;
}
break;
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;
}
}
break;
}
if (hotplug_slot > 0)
break;