]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/udev/udev-node.c
tree-wide: use -EBADF for fd initialization
[thirdparty/systemd.git] / src / udev / udev-node.c
index 4e7dca06de0d86819c7edf4ad9f8237ffc95e497..1990282c75427e6678836f6485f8f7a2a1b3d6c2 100644 (file)
@@ -14,6 +14,7 @@
 #include "format-util.h"
 #include "fs-util.h"
 #include "hexdecoct.h"
+#include "label.h"
 #include "mkdir-label.h"
 #include "parse-util.h"
 #include "path-util.h"
@@ -69,8 +70,6 @@ int udev_node_cleanup(void) {
 }
 
 static int node_symlink(sd_device *dev, const char *devnode, const char *slink) {
-        _cleanup_free_ char *target = NULL;
-        const char *id, *slink_tmp;
         struct stat st;
         int r;
 
@@ -91,35 +90,89 @@ static int node_symlink(sd_device *dev, const char *devnode, const char *slink)
         } else if (errno != ENOENT)
                 return log_device_debug_errno(dev, errno, "Failed to lstat() '%s': %m", slink);
 
-        /* use relative link */
-        r = path_make_relative_parent(slink, devnode, &target);
+        r = mkdir_parents_label(slink, 0755);
         if (r < 0)
-                return log_device_debug_errno(dev, r, "Failed to get relative path from '%s' to '%s': %m", slink, devnode);
+                return log_device_debug_errno(dev, r, "Failed to create parent directory of '%s': %m", slink);
 
-        r = device_get_device_id(dev, &id);
+        /* use relative link */
+        r = symlink_atomic_full_label(devnode, slink, /* make_relative = */ true);
         if (r < 0)
-                return log_device_debug_errno(dev, r, "Failed to get device id: %m");
+                return log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink, devnode);
 
-        slink_tmp = strjoina(slink, ".tmp-", id);
-        (void) unlink(slink_tmp);
+        log_device_debug(dev, "Successfully created symlink '%s' to '%s'", slink, devnode);
+        return 0;
+}
 
