udev: export tags of "dead" device nodes to /run/udev/static_node-tags/

This is upstream 84b6ad702e64db534f67ce32d4dd2fec00a16784

Based on a patch by Kay Sievers.

A tag is exported at boot as a symlinks to the device node in the folder
/run/udev/static_node-tags/<tagname>/, if the device node exists.

These tags are cleaned up by udevadm info --cleanup-db, but are otherwise
never removed.

Signed-off-by: Anthony G. Basile <blueness@gentoo.org>
This commit is contained in:
Anthony G. Basile 2013-07-30 14:05:40 -04:00
parent 67ba217a19
commit 6284c1ca41
4 changed files with 91 additions and 16 deletions

View File

@ -33,6 +33,7 @@
#include "path-util.h"
#include "conf-files.h"
#include "strbuf.h"
#include "strv.h"
#define PREALLOC_TOKEN 2048
@ -152,9 +153,9 @@ enum token_type {
TK_A_OWNER_ID, /* uid_t */
TK_A_GROUP_ID, /* gid_t */
TK_A_MODE_ID, /* mode_t */
TK_A_TAG, /* val */
TK_A_STATIC_NODE, /* val */
TK_A_ENV, /* val, attr */
TK_A_TAG, /* val */
TK_A_NAME, /* val */
TK_A_DEVLINK, /* val */
TK_A_ATTR, /* val, attr */
@ -2508,16 +2509,21 @@ int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event
}
}
void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
int udev_rules_apply_static_dev_perms(struct udev_rules *rules)
{
struct token *cur;
struct token *rule;
uid_t uid = 0;
gid_t gid = 0;
mode_t mode = 0;
_cleanup_strv_free_ char **tags = NULL;
char **t;
FILE *f = NULL;
_cleanup_free_ char *path = NULL;
int r = 0;
if (rules->tokens == NULL)
return;
return 0;
cur = &rules->tokens[0];
rule = cur;
@ -2534,6 +2540,8 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
uid = 0;
gid = 0;
mode = 0;
strv_free(tags);
tags = NULL;
break;
case TK_A_OWNER_ID:
uid = cur->key.uid;
@ -2543,19 +2551,57 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
break;
case TK_A_MODE_ID:
mode = cur->key.mode;
break;
case TK_A_TAG:
r = strv_extend(&tags, rules_str(rules, cur->key.value_off));
if (r < 0)
goto finish;
break;
case TK_A_STATIC_NODE: {
char filename[UTIL_PATH_SIZE];
char device_node[UTIL_PATH_SIZE];
char tags_dir[UTIL_PATH_SIZE];
char tag_symlink[UTIL_PATH_SIZE];
struct stat stats;
/* we assure, that the permissions tokens are sorted before the static token */
if (mode == 0 && uid == 0 && gid == 0)
if (mode == 0 && uid == 0 && gid == 0 && tags == NULL)
goto next;
strscpyl(filename, sizeof(filename), "/dev/", rules_str(rules, cur->key.value_off), NULL);
if (stat(filename, &stats) != 0)
strscpyl(device_node, sizeof(device_node), "/dev/", rules_str(rules, cur->key.value_off), NULL);
if (stat(device_node, &stats) != 0)
goto next;
if (!S_ISBLK(stats.st_mode) && !S_ISCHR(stats.st_mode))
goto next;
if (tags) {
/* Export the tags to a directory as symlinks, allowing otherwise dead nodes to be tagged */
STRV_FOREACH(t, tags) {
_cleanup_free_ char *unescaped_filename = NULL;
strscpyl(tags_dir, sizeof(tags_dir), "/run/udev/static_node-tags/", *t, "/", NULL);
r = mkdir_p(tags_dir, 0755);
if (r < 0) {
log_error("failed to create %s: %s\n", tags_dir, strerror(-r));
return r;
}
unescaped_filename = xescape(rules_str(rules, cur->key.value_off), "/.");
strscpyl(tag_symlink, sizeof(tag_symlink), tags_dir, unescaped_filename, NULL);
r = symlink(device_node, tag_symlink);
if (r < 0 && errno != EEXIST) {
log_error("failed to create symlink %s -> %s: %s\n", tag_symlink, device_node, strerror(errno));
return -errno;
} else
r = 0;
}
}
/* don't touch the permissions if only the tags were set */
if (mode == 0 && uid == 0 && gid == 0)
goto next;
if (mode == 0) {
if (gid > 0)
mode = 0660;
@ -2563,21 +2609,28 @@ void udev_rules_apply_static_dev_perms(struct udev_rules *rules)
mode = 0600;
}
if (mode != (stats.st_mode & 01777)) {
chmod(filename, mode);
log_debug("chmod '%s' %#o\n", filename, mode);
r = chmod(device_node, mode);
if (r < 0) {
log_error("failed to chmod '%s' %#o\n", device_node, mode);
return -errno;
} else
log_debug("chmod '%s' %#o\n", device_node, mode);
}
if ((uid != 0 && uid != stats.st_uid) || (gid != 0 && gid != stats.st_gid)) {
if (chown(filename, uid, gid) < 0)
log_debug("Following chown failed!");
log_debug("chown '%s' %u %u\n", filename, uid, gid);
r = chown(device_node, uid, gid);
if (r < 0) {
log_error("failed to chown '%s' %u %u \n", device_node, uid, gid);
return -errno;
} else
log_debug("chown '%s' %u %u\n", device_node, uid, gid);
}
utimensat(AT_FDCWD, filename, NULL, 0);
utimensat(AT_FDCWD, device_node, NULL, 0);
break;
}
case TK_END:
return;
goto finish;
}
cur++;
@ -2587,4 +2640,18 @@ next:
cur = rule + rule->rule.token_count;
continue;
}
finish:
if (f) {
fflush(f);
fchmod(fileno(f), 0644);
if (ferror(f) || rename(path, "/run/udev/static_node-tags") < 0) {
r = -errno;
unlink("/run/udev/static_node-tags");
unlink(path);
}
fclose(f);
}
return r;
}

View File

@ -72,7 +72,7 @@ struct udev_rules *udev_rules_new(struct udev *udev, int resolve_names);
struct udev_rules *udev_rules_unref(struct udev_rules *rules);
bool udev_rules_check_timestamp(struct udev_rules *rules);
int udev_rules_apply_to_event(struct udev_rules *rules, struct udev_event *event, const sigset_t *sigmask);
void udev_rules_apply_static_dev_perms(struct udev_rules *rules);
int udev_rules_apply_static_dev_perms(struct udev_rules *rules);
/* udev-event.c */
struct udev_event *udev_event_new(struct udev_device *dev);

View File

@ -251,6 +251,12 @@ static void cleanup_db(struct udev *udev)
closedir(dir);
}
dir = opendir("/run/udev/static_node-tags");
if (dir != NULL) {
cleanup_dir(dir, 0, 2);
closedir(dir);
}
dir = opendir("/run/udev/watch");
if (dir != NULL) {
cleanup_dir(dir, 0, 1);

View File

@ -1225,7 +1225,9 @@ int main(int argc, char *argv[])
}
log_debug("set children_max to %u\n", children_max);
udev_rules_apply_static_dev_perms(rules);
rc = udev_rules_apply_static_dev_perms(rules);
if (rc < 0)
log_error("failed to apply permissions on static device nodes - %s\n", strerror(-rc));
udev_list_node_init(&event_list);
udev_list_node_init(&worker_list);