2020-11-09 05:25:50 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
2003-07-21 05:48:48 +02:00
|
|
|
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <stdbool.h>
|
|
|
|
#include <stddef.h>
|
2003-10-23 04:39:54 +02:00
|
|
|
#include <sys/stat.h>
|
2015-10-24 22:58:24 +02:00
|
|
|
#include <unistd.h>
|
2003-07-21 05:48:48 +02:00
|
|
|
|
2018-08-29 08:54:56 +02:00
|
|
|
#include "alloc-util.h"
|
2017-10-31 11:30:40 +01:00
|
|
|
#include "device-nodes.h"
|
2018-08-29 08:54:56 +02:00
|
|
|
#include "device-private.h"
|
|
|
|
#include "device-util.h"
|
2016-12-09 10:04:30 +01:00
|
|
|
#include "dirent-util.h"
|
2018-08-29 08:54:56 +02:00
|
|
|
#include "fd-util.h"
|
2016-11-07 16:14:59 +01:00
|
|
|
#include "format-util.h"
|
2015-10-26 21:16:26 +01:00
|
|
|
#include "fs-util.h"
|
2018-11-16 03:44:17 +01:00
|
|
|
#include "libudev-util.h"
|
|
|
|
#include "mkdir.h"
|
2018-08-29 08:54:56 +02:00
|
|
|
#include "path-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "selinux-util.h"
|
|
|
|
#include "smack-util.h"
|
2020-10-23 16:30:23 +02:00
|
|
|
#include "stat-util.h"
|
2016-01-12 15:34:20 +01:00
|
|
|
#include "stdio-util.h"
|
2015-10-24 22:58:24 +02:00
|
|
|
#include "string-util.h"
|
2018-11-16 03:44:17 +01:00
|
|
|
#include "strxcpyx.h"
|
2018-08-29 08:54:56 +02:00
|
|
|
#include "udev-node.h"
|
2019-06-29 15:08:11 +02:00
|
|
|
#include "user-util.h"
|
2004-10-06 08:39:05 +02:00
|
|
|
|
2020-10-23 16:30:23 +02:00
|
|
|
#define LINK_UPDATE_MAX_RETRIES 128
|
|
|
|
|
2018-08-29 08:54:56 +02:00
|
|
|
static int node_symlink(sd_device *dev, const char *node, const char *slink) {
|
2018-09-23 09:31:12 +02:00
|
|
|
_cleanup_free_ char *slink_dirname = NULL, *target = NULL;
|
2018-08-29 08:54:56 +02:00
|
|
|
const char *id_filename, *slink_tmp;
|
2018-09-23 09:31:12 +02:00
|
|
|
struct stat stats;
|
2018-08-29 08:54:56 +02:00
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(dev);
|
|
|
|
assert(node);
|
|
|
|
assert(slink);
|
2018-09-23 09:31:12 +02:00
|
|
|
|
|
|
|
slink_dirname = dirname_malloc(slink);
|
|
|
|
if (!slink_dirname)
|
|
|
|
return log_oom();
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* use relative link */
|
2018-09-23 09:31:12 +02:00
|
|
|
r = path_make_relative(slink_dirname, node, &target);
|
|
|
|
if (r < 0)
|
2018-10-22 06:12:19 +02:00
|
|
|
return log_device_error_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, node);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* preserve link with correct target, do not replace node of other device */
|
|
|
|
if (lstat(slink, &stats) == 0) {
|
2020-07-16 18:04:45 +02:00
|
|
|
if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode))
|
|
|
|
return log_device_error_errno(dev, SYNTHETIC_ERRNO(EOPNOTSUPP),
|
|
|
|
"Conflicting device node '%s' found, link to '%s' will not be created.", slink, node);
|
|
|
|
else if (S_ISLNK(stats.st_mode)) {
|
2018-10-13 16:28:02 +02:00
|
|
|
_cleanup_free_ char *buf = NULL;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-10-13 16:28:02 +02:00
|
|
|
if (readlink_malloc(slink, &buf) >= 0 &&
|
|
|
|
streq(target, buf)) {
|
2018-10-22 06:12:19 +02:00
|
|
|
log_device_debug(dev, "Preserve already existing symlink '%s' to '%s'", slink, target);
|
2018-10-13 16:28:02 +02:00
|
|
|
(void) label_fix(slink, LABEL_IGNORE_ENOENT);
|
|
|
|
(void) utimensat(AT_FDCWD, slink, NULL, AT_SYMLINK_NOFOLLOW);
|
|
|
|
return 0;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2018-10-22 06:12:19 +02:00
|
|
|
log_device_debug(dev, "Creating symlink '%s' to '%s'", slink, target);
|
2012-01-10 01:34:15 +01:00
|
|
|
do {
|
2018-08-29 08:54:56 +02:00
|
|
|
r = mkdir_parents_label(slink, 0755);
|
|
|
|
if (!IN_SET(r, 0, -ENOENT))
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
2014-10-23 19:41:27 +02:00
|
|
|
mac_selinux_create_file_prepare(slink, S_IFLNK);
|
2018-08-29 08:54:56 +02:00
|
|
|
if (symlink(target, slink) < 0)
|
|
|
|
r = -errno;
|
2014-10-23 19:41:27 +02:00
|
|
|
mac_selinux_create_file_clear();
|
2018-08-29 08:54:56 +02:00
|
|
|
} while (r == -ENOENT);
|
|
|
|
if (r == 0)
|
|
|
|
return 0;
|
2018-10-30 05:29:44 +01:00
|
|
|
if (r < 0)
|
|
|
|
log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s', trying to replace '%s': %m", slink, target, slink);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
2018-10-22 06:12:19 +02:00
|
|
|
log_device_debug(dev, "Atomically replace '%s'", slink);
|
2018-08-29 08:54:56 +02:00
|
|
|
r = device_get_id_filename(dev, &id_filename);
|
|
|
|
if (r < 0)
|
2018-10-22 06:12:19 +02:00
|
|
|
return log_device_error_errno(dev, r, "Failed to get id_filename: %m");
|
2018-08-29 08:54:56 +02:00
|
|
|
slink_tmp = strjoina(slink, ".tmp-", id_filename);
|
|
|
|
(void) unlink(slink_tmp);
|
2012-01-10 01:34:15 +01:00
|
|
|
do {
|
2018-08-29 08:54:56 +02:00
|
|
|
r = mkdir_parents_label(slink_tmp, 0755);
|
|
|
|
if (!IN_SET(r, 0, -ENOENT))
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
2014-10-23 19:41:27 +02:00
|
|
|
mac_selinux_create_file_prepare(slink_tmp, S_IFLNK);
|
2018-08-29 08:54:56 +02:00
|
|
|
if (symlink(target, slink_tmp) < 0)
|
|
|
|
r = -errno;
|
2014-10-23 19:41:27 +02:00
|
|
|
mac_selinux_create_file_clear();
|
2018-08-29 08:54:56 +02:00
|
|
|
} while (r == -ENOENT);
|
|
|
|
if (r < 0)
|
2018-10-22 06:12:19 +02:00
|
|
|
return log_device_error_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target);
|
2018-08-29 08:54:56 +02:00
|
|
|
|
|
|
|
if (rename(slink_tmp, slink) < 0) {
|
2019-05-22 18:30:02 +02:00
|
|
|
r = log_device_error_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink);
|
2018-08-29 08:54:56 +02:00
|
|
|
(void) unlink(slink_tmp);
|
2020-10-23 16:30:23 +02:00
|
|
|
} else
|
|
|
|
/* Tell caller that we replaced already existing symlink. */
|
|
|
|
r = 1;
|
2018-08-29 08:54:56 +02:00
|
|
|
|
|
|
|
return r;
|
2006-06-14 16:32:52 +02:00
|
|
|
}
|
|
|
|
|
2009-09-09 18:18:17 +02:00
|
|
|
/* find device node of device with highest priority */
|
2018-08-29 08:54:56 +02:00
|
|
|
static int link_find_prioritized(sd_device *dev, bool add, const char *stackdir, char **ret) {
|
|
|
|
_cleanup_closedir_ DIR *dir = NULL;
|
|
|
|
_cleanup_free_ char *target = NULL;
|
2016-12-09 10:04:30 +01:00
|
|
|
struct dirent *dent;
|
2018-08-29 08:54:56 +02:00
|
|
|
int r, priority = 0;
|
|
|
|
|
|
|
|
assert(!add || dev);
|
|
|
|
assert(stackdir);
|
|
|
|
assert(ret);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
if (add) {
|
2018-08-29 08:54:56 +02:00
|
|
|
const char *devnode;
|
|
|
|
|
|
|
|
r = device_get_devlink_priority(dev, &priority);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = sd_device_get_devname(dev, &devnode);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
target = strdup(devnode);
|
|
|
|
if (!target)
|
|
|
|
return -ENOMEM;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
dir = opendir(stackdir);
|
2018-08-29 08:54:56 +02:00
|
|
|
if (!dir) {
|
|
|
|
if (target) {
|
|
|
|
*ret = TAKE_PTR(target);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
2016-12-09 10:04:30 +01:00
|
|
|
FOREACH_DIRENT_ALL(dent, dir, break) {
|
2018-08-29 08:54:56 +02:00
|
|
|
_cleanup_(sd_device_unrefp) sd_device *dev_db = NULL;
|
|
|
|
const char *devnode, *id_filename;
|
|
|
|
int db_prio = 0;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2016-12-09 10:04:30 +01:00
|
|
|
if (dent->d_name[0] == '\0')
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
if (dent->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
|
2018-10-22 06:12:19 +02:00
|
|
|
log_device_debug(dev, "Found '%s' claiming '%s'", dent->d_name, stackdir);
|
2018-08-29 08:54:56 +02:00
|
|
|
|
|
|
|
if (device_get_id_filename(dev, &id_filename) < 0)
|
|
|
|
continue;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* did we find ourself? */
|
2018-08-29 08:54:56 +02:00
|
|
|
if (streq(dent->d_name, id_filename))
|
2012-01-10 01:34:15 +01:00
|
|
|
continue;
|
|
|
|
|
2018-08-29 08:54:56 +02:00
|
|
|
if (sd_device_new_from_device_id(&dev_db, dent->d_name) < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (sd_device_get_devname(dev_db, &devnode) < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (device_get_devlink_priority(dev_db, &db_prio) < 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (target && db_prio <= priority)
|
|
|
|
continue;
|
|
|
|
|
2018-10-22 06:12:19 +02:00
|
|
|
log_device_debug(dev_db, "Device claims priority %i for '%s'", db_prio, stackdir);
|
2018-08-29 08:54:56 +02:00
|
|
|
|
|
|
|
r = free_and_strdup(&target, devnode);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
priority = db_prio;
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2018-08-29 08:54:56 +02:00
|
|
|
|
2019-01-08 18:46:03 +01:00
|
|
|
if (!target)
|
|
|
|
return -ENOENT;
|
|
|
|
|
2018-08-29 08:54:56 +02:00
|
|
|
*ret = TAKE_PTR(target);
|
|
|
|
return 0;
|
2008-10-16 17:16:58 +02:00
|
|
|
}
|
|
|
|
|
2020-12-14 08:17:19 +01:00
|
|
|
static size_t escape_path(const char *src, char *dest, size_t size) {
|
|
|
|
size_t i, j;
|
|
|
|
|
|
|
|
assert(src);
|
|
|
|
assert(dest);
|
|
|
|
|
|
|
|
for (i = 0, j = 0; src[i] != '\0'; i++) {
|
|
|
|
if (src[i] == '/') {
|
|
|
|
if (j+4 >= size) {
|
|
|
|
j = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
memcpy(&dest[j], "\\x2f", 4);
|
|
|
|
j += 4;
|
|
|
|
} else if (src[i] == '\\') {
|
|
|
|
if (j+4 >= size) {
|
|
|
|
j = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
memcpy(&dest[j], "\\x5c", 4);
|
|
|
|
j += 4;
|
|
|
|
} else {
|
|
|
|
if (j+1 >= size) {
|
|
|
|
j = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
dest[j] = src[i];
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dest[j] = '\0';
|
|
|
|
return j;
|
|
|
|
}
|
|
|
|
|
2009-09-09 18:18:17 +02:00
|
|
|
/* manage "stack of names" with possibly specified device priorities */
|
2018-08-29 08:54:56 +02:00
|
|
|
static int link_update(sd_device *dev, const char *slink, bool add) {
|
|
|
|
_cleanup_free_ char *target = NULL, *filename = NULL, *dirname = NULL;
|
|
|
|
char name_enc[PATH_MAX];
|
|
|
|
const char *id_filename;
|
2020-10-23 16:30:23 +02:00
|
|
|
int i, r, retries;
|
2018-08-29 08:54:56 +02:00
|
|
|
|
|
|
|
assert(dev);
|
|
|
|
assert(slink);
|
|
|
|
|
|
|
|
r = device_get_id_filename(dev, &id_filename);
|
|
|
|
if (r < 0)
|
2018-10-22 06:12:19 +02:00
|
|
|
return log_device_debug_errno(dev, r, "Failed to get id_filename: %m");
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2020-12-14 08:17:19 +01:00
|
|
|
escape_path(slink + STRLEN("/dev"), name_enc, sizeof(name_enc));
|
2018-11-30 10:43:57 +01:00
|
|
|
dirname = path_join("/run/udev/links/", name_enc);
|
2018-08-29 08:54:56 +02:00
|
|
|
if (!dirname)
|
|
|
|
return log_oom();
|
2018-11-30 10:43:57 +01:00
|
|
|
filename = path_join(dirname, id_filename);
|
2018-08-29 08:54:56 +02:00
|
|
|
if (!filename)
|
|
|
|
return log_oom();
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2020-12-09 09:46:08 +01:00
|
|
|
if (!add) {
|
|
|
|
if (unlink(filename) == 0)
|
|
|
|
(void) rmdir(dirname);
|
|
|
|
} else
|
|
|
|
for (;;) {
|
2018-08-29 08:54:56 +02:00
|
|
|
_cleanup_close_ int fd = -1;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-08-29 08:54:56 +02:00
|
|
|
r = mkdir_parents(filename, 0755);
|
|
|
|
if (!IN_SET(r, 0, -ENOENT))
|
2020-12-09 09:46:08 +01:00
|
|
|
return r;
|
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
fd = open(filename, O_WRONLY|O_CREAT|O_CLOEXEC|O_TRUNC|O_NOFOLLOW, 0444);
|
2020-12-09 09:46:08 +01:00
|
|
|
if (fd >= 0)
|
|
|
|
break;
|
|
|
|
if (errno != ENOENT)
|
|
|
|
return -errno;
|
|
|
|
}
|
2018-08-29 08:54:56 +02:00
|
|
|
|
2020-10-23 16:30:23 +02:00
|
|
|
/* If the database entry is not written yet we will just do one iteration and possibly wrong symlink
|
|
|
|
* will be fixed in the second invocation. */
|
|
|
|
retries = sd_device_get_is_initialized(dev) > 0 ? LINK_UPDATE_MAX_RETRIES : 1;
|
|
|
|
|
|
|
|
for (i = 0; i < retries; i++) {
|
|
|
|
struct stat st1 = {}, st2 = {};
|
|
|
|
|
|
|
|
r = stat(dirname, &st1);
|
|
|
|
if (r < 0 && errno != ENOENT)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
r = link_find_prioritized(dev, add, dirname, &target);
|
|
|
|
if (r == -ENOENT) {
|
|
|
|
log_device_debug(dev, "No reference left, removing '%s'", slink);
|
|
|
|
if (unlink(slink) == 0)
|
|
|
|
(void) rmdir_parents(slink, "/");
|
|
|
|
|
|
|
|
break;
|
|
|
|
} else if (r < 0)
|
|
|
|
return log_device_error_errno(dev, r, "Failed to determine highest priority symlink: %m");
|
|
|
|
|
|
|
|
r = node_symlink(dev, target, slink);
|
|
|
|
if (r < 0) {
|
|
|
|
(void) unlink(filename);
|
|
|
|
break;
|
|
|
|
} else if (r == 1)
|
|
|
|
/* We have replaced already existing symlink, possibly there is some other device trying
|
|
|
|
* to claim the same symlink. Let's do one more iteration to give us a chance to fix
|
|
|
|
* the error if other device actually claims the symlink with higher priority. */
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* Skip the second stat() if the first failed, stat_inode_unmodified() would return false regardless. */
|
|
|
|
if ((st1.st_mode & S_IFMT) != 0) {
|
|
|
|
r = stat(dirname, &st2);
|
|
|
|
if (r < 0 && errno != ENOENT)
|
|
|
|
return -errno;
|
|
|
|
|
|
|
|
if (stat_inode_unmodified(&st1, &st2))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return i < LINK_UPDATE_MAX_RETRIES ? 0 : -ELOOP;
|
priority based symlink handling
Symlinks can have priorities now, the priority is assigned to the device
and specified with OPTIONS="link_priority=100". Devices with higher
priorities overwrite the symlinks of devices with lower priorities.
If the device, that currently owns the link goes away, the symlink
will be removed, and recreated, pointing to the next device with the
highest actual priority.
This should solve the issue, that inserting an USB-stick may overwrite the
/dev/disk/by-id/-link of another disk, and removes the entire link after the
USB-stick is disconnected. If no priorities are specified, the new link will
overwrite the current one, and if the device goes away, it will restore
the old link. It should be possible to assign lower priorities to removable
devices, if needed.
In multipath setups, we see several devices, which all connect to the same
volume, and therefore all try to create the same metadata-links. The
different path-devices are combined into one device-mapper device, which also
contains the same metadata. It should be possible, to assign multipath-table
device-mapper devices a higher priority, so path-devices that appear and
disappear, will not overwrite or delete the device-mapper device links.
2007-03-18 12:51:57 +01:00
|
|
|
}
|
|
|
|
|
2018-08-29 08:54:56 +02:00
|
|
|
int udev_node_update_old_links(sd_device *dev, sd_device *dev_old) {
|
|
|
|
const char *name, *devpath;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(dev);
|
|
|
|
assert(dev_old);
|
|
|
|
|
|
|
|
r = sd_device_get_devpath(dev, &devpath);
|
|
|
|
if (r < 0)
|
2018-10-22 06:12:19 +02:00
|
|
|
return log_device_debug_errno(dev, r, "Failed to get devpath: %m");
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* update possible left-over symlinks */
|
2018-08-29 08:54:56 +02:00
|
|
|
FOREACH_DEVICE_DEVLINK(dev_old, name) {
|
|
|
|
const char *name_current;
|
|
|
|
bool found = false;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* check if old link name still belongs to this device */
|
2018-08-29 08:54:56 +02:00
|
|
|
FOREACH_DEVICE_DEVLINK(dev, name_current)
|
2013-02-13 18:13:22 +01:00
|
|
|
if (streq(name, name_current)) {
|
2018-08-29 08:54:56 +02:00
|
|
|
found = true;
|
2012-01-10 01:34:15 +01:00
|
|
|
break;
|
|
|
|
}
|
2018-08-29 08:54:56 +02:00
|
|
|
|
2012-01-10 01:34:15 +01:00
|
|
|
if (found)
|
|
|
|
continue;
|
|
|
|
|
2018-10-22 06:12:19 +02:00
|
|
|
log_device_debug(dev, "Updating old name, '%s' no longer belonging to '%s'",
|
|
|
|
name, devpath);
|
2012-10-08 21:54:39 +02:00
|
|
|
link_update(dev, name, false);
|
2012-01-10 01:34:15 +01:00
|
|
|
}
|
2018-08-29 08:54:56 +02:00
|
|
|
|
|
|
|
return 0;
|
priority based symlink handling
Symlinks can have priorities now, the priority is assigned to the device
and specified with OPTIONS="link_priority=100". Devices with higher
priorities overwrite the symlinks of devices with lower priorities.
If the device, that currently owns the link goes away, the symlink
will be removed, and recreated, pointing to the next device with the
highest actual priority.
This should solve the issue, that inserting an USB-stick may overwrite the
/dev/disk/by-id/-link of another disk, and removes the entire link after the
USB-stick is disconnected. If no priorities are specified, the new link will
overwrite the current one, and if the device goes away, it will restore
the old link. It should be possible to assign lower priorities to removable
devices, if needed.
In multipath setups, we see several devices, which all connect to the same
volume, and therefore all try to create the same metadata-links. The
different path-devices are combined into one device-mapper device, which also
contains the same metadata. It should be possible, to assign multipath-table
device-mapper devices a higher priority, so path-devices that appear and
disappear, will not overwrite or delete the device-mapper device links.
2007-03-18 12:51:57 +01:00
|
|
|
}
|
|
|
|
|
2019-06-29 15:08:11 +02:00
|
|
|
static int node_permissions_apply(sd_device *dev, bool apply_mac,
|
2013-10-08 01:59:10 +02:00
|
|
|
mode_t mode, uid_t uid, gid_t gid,
|
2019-03-04 20:01:34 +01:00
|
|
|
OrderedHashmap *seclabel_list) {
|
2018-08-29 08:54:56 +02:00
|
|
|
const char *devnode, *subsystem, *id_filename = NULL;
|
2020-09-14 21:58:40 +02:00
|
|
|
bool apply_mode, apply_uid, apply_gid;
|
|
|
|
_cleanup_close_ int node_fd = -1;
|
2018-08-29 08:54:56 +02:00
|
|
|
struct stat stats;
|
|
|
|
dev_t devnum;
|
2019-06-29 15:08:11 +02:00
|
|
|
int r;
|
2018-08-29 08:54:56 +02:00
|
|
|
|
|
|
|
assert(dev);
|
|
|
|
|
|
|
|
r = sd_device_get_devname(dev, &devnode);
|
|
|
|
if (r < 0)
|
2018-10-22 06:12:19 +02:00
|
|
|
return log_device_debug_errno(dev, r, "Failed to get devname: %m");
|
2018-08-29 08:54:56 +02:00
|
|
|
r = sd_device_get_subsystem(dev, &subsystem);
|
|
|
|
if (r < 0)
|
2018-10-22 06:12:19 +02:00
|
|
|
return log_device_debug_errno(dev, r, "Failed to get subsystem: %m");
|
2018-08-29 08:54:56 +02:00
|
|
|
r = sd_device_get_devnum(dev, &devnum);
|
|
|
|
if (r < 0)
|
2018-10-22 06:12:19 +02:00
|
|
|
return log_device_debug_errno(dev, r, "Failed to get devnum: %m");
|
2018-08-29 08:54:56 +02:00
|
|
|
(void) device_get_id_filename(dev, &id_filename);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-08-29 08:54:56 +02:00
|
|
|
if (streq(subsystem, "block"))
|
2012-01-10 01:34:15 +01:00
|
|
|
mode |= S_IFBLK;
|
|
|
|
else
|
|
|
|
mode |= S_IFCHR;
|
|
|
|
|
2020-09-14 21:58:40 +02:00
|
|
|
node_fd = open(devnode, O_PATH|O_NOFOLLOW|O_CLOEXEC);
|
|
|
|
if (node_fd < 0) {
|
|
|
|
if (errno == ENOENT) {
|
|
|
|
log_device_debug_errno(dev, errno, "Device node %s is missing, skipping handling.", devnode);
|
|
|
|
return 0; /* This is necessarily racey, so ignore missing the device */
|
|
|
|
}
|
|
|
|
|
|
|
|
return log_device_debug_errno(dev, errno, "Cannot open node %s: %m", devnode);
|
2019-11-12 06:58:25 +01:00
|
|
|
}
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2020-09-14 21:58:40 +02:00
|
|
|
if (fstat(node_fd, &stats) < 0)
|
|
|
|
return log_device_debug_errno(dev, errno, "cannot stat() node %s: %m", devnode);
|
|
|
|
|
|
|
|
if ((mode != MODE_INVALID && (stats.st_mode & S_IFMT) != (mode & S_IFMT)) || stats.st_rdev != devnum) {
|
|
|
|
log_device_debug(dev, "Found node '%s' with non-matching devnum %s, skipping handling.",
|
|
|
|
devnode, id_filename);
|
|
|
|
return 0; /* We might process a device that already got replaced by the time we have a look
|
|
|
|
* at it, handle this gracefully and step away. */
|
|
|
|
}
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2019-06-29 15:08:11 +02:00
|
|
|
apply_mode = mode != MODE_INVALID && (stats.st_mode & 0777) != (mode & 0777);
|
|
|
|
apply_uid = uid_is_valid(uid) && stats.st_uid != uid;
|
|
|
|
apply_gid = gid_is_valid(gid) && stats.st_gid != gid;
|
|
|
|
|
|
|
|
if (apply_mode || apply_uid || apply_gid || apply_mac) {
|
2018-08-29 08:54:56 +02:00
|
|
|
bool selinux = false, smack = false;
|
2018-10-16 22:37:34 +02:00
|
|
|
const char *name, *label;
|
2013-10-09 04:17:39 +02:00
|
|
|
|
2019-06-29 15:08:11 +02:00
|
|
|
if (apply_mode || apply_uid || apply_gid) {
|
2019-08-02 23:04:34 +02:00
|
|
|
log_device_debug(dev, "Setting permissions %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
|
|
|
|
devnode,
|
|
|
|
uid_is_valid(uid) ? uid : stats.st_uid,
|
|
|
|
gid_is_valid(gid) ? gid : stats.st_gid,
|
|
|
|
mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
|
2019-04-30 19:25:29 +02:00
|
|
|
|
2020-09-14 21:58:40 +02:00
|
|
|
r = fchmod_and_chown(node_fd, mode, uid, gid);
|
2019-04-30 19:25:29 +02:00
|
|
|
if (r < 0)
|
2020-09-08 19:10:27 +02:00
|
|
|
log_device_full_errno(dev, r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
|
|
|
|
"Failed to set owner/mode of %s to uid=" UID_FMT
|
|
|
|
", gid=" GID_FMT ", mode=%#o: %m",
|
|
|
|
devnode,
|
|
|
|
uid_is_valid(uid) ? uid : stats.st_uid,
|
|
|
|
gid_is_valid(gid) ? gid : stats.st_gid,
|
|
|
|
mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
|
2018-08-29 08:54:56 +02:00
|
|
|
} else
|
2019-08-02 23:04:34 +02:00
|
|
|
log_device_debug(dev, "Preserve permissions of %s, uid=" UID_FMT ", gid=" GID_FMT ", mode=%#o",
|
|
|
|
devnode,
|
|
|
|
uid_is_valid(uid) ? uid : stats.st_uid,
|
|
|
|
gid_is_valid(gid) ? gid : stats.st_gid,
|
|
|
|
mode != MODE_INVALID ? mode & 0777 : stats.st_mode & 0777);
|
2013-10-08 01:59:10 +02:00
|
|
|
|
|
|
|
/* apply SECLABEL{$module}=$label */
|
2020-09-08 11:58:29 +02:00
|
|
|
ORDERED_HASHMAP_FOREACH_KEY(label, name, seclabel_list) {
|
2018-08-29 08:54:56 +02:00
|
|
|
int q;
|
2013-10-08 01:59:10 +02:00
|
|
|
|
|
|
|
if (streq(name, "selinux")) {
|
2013-10-09 04:17:39 +02:00
|
|
|
selinux = true;
|
2014-10-23 18:06:51 +02:00
|
|
|
|
2020-09-14 21:58:40 +02:00
|
|
|
q = mac_selinux_apply_fd(node_fd, devnode, label);
|
2018-08-29 08:54:56 +02:00
|
|
|
if (q < 0)
|
2020-09-08 19:10:27 +02:00
|
|
|
log_device_full_errno(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
|
|
|
|
"SECLABEL: failed to set SELinux label '%s': %m", label);
|
2013-10-09 17:31:00 +02:00
|
|
|
else
|
2018-10-22 06:12:19 +02:00
|
|
|
log_device_debug(dev, "SECLABEL: set SELinux label '%s'", label);
|
2013-10-08 01:59:10 +02:00
|
|
|
|
2013-10-11 09:47:31 +02:00
|
|
|
} else if (streq(name, "smack")) {
|
2013-10-09 04:17:39 +02:00
|
|
|
smack = true;
|
2014-10-23 18:06:51 +02:00
|
|
|
|
2020-09-14 21:58:40 +02:00
|
|
|
q = mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, label);
|
2018-08-29 08:54:56 +02:00
|
|
|
if (q < 0)
|
2020-09-08 19:10:27 +02:00
|
|
|
log_device_full_errno(dev, q == -ENOENT ? LOG_DEBUG : LOG_ERR, q,
|
|
|
|
"SECLABEL: failed to set SMACK label '%s': %m", label);
|
2013-10-08 01:59:10 +02:00
|
|
|
else
|
2018-10-22 06:12:19 +02:00
|
|
|
log_device_debug(dev, "SECLABEL: set SMACK label '%s'", label);
|
2013-10-08 01:59:10 +02:00
|
|
|
|
|
|
|
} else
|
2018-10-22 06:12:19 +02:00
|
|
|
log_device_error(dev, "SECLABEL: unknown subsystem, ignoring '%s'='%s'", name, label);
|
2013-10-08 01:59:10 +02:00
|
|
|
}
|
2013-10-09 04:17:39 +02:00
|
|
|
|
|
|
|
/* set the defaults */
|
|
|
|
if (!selinux)
|
2020-09-14 21:58:40 +02:00
|
|
|
(void) mac_selinux_fix_fd(node_fd, devnode, LABEL_IGNORE_ENOENT);
|
2013-10-11 09:47:31 +02:00
|
|
|
if (!smack)
|
2020-09-14 21:58:40 +02:00
|
|
|
(void) mac_smack_apply_fd(node_fd, SMACK_ATTR_ACCESS, NULL);
|
2013-01-04 16:15:46 +01:00
|
|
|
}
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* always update timestamp when we re-use the node, like on media change events */
|
2020-09-14 21:58:40 +02:00
|
|
|
r = futimens_opath(node_fd, NULL);
|
|
|
|
if (r < 0)
|
|
|
|
log_device_debug_errno(dev, r, "Failed to adjust timestamp of node %s: %m", devnode);
|
2018-08-29 08:54:56 +02:00
|
|
|
|
|
|
|
return r;
|
2012-01-01 04:21:15 +01:00
|
|
|
}
|
|
|
|
|
2018-08-29 08:54:56 +02:00
|
|
|
static int xsprintf_dev_num_path_from_sd_device(sd_device *dev, char **ret) {
|
|
|
|
char filename[DEV_NUM_PATH_MAX], *s;
|
|
|
|
const char *subsystem;
|
|
|
|
dev_t devnum;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(ret);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-08-29 08:54:56 +02:00
|
|
|
r = sd_device_get_subsystem(dev, &subsystem);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2018-08-29 08:54:56 +02:00
|
|
|
r = sd_device_get_devnum(dev, &devnum);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
2012-01-10 01:34:15 +01:00
|
|
|
|
2017-10-31 11:30:40 +01:00
|
|
|
xsprintf_dev_num_path(filename,
|
2018-08-29 08:54:56 +02:00
|
|
|
streq(subsystem, "block") ? "block" : "char",
|
|
|
|
devnum);
|
|
|
|
|
|
|
|
s = strdup(filename);
|
|
|
|
if (!s)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
*ret = s;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int udev_node_add(sd_device *dev, bool apply,
|
|
|
|
mode_t mode, uid_t uid, gid_t gid,
|
2019-03-04 20:01:34 +01:00
|
|
|
OrderedHashmap *seclabel_list) {
|
2018-08-29 08:54:56 +02:00
|
|
|
const char *devnode, *devlink;
|
|
|
|
_cleanup_free_ char *filename = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(dev);
|
|
|
|
|
|
|
|
r = sd_device_get_devname(dev, &devnode);
|
|
|
|
if (r < 0)
|
2018-10-22 06:12:19 +02:00
|
|
|
return log_device_debug_errno(dev, r, "Failed to get devnode: %m");
|
2018-08-29 08:54:56 +02:00
|
|
|
|
|
|
|
if (DEBUG_LOGGING) {
|
|
|
|
const char *id_filename = NULL;
|
|
|
|
|
|
|
|
(void) device_get_id_filename(dev, &id_filename);
|
2019-08-02 23:04:34 +02:00
|
|
|
log_device_debug(dev, "Handling device node '%s', devnum=%s", devnode, strnull(id_filename));
|
2018-08-29 08:54:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
r = node_permissions_apply(dev, apply, mode, uid, gid, seclabel_list);
|
|
|
|
if (r < 0)
|
|
|
|
return r;
|
|
|
|
|
|
|
|
r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
|
|
|
|
if (r < 0)
|
2018-10-22 06:12:19 +02:00
|
|
|
return log_device_debug_errno(dev, r, "Failed to get device path: %m");
|
2018-08-29 08:54:56 +02:00
|
|
|
|
|
|
|
/* always add /dev/{block,char}/$major:$minor */
|
|
|
|
(void) node_symlink(dev, devnode, filename);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* create/update symlinks, add symlinks to name index */
|
2020-10-23 16:30:23 +02:00
|
|
|
FOREACH_DEVICE_DEVLINK(dev, devlink) {
|
|
|
|
r = link_update(dev, devlink, true);
|
|
|
|
if (r < 0)
|
|
|
|
log_device_info_errno(dev, r, "Failed to update device symlinks: %m");
|
|
|
|
}
|
2018-08-29 08:54:56 +02:00
|
|
|
|
|
|
|
return 0;
|
2003-07-21 05:48:48 +02:00
|
|
|
}
|
|
|
|
|
2018-08-29 08:54:56 +02:00
|
|
|
int udev_node_remove(sd_device *dev) {
|
|
|
|
_cleanup_free_ char *filename = NULL;
|
|
|
|
const char *devlink;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
assert(dev);
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* remove/update symlinks, remove symlinks from name index */
|
2020-10-23 16:30:23 +02:00
|
|
|
FOREACH_DEVICE_DEVLINK(dev, devlink) {
|
|
|
|
r = link_update(dev, devlink, false);
|
|
|
|
if (r < 0)
|
|
|
|
log_device_info_errno(dev, r, "Failed to update device symlinks: %m");
|
|
|
|
}
|
2018-08-29 08:54:56 +02:00
|
|
|
|
|
|
|
r = xsprintf_dev_num_path_from_sd_device(dev, &filename);
|
|
|
|
if (r < 0)
|
2018-10-22 06:12:19 +02:00
|
|
|
return log_device_debug_errno(dev, r, "Failed to get device path: %m");
|
2012-01-10 01:34:15 +01:00
|
|
|
|
|
|
|
/* remove /dev/{block,char}/$major:$minor */
|
2018-08-29 08:54:56 +02:00
|
|
|
(void) unlink(filename);
|
|
|
|
|
|
|
|
return 0;
|
2003-07-21 05:48:48 +02:00
|
|
|
}
|