merge: branch 'systemd' into ff/fix_systemd
Disabled unused code and adapted Makefile and meson files.
This commit is contained in:
@@ -2371,7 +2371,10 @@ src_libnm_systemd_shared_libnm_systemd_shared_la_SOURCES = \
|
||||
src/libnm-systemd-shared/src/basic/btrfs.c \
|
||||
src/libnm-systemd-shared/src/basic/btrfs.h \
|
||||
src/libnm-systemd-shared/src/basic/cgroup-util.h \
|
||||
src/libnm-systemd-shared/src/basic/chase.h \
|
||||
src/libnm-systemd-shared/src/basic/constants.h \
|
||||
src/libnm-systemd-shared/src/basic/devnum-util.c \
|
||||
src/libnm-systemd-shared/src/basic/devnum-util.h \
|
||||
src/libnm-systemd-shared/src/basic/dns-def.h \
|
||||
src/libnm-systemd-shared/src/basic/env-file.c \
|
||||
src/libnm-systemd-shared/src/basic/env-file.h \
|
||||
@@ -2538,7 +2541,11 @@ src_libnm_systemd_core_libnm_systemd_core_la_SOURCES = \
|
||||
src/libnm-systemd-core/src/libsystemd-network/network-common.h \
|
||||
src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-client.c \
|
||||
src/libnm-systemd-core/src/libsystemd-network/sd-dhcp6-lease.c \
|
||||
src/libnm-systemd-core/src/libsystemd/sd-device/device-internal.h \
|
||||
src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c \
|
||||
src/libnm-systemd-core/src/libsystemd/sd-device/device-private.h \
|
||||
src/libnm-systemd-core/src/libsystemd/sd-device/device-util.h \
|
||||
src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c \
|
||||
src/libnm-systemd-core/src/libsystemd/sd-event/event-source.h \
|
||||
src/libnm-systemd-core/src/libsystemd/sd-event/event-util.c \
|
||||
src/libnm-systemd-core/src/libsystemd/sd-event/event-util.h \
|
||||
|
@@ -10,6 +10,8 @@ libnm_systemd_core = static_library(
|
||||
'src/libsystemd-network/sd-dhcp-duid.c',
|
||||
'src/libsystemd-network/sd-dhcp6-client.c',
|
||||
'src/libsystemd-network/sd-dhcp6-lease.c',
|
||||
'src/libsystemd/sd-device/device-private.c',
|
||||
'src/libsystemd/sd-device/sd-device.c',
|
||||
'src/libsystemd/sd-event/event-util.c',
|
||||
'src/libsystemd/sd-event/sd-event.c',
|
||||
'src/libsystemd/sd-id128/id128-util.c',
|
||||
|
@@ -3,7 +3,9 @@
|
||||
#include "nm-sd-adapt-core.h"
|
||||
|
||||
#include <linux/if.h>
|
||||
#ifdef __GLIBC__
|
||||
#include <linux/if_arp.h>
|
||||
#endif
|
||||
|
||||
#include "arphrd-util.h"
|
||||
#include "device-util.h"
|
||||
|
@@ -7,7 +7,9 @@
|
||||
|
||||
#include <errno.h>
|
||||
#include <sys/ioctl.h>
|
||||
#ifdef __GLIBC__
|
||||
#include <linux/if_arp.h>
|
||||
#endif
|
||||
#include <linux/if_infiniband.h>
|
||||
|
||||
#include "sd-dhcp6-client.h"
|
||||
|
@@ -0,0 +1,117 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "sd-device.h"
|
||||
|
||||
#include "device-private.h"
|
||||
#include "hashmap.h"
|
||||
#include "set.h"
|
||||
#include "time-util.h"
|
||||
|
||||
#define LATEST_UDEV_DATABASE_VERSION 1
|
||||
|
||||
struct sd_device {
|
||||
unsigned n_ref;
|
||||
|
||||
/* The database version indicates the supported features by the udev database.
|
||||
* This is saved and parsed in V field.
|
||||
*
|
||||
* 0: None of the following features are supported (systemd version <= 246).
|
||||
* 1: The current tags (Q) and the database version (V) features are implemented (>= 247).
|
||||
*/
|
||||
unsigned database_version;
|
||||
|
||||
sd_device *parent;
|
||||
|
||||
OrderedHashmap *properties;
|
||||
Iterator properties_iterator;
|
||||
uint64_t properties_generation; /* changes whenever the properties are changed */
|
||||
uint64_t properties_iterator_generation; /* generation when iteration was started */
|
||||
|
||||
/* the subset of the properties that should be written to the db */
|
||||
OrderedHashmap *properties_db;
|
||||
|
||||
Hashmap *sysattr_values; /* cached sysattr values */
|
||||
|
||||
Set *sysattrs; /* names of sysattrs */
|
||||
Iterator sysattrs_iterator;
|
||||
|
||||
Set *all_tags, *current_tags;
|
||||
Iterator all_tags_iterator, current_tags_iterator;
|
||||
uint64_t all_tags_iterator_generation, current_tags_iterator_generation; /* generation when iteration was started */
|
||||
uint64_t tags_generation; /* changes whenever the tags are changed */
|
||||
|
||||
Set *devlinks;
|
||||
Iterator devlinks_iterator;
|
||||
uint64_t devlinks_generation; /* changes whenever the devlinks are changed */
|
||||
uint64_t devlinks_iterator_generation; /* generation when iteration was started */
|
||||
int devlink_priority;
|
||||
|
||||
Hashmap *children;
|
||||
Iterator children_iterator;
|
||||
bool children_enumerated;
|
||||
|
||||
int ifindex;
|
||||
char *devtype;
|
||||
char *devname;
|
||||
dev_t devnum;
|
||||
|
||||
char **properties_strv; /* the properties hashmap as a strv */
|
||||
char *properties_nulstr; /* the same as a nulstr */
|
||||
size_t properties_nulstr_len;
|
||||
|
||||
char *syspath;
|
||||
const char *devpath;
|
||||
const char *sysnum;
|
||||
char *sysname;
|
||||
|
||||
char *subsystem;
|
||||
char *driver_subsystem; /* only set for the 'drivers' subsystem */
|
||||
char *driver;
|
||||
|
||||
char *device_id;
|
||||
|
||||
usec_t usec_initialized;
|
||||
|
||||
mode_t devmode;
|
||||
uid_t devuid;
|
||||
gid_t devgid;
|
||||
|
||||
uint64_t diskseq; /* Block device sequence number, monothonically incremented by the kernel on create/attach */
|
||||
|
||||
/* only set when device is passed through netlink */
|
||||
sd_device_action_t action;
|
||||
uint64_t seqnum;
|
||||
|
||||
bool parent_set:1; /* no need to try to reload parent */
|
||||
bool sysattrs_read:1; /* don't try to re-read sysattrs once read */
|
||||
bool property_tags_outdated:1; /* need to update TAGS= or CURRENT_TAGS= property */
|
||||
bool property_devlinks_outdated:1; /* need to update DEVLINKS= property */
|
||||
bool properties_buf_outdated:1; /* need to reread hashmap */
|
||||
bool subsystem_set:1; /* don't reread subsystem */
|
||||
bool driver_set:1; /* don't reread driver */
|
||||
bool uevent_loaded:1; /* don't reread uevent */
|
||||
bool db_loaded; /* don't reread db */
|
||||
|
||||
bool is_initialized:1;
|
||||
bool sealed:1; /* don't read more information from uevent/db */
|
||||
bool db_persist:1; /* don't clean up the db when switching from initrd to real root */
|
||||
};
|
||||
|
||||
int device_new_aux(sd_device **ret);
|
||||
int device_add_property_aux(sd_device *device, const char *key, const char *value, bool db);
|
||||
static inline int device_add_property_internal(sd_device *device, const char *key, const char *value) {
|
||||
return device_add_property_aux(device, key, value, false);
|
||||
}
|
||||
|
||||
int device_set_syspath(sd_device *device, const char *_syspath, bool verify);
|
||||
int device_set_ifindex(sd_device *device, const char *ifindex);
|
||||
int device_set_devmode(sd_device *device, const char *devmode);
|
||||
int device_set_devname(sd_device *device, const char *devname);
|
||||
int device_set_devtype(sd_device *device, const char *devtype);
|
||||
int device_set_devnum(sd_device *device, const char *major, const char *minor);
|
||||
int device_set_subsystem(sd_device *device, const char *subsystem);
|
||||
int device_set_diskseq(sd_device *device, const char *str);
|
||||
int device_set_drivers_subsystem(sd_device *device);
|
||||
int device_set_driver(sd_device *device, const char *driver);
|
||||
int device_set_usec_initialized(sd_device *device, usec_t when);
|
962
src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
Normal file
962
src/libnm-systemd-core/src/libsystemd/sd-device/device-private.c
Normal file
@@ -0,0 +1,962 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "nm-sd-adapt-core.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <net/if.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-device.h"
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "device-internal.h"
|
||||
#include "device-private.h"
|
||||
#include "device-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "hashmap.h"
|
||||
#include "macro.h"
|
||||
#include "mkdir.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "set.h"
|
||||
#include "stdio-util.h"
|
||||
#include "string-table.h"
|
||||
#include "string-util.h"
|
||||
#include "strv.h"
|
||||
#include "strxcpyx.h"
|
||||
#include "tmpfile-util.h"
|
||||
#include "user-util.h"
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int device_add_property(sd_device *device, const char *key, const char *value) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(key);
|
||||
|
||||
r = device_add_property_aux(device, key, value, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (key[0] != '.') {
|
||||
r = device_add_property_aux(device, key, value, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_add_propertyf(sd_device *device, const char *key, const char *format, ...) {
|
||||
_cleanup_free_ char *value = NULL;
|
||||
va_list ap;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(key);
|
||||
|
||||
if (!format)
|
||||
return device_add_property(device, key, NULL);
|
||||
|
||||
va_start(ap, format);
|
||||
r = vasprintf(&value, format, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (r < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return device_add_property(device, key, value);
|
||||
}
|
||||
|
||||
void device_set_devlink_priority(sd_device *device, int priority) {
|
||||
assert(device);
|
||||
|
||||
device->devlink_priority = priority;
|
||||
}
|
||||
|
||||
void device_set_is_initialized(sd_device *device) {
|
||||
assert(device);
|
||||
|
||||
device->is_initialized = true;
|
||||
}
|
||||
|
||||
int device_ensure_usec_initialized(sd_device *device, sd_device *device_old) {
|
||||
usec_t when;
|
||||
|
||||
assert(device);
|
||||
|
||||
if (device_old && device_old->usec_initialized > 0)
|
||||
when = device_old->usec_initialized;
|
||||
else
|
||||
when = now(CLOCK_MONOTONIC);
|
||||
|
||||
return device_set_usec_initialized(device, when);
|
||||
}
|
||||
|
||||
uint64_t device_get_properties_generation(sd_device *device) {
|
||||
assert(device);
|
||||
|
||||
return device->properties_generation;
|
||||
}
|
||||
|
||||
uint64_t device_get_tags_generation(sd_device *device) {
|
||||
assert(device);
|
||||
|
||||
return device->tags_generation;
|
||||
}
|
||||
|
||||
uint64_t device_get_devlinks_generation(sd_device *device) {
|
||||
assert(device);
|
||||
|
||||
return device->devlinks_generation;
|
||||
}
|
||||
|
||||
int device_get_devnode_mode(sd_device *device, mode_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
r = device_read_db(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (device->devmode == MODE_INVALID)
|
||||
return -ENOENT;
|
||||
|
||||
if (ret)
|
||||
*ret = device->devmode;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_get_devnode_uid(sd_device *device, uid_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
r = device_read_db(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (device->devuid == UID_INVALID)
|
||||
return -ENOENT;
|
||||
|
||||
if (ret)
|
||||
*ret = device->devuid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_set_devuid(sd_device *device, const char *uid) {
|
||||
uid_t u;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(uid);
|
||||
|
||||
r = parse_uid(uid, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = device_add_property_internal(device, "DEVUID", uid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
device->devuid = u;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_get_devnode_gid(sd_device *device, gid_t *ret) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
r = device_read_db(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (device->devgid == GID_INVALID)
|
||||
return -ENOENT;
|
||||
|
||||
if (ret)
|
||||
*ret = device->devgid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_set_devgid(sd_device *device, const char *gid) {
|
||||
gid_t g;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(gid);
|
||||
|
||||
r = parse_gid(gid, &g);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = device_add_property_internal(device, "DEVGID", gid);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
device->devgid = g;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_set_action(sd_device *device, sd_device_action_t a) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(a >= 0 && a < _SD_DEVICE_ACTION_MAX);
|
||||
|
||||
r = device_add_property_internal(device, "ACTION", device_action_to_string(a));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
device->action = a;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_set_action_from_string(sd_device *device, const char *action) {
|
||||
sd_device_action_t a;
|
||||
|
||||
assert(device);
|
||||
assert(action);
|
||||
|
||||
a = device_action_from_string(action);
|
||||
if (a < 0)
|
||||
return a;
|
||||
|
||||
return device_set_action(device, a);
|
||||
}
|
||||
|
||||
static int device_set_seqnum(sd_device *device, const char *str) {
|
||||
uint64_t seqnum;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(str);
|
||||
|
||||
r = safe_atou64(str, &seqnum);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (seqnum == 0)
|
||||
return -EINVAL;
|
||||
|
||||
r = device_add_property_internal(device, "SEQNUM", str);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
device->seqnum = seqnum;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_amend(sd_device *device, const char *key, const char *value) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(key);
|
||||
assert(value);
|
||||
|
||||
if (streq(key, "DEVPATH")) {
|
||||
char *path;
|
||||
|
||||
path = strjoina("/sys", value);
|
||||
|
||||
/* the caller must verify or trust this data (e.g., if it comes from the kernel) */
|
||||
r = device_set_syspath(device, path, false);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set syspath to '%s': %m", path);
|
||||
} else if (streq(key, "SUBSYSTEM")) {
|
||||
r = device_set_subsystem(device, value);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem to '%s': %m", value);
|
||||
} else if (streq(key, "DEVTYPE")) {
|
||||
r = device_set_devtype(device, value);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set devtype to '%s': %m", value);
|
||||
} else if (streq(key, "DEVNAME")) {
|
||||
r = device_set_devname(device, value);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set devname to '%s': %m", value);
|
||||
} else if (streq(key, "USEC_INITIALIZED")) {
|
||||
usec_t t;
|
||||
|
||||
r = safe_atou64(value, &t);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to parse timestamp '%s': %m", value);
|
||||
|
||||
r = device_set_usec_initialized(device, t);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set usec-initialized to '%s': %m", value);
|
||||
} else if (streq(key, "DRIVER")) {
|
||||
r = device_set_driver(device, value);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set driver to '%s': %m", value);
|
||||
} else if (streq(key, "IFINDEX")) {
|
||||
r = device_set_ifindex(device, value);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set ifindex to '%s': %m", value);
|
||||
} else if (streq(key, "DEVMODE")) {
|
||||
r = device_set_devmode(device, value);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set devmode to '%s': %m", value);
|
||||
} else if (streq(key, "DEVUID")) {
|
||||
r = device_set_devuid(device, value);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set devuid to '%s': %m", value);
|
||||
} else if (streq(key, "DEVGID")) {
|
||||
r = device_set_devgid(device, value);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set devgid to '%s': %m", value);
|
||||
} else if (streq(key, "ACTION")) {
|
||||
r = device_set_action_from_string(device, value);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set action to '%s': %m", value);
|
||||
} else if (streq(key, "SEQNUM")) {
|
||||
r = device_set_seqnum(device, value);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set SEQNUM to '%s': %m", value);
|
||||
} else if (streq(key, "DISKSEQ")) {
|
||||
r = device_set_diskseq(device, value);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set DISKSEQ to '%s': %m", value);
|
||||
} else if (streq(key, "DEVLINKS")) {
|
||||
for (const char *p = value;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
|
||||
/* udev rules may set escaped strings, and sd-device does not modify the input
|
||||
* strings. So, it is also necessary to keep the strings received through
|
||||
* sd-device-monitor. */
|
||||
r = extract_first_word(&p, &word, NULL, EXTRACT_RETAIN_ESCAPE);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
|
||||
r = device_add_devlink(device, word);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to add devlink '%s': %m", word);
|
||||
}
|
||||
} else if (STR_IN_SET(key, "TAGS", "CURRENT_TAGS")) {
|
||||
for (const char *p = value;;) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
|
||||
r = extract_first_word(&p, &word, ":", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
break;
|
||||
if (isempty(word))
|
||||
continue;
|
||||
|
||||
r = device_add_tag(device, word, streq(key, "CURRENT_TAGS"));
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to add tag '%s': %m", word);
|
||||
}
|
||||
} else if (streq(key, "UDEV_DATABASE_VERSION")) {
|
||||
r = safe_atou(value, &device->database_version);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to parse udev database version '%s': %m", value);
|
||||
} else {
|
||||
r = device_add_property_internal(device, key, value);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to add property '%s=%s': %m", key, value);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_append(
|
||||
sd_device *device,
|
||||
char *key,
|
||||
const char **_major,
|
||||
const char **_minor) {
|
||||
|
||||
const char *major = NULL, *minor = NULL;
|
||||
char *value;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(key);
|
||||
assert(_major);
|
||||
assert(_minor);
|
||||
|
||||
value = strchr(key, '=');
|
||||
if (!value)
|
||||
return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
|
||||
"sd-device: Not a key-value pair: '%s'", key);
|
||||
|
||||
*value = '\0';
|
||||
|
||||
value++;
|
||||
|
||||
if (streq(key, "MAJOR"))
|
||||
major = value;
|
||||
else if (streq(key, "MINOR"))
|
||||
minor = value;
|
||||
else {
|
||||
r = device_amend(device, key, value);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (major)
|
||||
*_major = major;
|
||||
|
||||
if (minor)
|
||||
*_minor = minor;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void device_seal(sd_device *device) {
|
||||
assert(device);
|
||||
|
||||
device->sealed = true;
|
||||
}
|
||||
|
||||
static int device_verify(sd_device *device) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
if (!device->devpath || !device->subsystem || device->action < 0 || device->seqnum == 0)
|
||||
return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
|
||||
"sd-device: Device created from strv or nulstr lacks devpath, subsystem, action or seqnum.");
|
||||
|
||||
if (streq(device->subsystem, "drivers")) {
|
||||
r = device_set_drivers_subsystem(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
device->sealed = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_new_from_strv(sd_device **ret, char **strv) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
|
||||
const char *major = NULL, *minor = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(strv);
|
||||
|
||||
r = device_new_aux(&device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
STRV_FOREACH(key, strv) {
|
||||
r = device_append(device, *key, &major, &minor);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (major) {
|
||||
r = device_set_devnum(device, major, minor);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor);
|
||||
}
|
||||
|
||||
r = device_verify(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_new_from_nulstr(sd_device **ret, char *nulstr, size_t len) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *device = NULL;
|
||||
const char *major = NULL, *minor = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
assert(nulstr);
|
||||
assert(len);
|
||||
|
||||
r = device_new_aux(&device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
for (size_t i = 0; i < len; ) {
|
||||
char *key;
|
||||
const char *end;
|
||||
|
||||
key = nulstr + i;
|
||||
end = memchr(key, '\0', len - i);
|
||||
if (!end)
|
||||
return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL),
|
||||
"sd-device: Failed to parse nulstr");
|
||||
|
||||
i += end - key + 1;
|
||||
|
||||
/* netlink messages for some devices contain an unwanted newline at the end of value.
|
||||
* Let's drop the newline and remaining characters after the newline. */
|
||||
truncate_nl(key);
|
||||
|
||||
r = device_append(device, key, &major, &minor);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (major) {
|
||||
r = device_set_devnum(device, major, minor);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r, "sd-device: Failed to set devnum %s:%s: %m", major, minor);
|
||||
}
|
||||
|
||||
r = device_verify(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int device_update_properties_bufs(sd_device *device) {
|
||||
_cleanup_free_ char **buf_strv = NULL, *buf_nulstr = NULL;
|
||||
size_t nulstr_len = 0, num = 0;
|
||||
|
||||
assert(device);
|
||||
|
||||
if (!device->properties_buf_outdated)
|
||||
return 0;
|
||||
|
||||
/* append udev database version */
|
||||
buf_nulstr = newdup(char, "UDEV_DATABASE_VERSION=" STRINGIFY(LATEST_UDEV_DATABASE_VERSION) "\0",
|
||||
STRLEN("UDEV_DATABASE_VERSION=" STRINGIFY(LATEST_UDEV_DATABASE_VERSION)) + 2);
|
||||
if (!buf_nulstr)
|
||||
return -ENOMEM;
|
||||
|
||||
nulstr_len += STRLEN("UDEV_DATABASE_VERSION=" STRINGIFY(LATEST_UDEV_DATABASE_VERSION)) + 1;
|
||||
num++;
|
||||
|
||||
FOREACH_DEVICE_PROPERTY(device, prop, val) {
|
||||
size_t len = 0;
|
||||
|
||||
len = strlen(prop) + 1 + strlen(val);
|
||||
|
||||
buf_nulstr = GREEDY_REALLOC0(buf_nulstr, nulstr_len + len + 2);
|
||||
if (!buf_nulstr)
|
||||
return -ENOMEM;
|
||||
|
||||
strscpyl(buf_nulstr + nulstr_len, len + 1, prop, "=", val, NULL);
|
||||
nulstr_len += len + 1;
|
||||
num++;
|
||||
}
|
||||
|
||||
/* build buf_strv from buf_nulstr */
|
||||
buf_strv = new0(char*, num + 1);
|
||||
if (!buf_strv)
|
||||
return -ENOMEM;
|
||||
|
||||
size_t i = 0;
|
||||
NULSTR_FOREACH(p, buf_nulstr)
|
||||
buf_strv[i++] = p;
|
||||
assert(i == num);
|
||||
|
||||
free_and_replace(device->properties_nulstr, buf_nulstr);
|
||||
device->properties_nulstr_len = nulstr_len;
|
||||
free_and_replace(device->properties_strv, buf_strv);
|
||||
|
||||
device->properties_buf_outdated = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_get_properties_nulstr(sd_device *device, const char **ret_nulstr, size_t *ret_len) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
r = device_update_properties_bufs(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_nulstr)
|
||||
*ret_nulstr = device->properties_nulstr;
|
||||
if (ret_len)
|
||||
*ret_len = device->properties_nulstr_len;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_get_properties_strv(sd_device *device, char ***ret) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
r = device_update_properties_bufs(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = device->properties_strv;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_get_devlink_priority(sd_device *device, int *ret) {
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
r = device_read_db(device);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret)
|
||||
*ret = device->devlink_priority;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_clone_with_db(sd_device *device, sd_device **ret) {
|
||||
_cleanup_(sd_device_unrefp) sd_device *dest = NULL;
|
||||
const char *key, *val;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(ret);
|
||||
|
||||
/* The device may be already removed. Let's copy minimal set of information that was obtained through
|
||||
* netlink socket. */
|
||||
|
||||
r = device_new_aux(&dest);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Seal device to prevent reading the uevent file, as the device may have been already removed. */
|
||||
dest->sealed = true;
|
||||
|
||||
/* Copy syspath, then also devname, sysname or sysnum can be obtained. */
|
||||
r = device_set_syspath(dest, device->syspath, false);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Copy other information stored in database. Here, do not use FOREACH_DEVICE_PROPERTY() and
|
||||
* sd_device_get_property_value(), as they calls device_properties_prepare() ->
|
||||
* device_read_uevent_file(), but as commented in the above, the device may be already removed and
|
||||
* reading uevent file may fail. */
|
||||
ORDERED_HASHMAP_FOREACH_KEY(val, key, device->properties) {
|
||||
if (streq(key, "MINOR"))
|
||||
continue;
|
||||
|
||||
if (streq(key, "MAJOR")) {
|
||||
const char *minor = NULL;
|
||||
|
||||
minor = ordered_hashmap_get(device->properties, "MINOR");
|
||||
r = device_set_devnum(dest, val, minor);
|
||||
} else
|
||||
r = device_amend(dest, key, val);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (streq(key, "SUBSYSTEM") && streq(val, "drivers")) {
|
||||
r = free_and_strdup(&dest->driver_subsystem, device->driver_subsystem);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally, read the udev database. */
|
||||
r = device_read_db_internal(dest, /* force = */ true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(dest);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void device_cleanup_tags(sd_device *device) {
|
||||
assert(device);
|
||||
|
||||
device->all_tags = set_free_free(device->all_tags);
|
||||
device->current_tags = set_free_free(device->current_tags);
|
||||
device->property_tags_outdated = true;
|
||||
device->tags_generation++;
|
||||
}
|
||||
|
||||
void device_cleanup_devlinks(sd_device *device) {
|
||||
assert(device);
|
||||
|
||||
set_free_free(device->devlinks);
|
||||
device->devlinks = NULL;
|
||||
device->property_devlinks_outdated = true;
|
||||
device->devlinks_generation++;
|
||||
}
|
||||
|
||||
void device_remove_tag(sd_device *device, const char *tag) {
|
||||
assert(device);
|
||||
assert(tag);
|
||||
|
||||
free(set_remove(device->current_tags, tag));
|
||||
device->property_tags_outdated = true;
|
||||
device->tags_generation++;
|
||||
}
|
||||
|
||||
static int device_tag(sd_device *device, const char *tag, bool add) {
|
||||
const char *id;
|
||||
char *path;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(tag);
|
||||
|
||||
r = device_get_device_id(device, &id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
path = strjoina("/run/udev/tags/", tag, "/", id);
|
||||
|
||||
if (add)
|
||||
return touch_file(path, true, USEC_INFINITY, UID_INVALID, GID_INVALID, 0444);
|
||||
|
||||
if (unlink(path) < 0 && errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_tag_index(sd_device *device, sd_device *device_old, bool add) {
|
||||
int r = 0, k;
|
||||
|
||||
if (add && device_old)
|
||||
/* delete possible left-over tags */
|
||||
FOREACH_DEVICE_TAG(device_old, tag)
|
||||
if (!sd_device_has_tag(device, tag)) {
|
||||
k = device_tag(device_old, tag, false);
|
||||
if (r >= 0 && k < 0)
|
||||
r = k;
|
||||
}
|
||||
|
||||
FOREACH_DEVICE_TAG(device, tag) {
|
||||
k = device_tag(device, tag, add);
|
||||
if (r >= 0 && k < 0)
|
||||
r = k;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static bool device_has_info(sd_device *device) {
|
||||
assert(device);
|
||||
|
||||
if (!set_isempty(device->devlinks))
|
||||
return true;
|
||||
|
||||
if (device->devlink_priority != 0)
|
||||
return true;
|
||||
|
||||
if (!ordered_hashmap_isempty(device->properties_db))
|
||||
return true;
|
||||
|
||||
if (!set_isempty(device->all_tags))
|
||||
return true;
|
||||
|
||||
if (!set_isempty(device->current_tags))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool device_should_have_db(sd_device *device) {
|
||||
assert(device);
|
||||
|
||||
if (device_has_info(device))
|
||||
return true;
|
||||
|
||||
if (major(device->devnum) != 0)
|
||||
return true;
|
||||
|
||||
if (device->ifindex != 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void device_set_db_persist(sd_device *device) {
|
||||
assert(device);
|
||||
|
||||
device->db_persist = true;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
static int device_get_db_path(sd_device *device, char **ret) {
|
||||
const char *id;
|
||||
char *path;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
assert(ret);
|
||||
|
||||
r = device_get_device_id(device, &id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
path = path_join("/run/udev/data/", id);
|
||||
if (!path)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = path;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int device_has_db(sd_device *device) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
r = device_get_db_path(device, &path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return access(path, F_OK) >= 0;
|
||||
}
|
||||
|
||||
int device_update_db(sd_device *device) {
|
||||
_cleanup_(unlink_and_freep) char *path = NULL, *path_tmp = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
/* do not store anything for otherwise empty devices */
|
||||
if (!device_should_have_db(device))
|
||||
return device_delete_db(device);
|
||||
|
||||
r = device_get_db_path(device, &path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* write a database file */
|
||||
r = mkdir_parents(path, 0755);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r,
|
||||
"sd-device: Failed to create parent directories of '%s': %m",
|
||||
path);
|
||||
|
||||
r = fopen_temporary(path, &f, &path_tmp);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r,
|
||||
"sd-device: Failed to create temporary file for database file '%s': %m",
|
||||
path);
|
||||
|
||||
/* set 'sticky' bit to indicate that we should not clean the database when we transition from initrd
|
||||
* to the real root */
|
||||
if (fchmod(fileno(f), device->db_persist ? 01644 : 0644) < 0)
|
||||
return log_device_debug_errno(device, errno,
|
||||
"sd-device: Failed to chmod temporary database file '%s': %m",
|
||||
path_tmp);
|
||||
|
||||
if (device_has_info(device)) {
|
||||
const char *property, *value, *ct;
|
||||
|
||||
if (major(device->devnum) > 0) {
|
||||
FOREACH_DEVICE_DEVLINK(device, devlink)
|
||||
fprintf(f, "S:%s\n", devlink + STRLEN("/dev/"));
|
||||
|
||||
if (device->devlink_priority != 0)
|
||||
fprintf(f, "L:%i\n", device->devlink_priority);
|
||||
}
|
||||
|
||||
if (device->usec_initialized > 0)
|
||||
fprintf(f, "I:"USEC_FMT"\n", device->usec_initialized);
|
||||
|
||||
ORDERED_HASHMAP_FOREACH_KEY(value, property, device->properties_db)
|
||||
fprintf(f, "E:%s=%s\n", property, value);
|
||||
|
||||
FOREACH_DEVICE_TAG(device, tag)
|
||||
fprintf(f, "G:%s\n", tag); /* Any tag */
|
||||
|
||||
SET_FOREACH(ct, device->current_tags)
|
||||
fprintf(f, "Q:%s\n", ct); /* Current tag */
|
||||
|
||||
/* Always write the latest database version here, instead of the value stored in
|
||||
* device->database_version, as which may be 0. */
|
||||
fputs("V:" STRINGIFY(LATEST_UDEV_DATABASE_VERSION) "\n", f);
|
||||
}
|
||||
|
||||
r = fflush_and_check(f);
|
||||
if (r < 0)
|
||||
return log_device_debug_errno(device, r,
|
||||
"sd-device: Failed to flush temporary database file '%s': %m",
|
||||
path_tmp);
|
||||
|
||||
if (rename(path_tmp, path) < 0)
|
||||
return log_device_debug_errno(device, errno,
|
||||
"sd-device: Failed to rename temporary database file '%s' to '%s': %m",
|
||||
path_tmp, path);
|
||||
|
||||
log_device_debug(device, "sd-device: Created database file '%s' for '%s'.", path, device->devpath);
|
||||
|
||||
path_tmp = mfree(path_tmp);
|
||||
path = mfree(path);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_delete_db(sd_device *device) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
r = device_get_db_path(device, &path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (unlink(path) < 0 && errno != ENOENT)
|
||||
return -errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int device_read_db_internal(sd_device *device, bool force) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
int r;
|
||||
|
||||
assert(device);
|
||||
|
||||
if (device->db_loaded || (!force && device->sealed))
|
||||
return 0;
|
||||
|
||||
r = device_get_db_path(device, &path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return device_read_db_internal_filename(device, path);
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
static const char* const device_action_table[_SD_DEVICE_ACTION_MAX] = {
|
||||
[SD_DEVICE_ADD] = "add",
|
||||
[SD_DEVICE_REMOVE] = "remove",
|
||||
[SD_DEVICE_CHANGE] = "change",
|
||||
[SD_DEVICE_MOVE] = "move",
|
||||
[SD_DEVICE_ONLINE] = "online",
|
||||
[SD_DEVICE_OFFLINE] = "offline",
|
||||
[SD_DEVICE_BIND] = "bind",
|
||||
[SD_DEVICE_UNBIND] = "unbind",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(device_action, sd_device_action_t);
|
||||
|
||||
void dump_device_action_table(void) {
|
||||
DUMP_STRING_TABLE(device_action, sd_device_action_t, _SD_DEVICE_ACTION_MAX);
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
@@ -0,0 +1,77 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <dirent.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "sd-device.h"
|
||||
|
||||
#include "macro.h"
|
||||
|
||||
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_property_int(sd_device *device, const char *key, int *ret);
|
||||
int device_get_sysattr_int(sd_device *device, const char *sysattr, int *ret_value);
|
||||
int device_get_sysattr_unsigned(sd_device *device, const char *sysattr, unsigned *ret_value);
|
||||
int device_get_sysattr_u32(sd_device *device, const char *sysattr, uint32_t *ret_value);
|
||||
int device_get_sysattr_bool(sd_device *device, const char *sysattr);
|
||||
int device_get_device_id(sd_device *device, const char **ret);
|
||||
int device_get_devlink_priority(sd_device *device, int *ret);
|
||||
int device_get_devnode_mode(sd_device *device, mode_t *ret);
|
||||
int device_get_devnode_uid(sd_device *device, uid_t *ret);
|
||||
int device_get_devnode_gid(sd_device *device, gid_t *ret);
|
||||
|
||||
void device_clear_sysattr_cache(sd_device *device);
|
||||
int device_cache_sysattr_value(sd_device *device, const char *key, char *value);
|
||||
int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value);
|
||||
|
||||
void device_seal(sd_device *device);
|
||||
void device_set_is_initialized(sd_device *device);
|
||||
void device_set_db_persist(sd_device *device);
|
||||
void device_set_devlink_priority(sd_device *device, int priority);
|
||||
int device_ensure_usec_initialized(sd_device *device, sd_device *device_old);
|
||||
int device_add_devlink(sd_device *device, const char *devlink);
|
||||
int device_remove_devlink(sd_device *device, const char *devlink);
|
||||
bool device_has_devlink(sd_device *device, const char *devlink);
|
||||
int device_add_property(sd_device *device, const char *property, const char *value);
|
||||
int device_add_propertyf(sd_device *device, const char *key, const char *format, ...) _printf_(3, 4);
|
||||
int device_add_tag(sd_device *device, const char *tag, bool both);
|
||||
void device_remove_tag(sd_device *device, const char *tag);
|
||||
void device_cleanup_tags(sd_device *device);
|
||||
void device_cleanup_devlinks(sd_device *device);
|
||||
|
||||
uint64_t device_get_properties_generation(sd_device *device);
|
||||
uint64_t device_get_tags_generation(sd_device *device);
|
||||
uint64_t device_get_devlinks_generation(sd_device *device);
|
||||
|
||||
int device_properties_prepare(sd_device *device);
|
||||
int device_get_properties_nulstr(sd_device *device, const char **ret_nulstr, size_t *ret_len);
|
||||
int device_get_properties_strv(sd_device *device, char ***ret);
|
||||
|
||||
int device_clone_with_db(sd_device *device, sd_device **ret);
|
||||
|
||||
int device_tag_index(sd_device *dev, sd_device *dev_old, bool add);
|
||||
bool device_should_have_db(sd_device *device);
|
||||
int device_has_db(sd_device *device);
|
||||
int device_update_db(sd_device *device);
|
||||
int device_delete_db(sd_device *device);
|
||||
int device_read_db_internal_filename(sd_device *device, const char *filename); /* For fuzzer */
|
||||
int device_read_db_internal(sd_device *device, bool force);
|
||||
static inline int device_read_db(sd_device *device) {
|
||||
return device_read_db_internal(device, false);
|
||||
}
|
||||
|
||||
int device_read_uevent_file(sd_device *device);
|
||||
|
||||
int device_set_action(sd_device *device, sd_device_action_t a);
|
||||
sd_device_action_t device_action_from_string(const char *s) _pure_;
|
||||
const char *device_action_to_string(sd_device_action_t a) _const_;
|
||||
void dump_device_action_table(void);
|
2724
src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
Normal file
2724
src/libnm-systemd-core/src/libsystemd/sd-device/sd-device.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -6,6 +6,7 @@ libnm_systemd_shared = static_library(
|
||||
'nm-sd-utils-shared.c',
|
||||
'src/basic/alloc-util.c',
|
||||
'src/basic/btrfs.c',
|
||||
'src/basic/devnum-util.c',
|
||||
'src/basic/env-file.c',
|
||||
'src/basic/env-util.c',
|
||||
'src/basic/escape.c',
|
||||
|
64
src/libnm-systemd-shared/src/basic/chase.h
Normal file
64
src/libnm-systemd-shared/src/basic/chase.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <dirent.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "stat-util.h"
|
||||
|
||||
typedef enum ChaseFlags {
|
||||
CHASE_PREFIX_ROOT = 1 << 0, /* The specified path will be prefixed by the specified root before beginning the iteration */
|
||||
CHASE_NONEXISTENT = 1 << 1, /* It's OK if the path doesn't actually exist. */
|
||||
CHASE_NO_AUTOFS = 1 << 2, /* Return -EREMOTE if autofs mount point found */
|
||||
CHASE_SAFE = 1 << 3, /* Return -EPERM if we ever traverse from unprivileged to privileged files or directories */
|
||||
CHASE_TRAIL_SLASH = 1 << 4, /* Any trailing slash will be preserved */
|
||||
CHASE_STEP = 1 << 5, /* Just execute a single step of the normalization */
|
||||
CHASE_NOFOLLOW = 1 << 6, /* Do not follow the path's right-most component. With ret_fd, when the path's
|
||||
* right-most component refers to symlink, return O_PATH fd of the symlink. */
|
||||
CHASE_WARN = 1 << 7, /* Emit an appropriate warning when an error is encountered.
|
||||
* Note: this may do an NSS lookup, hence this flag cannot be used in PID 1. */
|
||||
CHASE_AT_RESOLVE_IN_ROOT = 1 << 8, /* Same as openat2()'s RESOLVE_IN_ROOT flag, symlinks are resolved
|
||||
* relative to the given directory fd instead of root. */
|
||||
CHASE_PROHIBIT_SYMLINKS = 1 << 9, /* Refuse all symlinks */
|
||||
CHASE_PARENT = 1 << 10, /* Chase the parent directory of the given path. Note that the
|
||||
* full path is still stored in ret_path and only the returned
|
||||
* file descriptor will point to the parent directory. Note that
|
||||
* the result path is the root or '.', then the file descriptor
|
||||
* also points to the result path even if this flag is set.
|
||||
* When this specified, chase() will succeed with 1 even if the
|
||||
* file points to the last path component does not exist. */
|
||||
CHASE_MKDIR_0755 = 1 << 11, /* Create any missing parent directories in the given path. This
|
||||
* needs to be set with CHASE_NONEXISTENT and/or CHASE_PARENT.
|
||||
* Note, chase_and_open() or friends always add CHASE_PARENT flag
|
||||
* when internally call chase(), hence CHASE_MKDIR_0755 can be
|
||||
* safely set without CHASE_NONEXISTENT and CHASE_PARENT. */
|
||||
CHASE_EXTRACT_FILENAME = 1 << 12, /* Only return the last component of the resolved path */
|
||||
} ChaseFlags;
|
||||
|
||||
bool unsafe_transition(const struct stat *a, const struct stat *b);
|
||||
|
||||
/* How many iterations to execute before returning -ELOOP */
|
||||
#define CHASE_MAX 32
|
||||
|
||||
int chase(const char *path_with_prefix, const char *root, ChaseFlags chase_flags, char **ret_path, int *ret_fd);
|
||||
|
||||
int chaseat_prefix_root(const char *path, const char *root, char **ret);
|
||||
int chase_extract_filename(const char *path, const char *root, char **ret);
|
||||
|
||||
int chase_and_open(const char *path, const char *root, ChaseFlags chase_flags, int open_flags, char **ret_path);
|
||||
int chase_and_opendir(const char *path, const char *root, ChaseFlags chase_flags, char **ret_path, DIR **ret_dir);
|
||||
int chase_and_stat(const char *path, const char *root, ChaseFlags chase_flags, char **ret_path, struct stat *ret_stat);
|
||||
int chase_and_access(const char *path, const char *root, ChaseFlags chase_flags, int access_mode, char **ret_path);
|
||||
int chase_and_fopen_unlocked(const char *path, const char *root, ChaseFlags chase_flags, const char *open_flags, char **ret_path, FILE **ret_file);
|
||||
int chase_and_unlink(const char *path, const char *root, ChaseFlags chase_flags, int unlink_flags, char **ret_path);
|
||||
int chase_and_open_parent(const char *path, const char *root, ChaseFlags chase_flags, char **ret_filename);
|
||||
|
||||
int chaseat(int dir_fd, const char *path, ChaseFlags flags, char **ret_path, int *ret_fd);
|
||||
|
||||
int chase_and_openat(int dir_fd, const char *path, ChaseFlags chase_flags, int open_flags, char **ret_path);
|
||||
int chase_and_opendirat(int dir_fd, const char *path, ChaseFlags chase_flags, char **ret_path, DIR **ret_dir);
|
||||
int chase_and_statat(int dir_fd, const char *path, ChaseFlags chase_flags, char **ret_path, struct stat *ret_stat);
|
||||
int chase_and_accessat(int dir_fd, const char *path, ChaseFlags chase_flags, int access_mode, char **ret_path);
|
||||
int chase_and_fopenat_unlocked(int dir_fd, const char *path, ChaseFlags chase_flags, const char *open_flags, char **ret_path, FILE **ret_file);
|
||||
int chase_and_unlinkat(int dir_fd, const char *path, ChaseFlags chase_flags, int unlink_flags, char **ret_path);
|
||||
int chase_and_open_parent_at(int dir_fd, const char *path, ChaseFlags chase_flags, char **ret_filename);
|
142
src/libnm-systemd-shared/src/basic/devnum-util.c
Normal file
142
src/libnm-systemd-shared/src/basic/devnum-util.c
Normal file
@@ -0,0 +1,142 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "nm-sd-adapt-shared.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "chase.h"
|
||||
#include "devnum-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "string-util.h"
|
||||
|
||||
int parse_devnum(const char *s, dev_t *ret) {
|
||||
const char *major;
|
||||
unsigned x, y;
|
||||
size_t n;
|
||||
int r;
|
||||
|
||||
n = strspn(s, DIGITS);
|
||||
if (n == 0)
|
||||
return -EINVAL;
|
||||
if (n > DECIMAL_STR_MAX(dev_t))
|
||||
return -EINVAL;
|
||||
if (s[n] != ':')
|
||||
return -EINVAL;
|
||||
|
||||
major = strndupa_safe(s, n);
|
||||
r = safe_atou(major, &x);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = safe_atou(s + n + 1, &y);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (!DEVICE_MAJOR_VALID(x) || !DEVICE_MINOR_VALID(y))
|
||||
return -ERANGE;
|
||||
|
||||
*ret = makedev(x, y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret) {
|
||||
const char *t;
|
||||
|
||||
/* Generates the /dev/{char|block}/MAJOR:MINOR path for a dev_t */
|
||||
|
||||
if (S_ISCHR(mode))
|
||||
t = "char";
|
||||
else if (S_ISBLK(mode))
|
||||
t = "block";
|
||||
else
|
||||
return -ENODEV;
|
||||
|
||||
if (asprintf(ret, "/dev/%s/" DEVNUM_FORMAT_STR, t, DEVNUM_FORMAT_VAL(devnum)) < 0)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int device_path_make_inaccessible(mode_t mode, char **ret) {
|
||||
char *s;
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (S_ISCHR(mode))
|
||||
s = strdup("/run/systemd/inaccessible/chr");
|
||||
else if (S_ISBLK(mode))
|
||||
s = strdup("/run/systemd/inaccessible/blk");
|
||||
else
|
||||
return -ENODEV;
|
||||
if (!s)
|
||||
return -ENOMEM;
|
||||
|
||||
*ret = s;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret) {
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
/* Finds the canonical path for a device, i.e. resolves the /dev/{char|block}/MAJOR:MINOR path to the end. */
|
||||
|
||||
assert(ret);
|
||||
|
||||
if (devnum_is_zero(devnum))
|
||||
/* A special hack to make sure our 'inaccessible' device nodes work. They won't have symlinks in
|
||||
* /dev/block/ and /dev/char/, hence we handle them specially here. */
|
||||
return device_path_make_inaccessible(mode, ret);
|
||||
|
||||
r = device_path_make_major_minor(mode, devnum, &p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return chase(p, NULL, 0, ret, NULL);
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devnum) {
|
||||
mode_t mode;
|
||||
dev_t devnum;
|
||||
int r;
|
||||
|
||||
/* Tries to extract the major/minor directly from the device path if we can. Handles /dev/block/ and /dev/char/
|
||||
* paths, as well out synthetic inaccessible device nodes. Never goes to disk. Returns -ENODEV if the device
|
||||
* path cannot be parsed like this. */
|
||||
|
||||
if (path_equal(path, "/run/systemd/inaccessible/chr")) {
|
||||
mode = S_IFCHR;
|
||||
devnum = makedev(0, 0);
|
||||
} else if (path_equal(path, "/run/systemd/inaccessible/blk")) {
|
||||
mode = S_IFBLK;
|
||||
devnum = makedev(0, 0);
|
||||
} else {
|
||||
const char *w;
|
||||
|
||||
w = path_startswith(path, "/dev/block/");
|
||||
if (w)
|
||||
mode = S_IFBLK;
|
||||
else {
|
||||
w = path_startswith(path, "/dev/char/");
|
||||
if (!w)
|
||||
return -ENODEV;
|
||||
|
||||
mode = S_IFCHR;
|
||||
}
|
||||
|
||||
r = parse_devnum(w, &devnum);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (ret_mode)
|
||||
*ret_mode = mode;
|
||||
if (ret_devnum)
|
||||
*ret_devnum = devnum;
|
||||
|
||||
return 0;
|
||||
}
|
56
src/libnm-systemd-shared/src/basic/devnum-util.h
Normal file
56
src/libnm-systemd-shared/src/basic/devnum-util.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "stdio-util.h"
|
||||
|
||||
int parse_devnum(const char *s, dev_t *ret);
|
||||
|
||||
/* glibc and the Linux kernel have different ideas about the major/minor size. These calls will check whether the
|
||||
* specified major is valid by the Linux kernel's standards, not by glibc's. Linux has 20bits of minor, and 12 bits of
|
||||
* major space. See MINORBITS in linux/kdev_t.h in the kernel sources. (If you wonder why we define _y here, instead of
|
||||
* comparing directly >= 0: it's to trick out -Wtype-limits, which would otherwise complain if the type is unsigned, as
|
||||
* such a test would be pointless in such a case.) */
|
||||
|
||||
#define DEVICE_MAJOR_VALID(x) \
|
||||
({ \
|
||||
typeof(x) _x = (x), _y = 0; \
|
||||
_x >= _y && _x < (UINT32_C(1) << 12); \
|
||||
\
|
||||
})
|
||||
|
||||
#define DEVICE_MINOR_VALID(x) \
|
||||
({ \
|
||||
typeof(x) _x = (x), _y = 0; \
|
||||
_x >= _y && _x < (UINT32_C(1) << 20); \
|
||||
})
|
||||
|
||||
int device_path_make_major_minor(mode_t mode, dev_t devnum, char **ret);
|
||||
int device_path_make_inaccessible(mode_t mode, char **ret);
|
||||
int device_path_make_canonical(mode_t mode, dev_t devnum, char **ret);
|
||||
int device_path_parse_major_minor(const char *path, mode_t *ret_mode, dev_t *ret_devnum);
|
||||
|
||||
static inline bool devnum_set_and_equal(dev_t a, dev_t b) {
|
||||
/* Returns true if a and b definitely refer to the same device. If either is zero, this means "don't
|
||||
* know" and we'll return false */
|
||||
return a == b && a != 0;
|
||||
}
|
||||
|
||||
/* Maximum string length for a major:minor string. (Note that DECIMAL_STR_MAX includes space for a trailing NUL) */
|
||||
#define DEVNUM_STR_MAX (DECIMAL_STR_MAX(dev_t)-1+1+DECIMAL_STR_MAX(dev_t))
|
||||
|
||||
#define DEVNUM_FORMAT_STR "%u:%u"
|
||||
#define DEVNUM_FORMAT_VAL(d) major(d), minor(d)
|
||||
|
||||
static inline char *format_devnum(dev_t d, char buf[static DEVNUM_STR_MAX]) {
|
||||
return ASSERT_PTR(snprintf_ok(buf, DEVNUM_STR_MAX, DEVNUM_FORMAT_STR, DEVNUM_FORMAT_VAL(d)));
|
||||
}
|
||||
|
||||
#define FORMAT_DEVNUM(d) format_devnum((d), (char[DEVNUM_STR_MAX]) {})
|
||||
|
||||
static inline bool devnum_is_zero(dev_t d) {
|
||||
return major(d) == 0 && minor(d) == 0;
|
||||
}
|
@@ -156,7 +156,6 @@ int readlink_malloc(const char *p, char **ret) {
|
||||
return readlinkat_malloc(AT_FDCWD, p, ret);
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int readlink_value(const char *p, char **ret) {
|
||||
_cleanup_free_ char *link = NULL, *name = NULL;
|
||||
int r;
|
||||
@@ -178,6 +177,7 @@ int readlink_value(const char *p, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int readlink_and_make_absolute(const char *p, char **ret) {
|
||||
_cleanup_free_ char *target = NULL;
|
||||
int r;
|
||||
|
@@ -22,7 +22,6 @@ DEFINE_HASH_OPS_FULL(string_hash_ops_free_strv_free,
|
||||
char, string_hash_func, string_compare_func, free,
|
||||
char*, strv_free);
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
void path_hash_func(const char *q, struct siphash *state) {
|
||||
bool add_slash = false;
|
||||
|
||||
@@ -68,7 +67,6 @@ DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(path_hash_ops_free,
|
||||
DEFINE_HASH_OPS_FULL(path_hash_ops_free_free,
|
||||
char, path_hash_func, path_compare, free,
|
||||
void, free);
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
void trivial_hash_func(const void *p, struct siphash *state) {
|
||||
siphash24_compress_typesafe(p, state);
|
||||
|
@@ -219,6 +219,7 @@ int path_make_relative_parent(const char *from_child, const char *to, char **ret
|
||||
|
||||
return path_make_relative(from, to, ret);
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
char* path_startswith_strv(const char *p, char **set) {
|
||||
STRV_FOREACH(s, set) {
|
||||
@@ -232,6 +233,7 @@ char* path_startswith_strv(const char *p, char **set) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
int path_strv_make_absolute_cwd(char **l) {
|
||||
int r;
|
||||
|
||||
|
@@ -110,12 +110,14 @@ int xstatfsat(int dir_fd, const char *path, struct statfs *ret);
|
||||
} var
|
||||
#endif
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
static inline usec_t statx_timestamp_load(const struct statx_timestamp *ts) {
|
||||
return timespec_load(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
|
||||
}
|
||||
static inline nsec_t statx_timestamp_load_nsec(const struct statx_timestamp *ts) {
|
||||
return timespec_load_nsec(&(const struct timespec) { .tv_sec = ts->tv_sec, .tv_nsec = ts->tv_nsec });
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
void inode_hash_func(const struct stat *q, struct siphash *state);
|
||||
int inode_compare_func(const struct stat *a, const struct stat *b);
|
||||
|
@@ -1319,6 +1319,7 @@ bool streq_skip_trailing_chars(const char *s1, const char *s2, const char *ok) {
|
||||
|
||||
return in_charset(s1, ok) && in_charset(s2, ok);
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
char *string_replace_char(char *str, char old_char, char new_char) {
|
||||
assert(str);
|
||||
@@ -1331,7 +1332,6 @@ char *string_replace_char(char *str, char old_char, char new_char) {
|
||||
|
||||
return str;
|
||||
}
|
||||
#endif /* NM_IGNORED */
|
||||
|
||||
int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
|
||||
char *b;
|
||||
@@ -1376,7 +1376,6 @@ int make_cstring(const char *s, size_t n, MakeCStringMode mode, char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
size_t strspn_from_end(const char *str, const char *accept) {
|
||||
size_t n = 0;
|
||||
|
||||
@@ -1392,6 +1391,7 @@ size_t strspn_from_end(const char *str, const char *accept) {
|
||||
return n;
|
||||
}
|
||||
|
||||
#if 0 /* NM_IGNORED */
|
||||
char *strdupspn(const char *a, const char *accept) {
|
||||
if (isempty(a) || isempty(accept))
|
||||
return strdup("");
|
||||
|
Reference in New Issue
Block a user