summaryrefslogtreecommitdiff
path: root/udev_node.c
diff options
context:
space:
mode:
authorKay Sievers <kay.sievers@vrfy.org>2007-06-21 02:28:09 +0200
committerKay Sievers <kay.sievers@vrfy.org>2007-06-21 02:28:09 +0200
commite54f0b4ad2f10ecfefe01d607c43f086e9e889fe (patch)
tree537a9d29a45f08c9d4bd3a4fd526b9ccf74888ba /udev_node.c
parent3b62a5690376e8caebf6d49c17e639a3992dbfe7 (diff)
atomically replace existing nodes and symlinks
Based on a patch from Scott James Remnant <scott@ubuntu.com>.
Diffstat (limited to 'udev_node.c')
-rw-r--r--udev_node.c108
1 files changed, 77 insertions, 31 deletions
diff --git a/udev_node.c b/udev_node.c
index 338948fc54..8c0287405c 100644
--- a/udev_node.c
+++ b/udev_node.c
@@ -33,9 +33,11 @@
#include "udev_rules.h"
#include "udev_selinux.h"
+#define TMP_FILE_EXT ".udev-tmp"
int udev_node_mknod(struct udevice *udev, const char *file, dev_t devt, mode_t mode, uid_t uid, gid_t gid)
{
+ char file_tmp[PATH_SIZE + sizeof(TMP_FILE_EXT)];
struct stat stats;
int retval = 0;
@@ -44,28 +46,37 @@ int udev_node_mknod(struct udevice *udev, const char *file, dev_t devt, mode_t m
else
mode |= S_IFCHR;
- if (lstat(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 (lstat(file, &stats) == 0) {
+ 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)
+ goto perms;
+
+ info("atomically replace '%s'", file);
+ strlcpy(file_tmp, file, sizeof(file_tmp));
+ strlcat(file_tmp, TMP_FILE_EXT, sizeof(file_tmp));
+ selinux_setfscreatecon(file_tmp, udev->dev->kernel, mode);
+ retval = mknod(file_tmp, mode, devt);
+ selinux_resetfscreatecon();
if (retval != 0) {
err("mknod(%s, %#o, %u, %u) failed: %s",
- file, mode, major(devt), minor(devt), strerror(errno));
+ file_tmp, mode, major(devt), minor(devt), strerror(errno));
+ goto exit;
+ }
+ retval = rename(file_tmp, file);
+ if (retval != 0) {
+ err("rename(%s, %s) failed: %s",
+ file_tmp, file, strerror(errno));
+ unlink(file_tmp);
goto exit;
}
@@ -84,18 +95,19 @@ perms:
goto exit;
}
}
-
exit:
return retval;
}
static int node_symlink(const char *node, const char *slink)
{
+ struct stat stats;
char target[PATH_SIZE] = "";
- char buf[PATH_SIZE];
+ char slink_tmp[PATH_SIZE + sizeof(TMP_FILE_EXT)];
int i = 0;
int tail = 0;
int len;
+ int retval = 0;
/* use relative link */
while (node[i] && (node[i] == slink[i])) {
@@ -110,28 +122,62 @@ static int node_symlink(const char *node, const char *slink)
}
strlcat(target, &node[tail], sizeof(target));
- /* look if symlink already exists */
- len = readlink(slink, buf, sizeof(buf));
- if (len > 0) {
- buf[len] = '\0';
- if (strcmp(target, buf) == 0) {
- info("preserve already existing symlink '%s' to '%s'", slink, target);
- selinux_setfilecon(slink, NULL, S_IFLNK);
- goto exit;
+ /* preserve link with correct target, do not replace node of other device */
+ if (lstat(slink, &stats) == 0) {
+ if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) {
+ struct stat stats2;
+
+ info("found existing node instead of symlink '%s'", slink);
+ if (lstat(node, &stats2) == 0) {
+ if ((stats.st_mode & S_IFMT) == (stats2.st_mode & S_IFMT) &&
+ stats.st_rdev == stats2.st_rdev) {
+ info("replace device node '%s' with symlink to our node '%s'", slink, node);
+ } else {
+ err("device node '%s' already exists, link '%s' will not overwrite it", node, slink);
+ goto exit;
+ }
+ }
+ } else if (S_ISLNK(stats.st_mode)) {
+ char buf[PATH_SIZE];
+
+ info("found existing symlink '%s'", slink);
+ len = readlink(slink, buf, sizeof(buf));
+ if (len > 0) {
+ buf[len] = '\0';
+ if (strcmp(target, buf) == 0) {
+ info("preserve already existing symlink '%s' to '%s'", slink, target);
+ selinux_setfilecon(slink, NULL, S_IFLNK);
+ goto exit;
+ }
+ }
}
- info("link '%s' points to different target '%s', delete it", slink, buf);
- unlink(slink);
}
- /* create link */
info("creating symlink '%s' to '%s'", slink, target);
selinux_setfscreatecon(slink, NULL, S_IFLNK);
- if (symlink(target, slink) != 0)
- err("symlink(%s, %s) failed: %s", target, slink, strerror(errno));
+ retval = symlink(target, slink);
selinux_resetfscreatecon();
+ if (retval == 0)
+ goto exit;
+ info("atomically replace '%s'", slink);
+ strlcpy(slink_tmp, slink, sizeof(slink_tmp));
+ strlcat(slink_tmp, TMP_FILE_EXT, sizeof(slink_tmp));
+ selinux_setfscreatecon(slink_tmp, NULL, S_IFLNK);
+ retval = symlink(target, slink_tmp);
+ selinux_resetfscreatecon();
+ if (retval != 0) {
+ err("symlink(%s, %s) failed: %s", target, slink_tmp, strerror(errno));
+ goto exit;
+ }
+ retval = rename(slink_tmp, slink);
+ if (retval != 0) {
+ err("rename(%s, %s) failed: %s", slink_tmp, slink, strerror(errno));
+ unlink(slink_tmp);
+ goto exit;
+ }
exit:
- return 0;
+ return retval;
}
static int update_link(struct udevice *udev, const char *name)
@@ -303,7 +349,7 @@ int udev_node_add(struct udevice *udev)
gid = lookup_group(udev->group);
}
- info("creating device node '%s', major = '%d', minor = '%d', " "mode = '%#o', uid = '%d', gid = '%d'",
+ 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)