mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-10-31 16:21:11 +03:00
95776dc6ec
This scheme is more consistent and makes it obvious if a match happens against the event device only, or the full chain of parent devices. The old key names are now: BUS -> SUBSYSTEMS ID -> KERNELS SYSFS -> ATTRS DRIVER -> DRIVERS Match keys for the event device: KERNEL SUBSYSTEM ATTR DRIVER (in a future release, for now the same as DRIVERS) Match keys for all devices along the parent device chain: KERNELS SUBSYSTEMS ATTRS DRIVERS ID, BUS, SYSFS are no longer mentioned in the man page but still work. DRIVER must be converted to DRIVERS to match the new scheme. For now, an error is logged, if DRIVER is used. In a future release, the DRIVER key behaviour will change.
342 lines
8.6 KiB
C
342 lines
8.6 KiB
C
/*
|
|
* udev-node.c - device node handling
|
|
*
|
|
* Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com>
|
|
* Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation version 2 of the License.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*
|
|
*/
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <stddef.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <grp.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "udev.h"
|
|
#include "udev_rules.h"
|
|
#include "udev_selinux.h"
|
|
|
|
|
|
int udev_node_mknod(struct udevice *udev, const char *file, dev_t devt, mode_t mode, uid_t uid, gid_t gid)
|
|
{
|
|
struct stat stats;
|
|
int retval = 0;
|
|
|
|
if (major(devt) != 0 && strcmp(udev->dev->subsystem, "block") == 0)
|
|
mode |= S_IFBLK;
|
|
else
|
|
mode |= S_IFCHR;
|
|
|
|
if (stat(file, &stats) != 0)
|
|
goto create;
|
|
|
|
/* preserve node with already correct numbers, to prevent changing the inode number */
|
|
if ((stats.st_mode & S_IFMT) == (mode & S_IFMT) && (stats.st_rdev == devt)) {
|
|
info("preserve file '%s', because it has correct dev_t", file);
|
|
selinux_setfilecon(file, udev->dev->kernel, stats.st_mode);
|
|
goto perms;
|
|
}
|
|
|
|
if (unlink(file) != 0)
|
|
err("unlink(%s) failed: %s", file, strerror(errno));
|
|
else
|
|
dbg("already present file '%s' unlinked", file);
|
|
|
|
create:
|
|
selinux_setfscreatecon(file, udev->dev->kernel, mode);
|
|
retval = mknod(file, mode, devt);
|
|
selinux_resetfscreatecon();
|
|
if (retval != 0) {
|
|
err("mknod(%s, %#o, %u, %u) failed: %s",
|
|
file, mode, major(devt), minor(devt), strerror(errno));
|
|
goto exit;
|
|
}
|
|
|
|
perms:
|
|
dbg("chmod(%s, %#o)", file, mode);
|
|
if (chmod(file, mode) != 0) {
|
|
err("chmod(%s, %#o) failed: %s", file, mode, strerror(errno));
|
|
goto exit;
|
|
}
|
|
|
|
if (uid != 0 || gid != 0) {
|
|
dbg("chown(%s, %u, %u)", file, uid, gid);
|
|
if (chown(file, uid, gid) != 0) {
|
|
err("chown(%s, %u, %u) failed: %s",
|
|
file, uid, gid, strerror(errno));
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return retval;
|
|
}
|
|
|
|
static int udev_node_symlink(struct udevice *udev, const char *linktarget, const char *filename)
|
|
{
|
|
char target[PATH_SIZE];
|
|
int len;
|
|
|
|
/* look if symlink already exists */
|
|
len = readlink(filename, target, sizeof(target));
|
|
if (len > 0) {
|
|
target[len] = '\0';
|
|
if (strcmp(linktarget, target) == 0) {
|
|
info("preserving symlink '%s' to '%s'", filename, linktarget);
|
|
selinux_setfilecon(filename, NULL, S_IFLNK);
|
|
goto exit;
|
|
} else {
|
|
info("link '%s' points to different target '%s', delete it", filename, target);
|
|
unlink(filename);
|
|
}
|
|
}
|
|
|
|
/* create link */
|
|
info("creating symlink '%s' to '%s'", filename, linktarget);
|
|
selinux_setfscreatecon(filename, NULL, S_IFLNK);
|
|
if (symlink(linktarget, filename) != 0)
|
|
err("symlink(%s, %s) failed: %s", linktarget, filename, strerror(errno));
|
|
selinux_resetfscreatecon();
|
|
|
|
exit:
|
|
return 0;
|
|
}
|
|
|
|
int udev_node_add(struct udevice *udev, struct udevice *udev_old)
|
|
{
|
|
char filename[PATH_SIZE];
|
|
struct name_entry *name_loop;
|
|
uid_t uid;
|
|
gid_t gid;
|
|
int tail;
|
|
int i;
|
|
int retval = 0;
|
|
|
|
snprintf(filename, sizeof(filename), "%s/%s", udev_root, udev->name);
|
|
filename[sizeof(filename)-1] = '\0';
|
|
|
|
/* create parent directories if needed */
|
|
if (strchr(udev->name, '/'))
|
|
create_path(filename);
|
|
|
|
if (strcmp(udev->owner, "root") == 0)
|
|
uid = 0;
|
|
else {
|
|
char *endptr;
|
|
unsigned long id;
|
|
|
|
id = strtoul(udev->owner, &endptr, 10);
|
|
if (endptr[0] == '\0')
|
|
uid = (uid_t) id;
|
|
else
|
|
uid = lookup_user(udev->owner);
|
|
}
|
|
|
|
if (strcmp(udev->group, "root") == 0)
|
|
gid = 0;
|
|
else {
|
|
char *endptr;
|
|
unsigned long id;
|
|
|
|
id = strtoul(udev->group, &endptr, 10);
|
|
if (endptr[0] == '\0')
|
|
gid = (gid_t) id;
|
|
else
|
|
gid = lookup_group(udev->group);
|
|
}
|
|
|
|
info("creating device node '%s', major = '%d', minor = '%d', " "mode = '%#o', uid = '%d', gid = '%d'",
|
|
filename, major(udev->devt), minor(udev->devt), udev->mode, uid, gid);
|
|
|
|
if (!udev->test_run)
|
|
if (udev_node_mknod(udev, filename, udev->devt, udev->mode, uid, gid) != 0) {
|
|
retval = -1;
|
|
goto exit;
|
|
}
|
|
|
|
setenv("DEVNAME", filename, 1);
|
|
|
|
/* create all_partitions if requested */
|
|
if (udev->partitions) {
|
|
char partitionname[PATH_SIZE];
|
|
char *attr;
|
|
int range;
|
|
|
|
/* take the maximum registered minor range */
|
|
attr = sysfs_attr_get_value(udev->dev->devpath, "range");
|
|
if (attr) {
|
|
range = atoi(attr);
|
|
if (range > 1)
|
|
udev->partitions = range-1;
|
|
}
|
|
info("creating device partition nodes '%s[1-%i]'", filename, udev->partitions);
|
|
if (!udev->test_run) {
|
|
for (i = 1; i <= udev->partitions; i++) {
|
|
dev_t part_devt;
|
|
|
|
snprintf(partitionname, sizeof(partitionname), "%s%d", filename, i);
|
|
partitionname[sizeof(partitionname)-1] = '\0';
|
|
part_devt = makedev(major(udev->devt), minor(udev->devt) + i);
|
|
udev_node_mknod(udev, partitionname, part_devt, udev->mode, uid, gid);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* create symlink(s) if requested */
|
|
if (!list_empty(&udev->symlink_list)) {
|
|
char symlinks[512] = "";
|
|
|
|
list_for_each_entry(name_loop, &udev->symlink_list, node) {
|
|
char linktarget[PATH_SIZE];
|
|
|
|
snprintf(filename, sizeof(filename), "%s/%s", udev_root, name_loop->name);
|
|
filename[sizeof(filename)-1] = '\0';
|
|
|
|
dbg("symlink '%s' to node '%s' requested", filename, udev->name);
|
|
if (!udev->test_run)
|
|
if (strchr(filename, '/'))
|
|
create_path(filename);
|
|
|
|
/* optimize relative link */
|
|
linktarget[0] = '\0';
|
|
i = 0;
|
|
tail = 0;
|
|
while (udev->name[i] && (udev->name[i] == name_loop->name[i])) {
|
|
if (udev->name[i] == '/')
|
|
tail = i+1;
|
|
i++;
|
|
}
|
|
while (name_loop->name[i] != '\0') {
|
|
if (name_loop->name[i] == '/')
|
|
strlcat(linktarget, "../", sizeof(linktarget));
|
|
i++;
|
|
}
|
|
|
|
strlcat(linktarget, &udev->name[tail], sizeof(linktarget));
|
|
|
|
info("creating symlink '%s' to '%s'", filename, linktarget);
|
|
if (!udev->test_run)
|
|
udev_node_symlink(udev, linktarget, filename);
|
|
|
|
strlcat(symlinks, filename, sizeof(symlinks));
|
|
strlcat(symlinks, " ", sizeof(symlinks));
|
|
}
|
|
|
|
remove_trailing_chars(symlinks, ' ');
|
|
setenv("DEVLINKS", symlinks, 1);
|
|
}
|
|
|
|
exit:
|
|
selinux_exit();
|
|
return retval;
|
|
}
|
|
|
|
void udev_node_remove_symlinks(struct udevice *udev)
|
|
{
|
|
char filename[PATH_SIZE];
|
|
struct name_entry *name_loop;
|
|
struct stat stats;
|
|
|
|
if (!list_empty(&udev->symlink_list)) {
|
|
char symlinks[512] = "";
|
|
|
|
list_for_each_entry(name_loop, &udev->symlink_list, node) {
|
|
snprintf(filename, sizeof(filename), "%s/%s", udev_root, name_loop->name);
|
|
filename[sizeof(filename)-1] = '\0';
|
|
|
|
if (stat(filename, &stats) != 0) {
|
|
dbg("symlink '%s' not found", filename);
|
|
continue;
|
|
}
|
|
if (udev->devt && stats.st_rdev != udev->devt) {
|
|
info("symlink '%s' points to a different device, skip removal", filename);
|
|
continue;
|
|
}
|
|
|
|
info("removing symlink '%s'", filename);
|
|
if (!udev->test_run) {
|
|
unlink(filename);
|
|
|
|
if (strchr(filename, '/'))
|
|
delete_path(filename);
|
|
}
|
|
|
|
strlcat(symlinks, filename, sizeof(symlinks));
|
|
strlcat(symlinks, " ", sizeof(symlinks));
|
|
}
|
|
|
|
remove_trailing_chars(symlinks, ' ');
|
|
if (symlinks[0] != '\0')
|
|
setenv("DEVLINKS", symlinks, 1);
|
|
}
|
|
}
|
|
|
|
int udev_node_remove(struct udevice *udev)
|
|
{
|
|
char filename[PATH_SIZE];
|
|
char partitionname[PATH_SIZE];
|
|
struct stat stats;
|
|
int retval;
|
|
int num;
|
|
|
|
udev_node_remove_symlinks(udev);
|
|
|
|
snprintf(filename, sizeof(filename), "%s/%s", udev_root, udev->name);
|
|
filename[sizeof(filename)-1] = '\0';
|
|
|
|
if (stat(filename, &stats) != 0) {
|
|
dbg("device node '%s' not found", filename);
|
|
return -1;
|
|
}
|
|
if (udev->devt && stats.st_rdev != udev->devt) {
|
|
info("device node '%s' points to a different device, skip removal", filename);
|
|
return -1;
|
|
}
|
|
|
|
info("removing device node '%s'", filename);
|
|
retval = unlink_secure(filename);
|
|
if (retval)
|
|
return retval;
|
|
|
|
setenv("DEVNAME", filename, 1);
|
|
|
|
num = udev->partitions;
|
|
if (num > 0) {
|
|
int i;
|
|
|
|
info("removing all_partitions '%s[1-%i]'", filename, num);
|
|
if (num > 255) {
|
|
info("garbage from udev database, skip all_partitions removal");
|
|
return -1;
|
|
}
|
|
for (i = 1; i <= num; i++) {
|
|
snprintf(partitionname, sizeof(partitionname), "%s%d", filename, i);
|
|
partitionname[sizeof(partitionname)-1] = '\0';
|
|
unlink_secure(partitionname);
|
|
}
|
|
}
|
|
|
|
if (strchr(udev->name, '/'))
|
|
delete_path(filename);
|
|
|
|
return retval;
|
|
}
|