-        r = mkdir_parents_label(slink_tmp, 0755);
-        if (r < 0)
-                return log_device_debug_errno(dev, r, "Failed to create parent directory of '%s': %m", slink_tmp);
+static int stack_directory_read_one(int dirfd, const char *id, bool is_symlink, char **devnode, int *priority) {
+        int tmp_prio, r;
 
-        mac_selinux_create_file_prepare(slink_tmp, S_IFLNK);
-        r = RET_NERRNO(symlink(target, slink_tmp));
-        mac_selinux_create_file_clear();
-        if (r < 0)
-                return log_device_debug_errno(dev, r, "Failed to create symlink '%s' to '%s': %m", slink_tmp, target);
+        assert(dirfd >= 0);
+        assert(id);
+        assert(devnode);
+        assert(priority);
 
-        if (rename(slink_tmp, slink) < 0) {
-                r = log_device_debug_errno(dev, errno, "Failed to rename '%s' to '%s': %m", slink_tmp, slink);
-                (void) unlink(slink_tmp);
-                return r;
+        if (is_symlink) {
+                _cleanup_free_ char *buf = NULL;
+                char *colon;
+
+                /* New format. The devnode and priority can be obtained from symlink. */
+
+                r = readlinkat_malloc(dirfd, id, &buf);
+                if (r < 0)
+                        return r;
+
+                colon = strchr(buf, ':');
+                if (!colon || colon == buf)
+                        return -EINVAL;
+
+                *colon = '\0';
+
+                /* Of course, this check is racy, but it is not necessary to be perfect. Even if the device
+                 * node will be removed after this check, we will receive 'remove' uevent, and the invalid
+                 * symlink will be removed during processing the event. The check is just for shortening the
+                 * timespan that the symlink points to a non-existing device node. */
+                if (access(colon + 1, F_OK) < 0)
+                        return -errno;
+
+                r = safe_atoi(buf, &tmp_prio);
+                if (r < 0)
+                        return r;
+
+                if (*devnode && tmp_prio <= *priority)
+                        return 0; /* Unchanged */
+
+                r = free_and_strdup(devnode, colon + 1);
+                if (r < 0)
+                        return r;
+
+        } else {
+                _cleanup_(sd_device_unrefp) sd_device *dev = NULL;
+                const char *val;
+
+                /* Old format. The devnode and priority must be obtained from uevent and udev database. */
+
+                r = sd_device_new_from_device_id(&dev, id);
+                if (r < 0)
+                        return r;
+
+                r = device_get_devlink_priority(dev, &tmp_prio);
+                if (r < 0)
+                        return r;
+
+                if (*devnode && tmp_prio <= *priority)
+                        return 0; /* Unchanged */
+
+                r = sd_device_get_devname(dev, &val);
+                if (r < 0)
+                        return r;
+
+                r = free_and_strdup(devnode, val);
+                if (r < 0)
+                        return r;
         }
 
-        return 0;
+        *priority = tmp_prio;
+        return 1; /* Updated */
 }
 
 static int stack_directory_find_prioritized_devnode(sd_device *dev, const char *dirname, bool add, char **ret) {
@@ -160,8 +213,6 @@ static int stack_directory_find_prioritized_devnode(sd_device *dev, const char *
                 return r;
 
         FOREACH_DIRENT_ALL(de, dir, break) {
-                int tmp_prio;
-
                 if (de->d_name[0] == '.')
                         continue;
 
@@ -169,61 +220,14 @@ static int stack_directory_find_prioritized_devnode(sd_device *dev, const char *
                 if (streq(de->d_name, id))
                         continue;
 
-                if (de->d_type == DT_LNK) {
-                        _cleanup_free_ char *buf = NULL;
-                        char *colon;
-
-                        /* New format. The devnode and priority can be obtained from symlink. */
-
-                        r = readlinkat_malloc(dirfd(dir), de->d_name, &buf);
-                        if (r < 0) {
-                                log_device_debug_errno(dev, r, "Failed to read symlink %s, ignoring: %m", de->d_name);
-                                continue;
-                        }
-
-                        colon = strchr(buf, ':');
-                        if (!colon || colon == buf)
-                                continue;
-
-                        *colon = '\0';
-
-                        if (safe_atoi(buf, &tmp_prio) < 0)
-                                continue;
-
-                        if (devnode && tmp_prio <= priority)
-                                continue;
-
-                        r = free_and_strdup(&devnode, colon + 1);
-                        if (r < 0)
-                                return r;
-
-                } else if (de->d_type == DT_REG) {
-                        _cleanup_(sd_device_unrefp) sd_device *tmp_dev = NULL;
-                        const char *val;
-
-                        /* Old format. The devnode and priority must be obtained from uevent and
-                         * udev database files. */
-
-                        if (sd_device_new_from_device_id(&tmp_dev, de->d_name) < 0)
-                                continue;
-
-                        if (device_get_devlink_priority(tmp_dev, &tmp_prio) < 0)
-                                continue;
-
-                        if (devnode && tmp_prio <= priority)
-                                continue;
-
-                        if (sd_device_get_devname(tmp_dev, &val) < 0)
-                                continue;
-
-                        r = free_and_strdup(&devnode, val);
-                        if (r < 0)
-                                return r;
-
-                } else
+                if (!IN_SET(de->d_type, DT_LNK, DT_REG))
                         continue;
 
-                priority = tmp_prio;
+                r = stack_directory_read_one(dirfd(dir), de->d_name, /* is_symlink = */ de->d_type == DT_LNK, &devnode, &priority);
+                if (r < 0) {
+                        log_debug_errno(r, "Failed to read '%s/%s', ignoring: %m", dirname, de->d_name);
+                        continue;
+                }
         }
 
         *ret = TAKE_PTR(devnode);
@@ -277,7 +281,7 @@ static int stack_directory_update(sd_device *dev, int fd, bool add) {
 }
 
 static int stack_directory_open(const char *dirname) {
-        _cleanup_close_ int fd = -1;
+        _cleanup_close_ int fd = -EBADF;
         int r;
 
         assert(dirname);
@@ -294,7 +298,7 @@ static int stack_directory_open(const char *dirname) {
 }
 
 static int stack_directory_lock(int dirfd) {
-        _cleanup_close_ int fd = -1;
+        _cleanup_close_ int fd = -EBADF;
 
         assert(dirfd >= 0);
 
@@ -383,7 +387,7 @@ static int stack_directory_get_name(const char *slink, char **ret) {
 
 static int link_update(sd_device *dev, const char *slink, bool add) {
         _cleanup_free_ char *dirname = NULL, *devnode = NULL;
-        _cleanup_close_ int dirfd = -1, lockfd = -1;
+        _cleanup_close_ int dirfd = -EBADF, lockfd = -EBADF;
         int r;
 
         assert(dev);
@@ -620,7 +624,7 @@ int udev_node_apply_permissions(
                 OrderedHashmap *seclabel_list) {
 
         const char *devnode;
-        _cleanup_close_ int node_fd = -1;
+        _cleanup_close_ int node_fd = -EBADF;
         int r;
 
         assert(dev);
@@ -650,7 +654,7 @@ int static_node_apply_permissions(
                 char **tags) {
 
         _cleanup_free_ char *unescaped_filename = NULL;
-        _cleanup_close_ int node_fd = -1;
+        _cleanup_close_ int node_fd = -EBADF;
         const char *devnode;
         struct stat stats;
         int r;