]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/libsystemd/sd-device/sd-device.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / libsystemd / sd-device / sd-device.c
index 1eb71e17dbfd8bf00d86ab13a484c91d2a131943..584e7def8a546dfe51c30089b87f68229062d96a 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <ctype.h>
 #include <net/if.h>
+#include <sys/ioctl.h>
 #include <sys/types.h>
 
 #include "sd-device.h"
 #include "set.h"
 #include "socket-util.h"
 #include "stat-util.h"
+#include "stdio-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "strxcpyx.h"
 #include "util.h"
 
 int device_new_aux(sd_device **ret) {
-        sd_device *device = NULL;
+        sd_device *device;
 
         assert(ret);
 
@@ -38,7 +40,6 @@ int device_new_aux(sd_device **ret) {
         *device = (sd_device) {
                 .n_ref = 1,
                 .watch_handle = -1,
-                .ifindex = -1,
                 .devmode = (mode_t) -1,
                 .devuid = (uid_t) -1,
                 .devgid = (gid_t) -1,
@@ -138,17 +139,17 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
         assert(_syspath);
 
         /* must be a subdirectory of /sys */
-        if (!path_startswith(_syspath, "/sys/")) {
-                log_debug("sd-device: syspath '%s' is not a subdirectory of /sys", _syspath);
-                return -EINVAL;
-        }
+        if (!path_startswith(_syspath, "/sys/"))
+                return log_debug_errno(SYNTHETIC_ERRNO(EINVAL),
+                                       "sd-device: Syspath '%s' is not a subdirectory of /sys",
+                                       _syspath);
 
         if (verify) {
                 r = chase_symlinks(_syspath, NULL, 0, &syspath);
                 if (r == -ENOENT)
                         return -ENODEV; /* the device does not exist (any more?) */
                 if (r < 0)
-                        return log_debug_errno(r, "sd-device: could not get target of '%s': %m", _syspath);
+                        return log_debug_errno(r, "sd-device: Failed to get target of '%s': %m", _syspath);
 
                 if (!path_startswith(syspath, "/sys")) {
                         _cleanup_free_ char *real_sys = NULL, *new_syspath = NULL;
@@ -157,17 +158,17 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
                         /* /sys is a symlink to somewhere sysfs is mounted on? In that case, we convert the path to real sysfs to "/sys". */
                         r = chase_symlinks("/sys", NULL, 0, &real_sys);
                         if (r < 0)
-                                return log_debug_errno(r, "sd-device: could not chase symlink /sys: %m");
+                                return log_debug_errno(r, "sd-device: Failed to chase symlink /sys: %m");
 
                         p = path_startswith(syspath, real_sys);
-                        if (!p) {
-                                log_debug("sd-device: canonicalized path '%s' does not starts with sysfs mount point '%s'", syspath, real_sys);
-                                return -ENODEV;
-                        }
+                        if (!p)
+                                return log_debug_errno(SYNTHETIC_ERRNO(ENODEV),
+                                                       "sd-device: Canonicalized path '%s' does not starts with sysfs mount point '%s'",
+                                                       syspath, real_sys);
 
                         new_syspath = strjoin("/sys/", p);
                         if (!new_syspath)
-                                return log_oom();
+                                return -ENOMEM;
 
                         free_and_replace(syspath, new_syspath);
                         path_simplify(syspath, false);
@@ -204,9 +205,7 @@ int device_set_syspath(sd_device *device, const char *_syspath, bool verify) {
                 return r;
 
         free_and_replace(device->syspath, syspath);
-
         device->devpath = devpath;
-
         return 0;
 }
 
@@ -226,7 +225,6 @@ _public_ int sd_device_new_from_syspath(sd_device **ret, const char *syspath) {
                 return r;
 
         *ret = TAKE_PTR(device);
-
         return 0;
 }
 
@@ -498,8 +496,6 @@ int device_read_uevent_file(sd_device *device) {
         if (device->uevent_loaded || device->sealed)
                 return 0;
 
-        device->uevent_loaded = true;
-
         r = sd_device_get_syspath(device, &syspath);
         if (r < 0)
                 return r;
@@ -507,14 +503,18 @@ int device_read_uevent_file(sd_device *device) {
         path = strjoina(syspath, "/uevent");
 
         r = read_full_file(path, &uevent, &uevent_len);
-        if (r == -EACCES)
+        if (r == -EACCES) {
                 /* empty uevent files may be write-only */
+                device->uevent_loaded = true;
                 return 0;
-        else if (r == -ENOENT)
+        }
+        if (r == -ENOENT)
                 /* some devices may not have uevent files, see set_syspath() */
                 return 0;
-        else if (r < 0)
-                return log_debug_errno(r, "sd-device: failed to read uevent file '%s': %m", path);
+        if (r < 0)
+                return log_device_debug_errno(device, r, "sd-device: Failed to read uevent file '%s': %m", path);
+
+        device->uevent_loaded = true;
 
         for (i = 0; i < uevent_len; i++)
                 switch (state) {
@@ -533,7 +533,7 @@ int device_read_uevent_file(sd_device *device) {
                                 state = PRE_VALUE;
                         } else if (strchr(NEWLINE, uevent[i])) {
                                 uevent[i] = '\0';
-                                log_debug("sd-device: ignoring invalid uevent line '%s'", key);
+                                log_device_debug(device, "sd-device: Invalid uevent line '%s', ignoring", key);
 
                                 state = PRE_KEY;
                         }
@@ -550,20 +550,20 @@ int device_read_uevent_file(sd_device *device) {
 
                                 r = handle_uevent_line(device, key, value, &major, &minor);
                                 if (r < 0)
-                                        log_debug_errno(r, "sd-device: failed to handle uevent entry '%s=%s': %m", key, value);
+                                        log_device_debug_errno(device, r, "sd-device: Failed to handle uevent entry '%s=%s', ignoring: %m", key, value);
 
                                 state = PRE_KEY;
                         }
 
                         break;
                 default:
-                        assert_not_reached("invalid state when parsing uevent file");
+                        assert_not_reached("Invalid state when parsing uevent file");
                 }
 
         if (major) {
                 r = device_set_devnum(device, major, minor);
                 if (r < 0)
-                        log_debug_errno(r, "sd-device: could not set 'MAJOR=%s' or 'MINOR=%s' from '%s': %m", major, minor, path);
+                        log_device_debug_errno(device, r, "sd-device: Failed to set 'MAJOR=%s' or 'MINOR=%s' from '%s', ignoring: %m", major, minor, path);
         }
 
         return 0;
@@ -573,16 +573,16 @@ _public_ int sd_device_get_ifindex(sd_device *device, int *ifindex) {
         int r;
 
         assert_return(device, -EINVAL);
-        assert_return(ifindex, -EINVAL);
 
         r = device_read_uevent_file(device);
         if (r < 0)
                 return r;
 
-        if (device->ifindex < 0)
+        if (device->ifindex <= 0)
                 return -ENOENT;
 
-        *ifindex = device->ifindex;
+        if (ifindex)
+                *ifindex = device->ifindex;
 
         return 0;
 }
@@ -595,19 +595,20 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
 
         switch (id[0]) {
         case 'b':
-        case 'c':
-        {
-                char type;
-                int maj, min;
+        case 'c': {
+                dev_t devt;
 
-                r = sscanf(id, "%c%i:%i", &type, &maj, &min);
-                if (r != 3)
+                if (isempty(id))
                         return -EINVAL;
 
-                return sd_device_new_from_devnum(ret, type, makedev(maj, min));
+                r = parse_dev(id + 1, &devt);
+                if (r < 0)
+                        return r;
+
+                return sd_device_new_from_devnum(ret, id[0], devt);
         }
-        case 'n':
-        {
+
+        case 'n': {
                 _cleanup_(sd_device_unrefp) sd_device *device = NULL;
                 _cleanup_close_ int sk = -1;
                 struct ifreq ifr = {};
@@ -638,11 +639,10 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
                         return -ENODEV;
 
                 *ret = TAKE_PTR(device);
-
                 return 0;
         }
-        case '+':
-        {
+
+        case '+': {
                 char subsys[PATH_MAX];
                 char *sysname;
 
@@ -656,6 +656,7 @@ _public_ int sd_device_new_from_device_id(sd_device **ret, const char *id) {
 
                 return sd_device_new_from_subsystem_sysname(ret, subsys, sysname);
         }
+
         default:
                 return -EINVAL;
         }
@@ -723,7 +724,6 @@ _public_ int sd_device_get_parent(sd_device *child, sd_device **ret) {
                 return -ENOENT;
 
         *ret = child->parent;
-
         return 0;
 }
 
@@ -742,11 +742,8 @@ int device_set_subsystem(sd_device *device, const char *_subsystem) {
         if (r < 0)
                 return r;
 
-        free_and_replace(device->subsystem, subsystem);
-
         device->subsystem_set = true;
-
-        return 0;
+        return free_and_replace(device->subsystem, subsystem);
 }
 
 static int device_set_drivers_subsystem(sd_device *device, const char *_subsystem) {
@@ -765,9 +762,7 @@ static int device_set_drivers_subsystem(sd_device *device, const char *_subsyste
         if (r < 0)
                 return r;
 
-        free_and_replace(device->driver_subsystem, subsystem);
-
-        return 0;
+        return free_and_replace(device->driver_subsystem, subsystem);
 }
 
 _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
@@ -794,12 +789,12 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
                 else if (path_startswith(device->devpath, "/module/"))
                         r = device_set_subsystem(device, "module");
                 else if (!(drivers = strstr(syspath, "/drivers/")) &&
-                         (path_startswith(device->devpath, "/subsystem/") ||
-                          path_startswith(device->devpath, "/class/") ||
-                          path_startswith(device->devpath, "/bus/")))
+                         PATH_STARTSWITH_SET(device->devpath, "/subsystem/",
+                                                              "/class/",
+                                                              "/bus/"))
                         r = device_set_subsystem(device, "subsystem");
                 if (r < 0 && r != -ENOENT)
-                        return log_debug_errno(r, "sd-device: could not set subsystem for %s: %m", device->devpath);
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem for %s: %m", device->devpath);
 
                 device->subsystem_set = true;
         } else if (!device->driver_subsystem_set)
@@ -822,7 +817,7 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
                                         r = device_set_drivers_subsystem(device, subsys + 1);
                         }
                         if (r < 0 && r != -ENOENT)
-                                return log_debug_errno(r, "sd-device: could not set subsystem for driver %s: %m", device->devpath);
+                                return log_device_debug_errno(device, r, "sd-device: Failed to set subsystem for driver %s: %m", device->devpath);
                 }
 
                 device->driver_subsystem_set = true;
@@ -832,7 +827,6 @@ _public_ int sd_device_get_subsystem(sd_device *device, const char **ret) {
                 return -ENOENT;
 
         *ret = device->subsystem;
-
         return 0;
 }
 
@@ -882,7 +876,6 @@ _public_ int sd_device_get_parent_with_subsystem_devtype(sd_device *child, const
                 return r;
 
         *ret = parent;
-
         return 0;
 }
 
@@ -890,7 +883,6 @@ _public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) {
         int r;
 
         assert_return(device, -EINVAL);
-        assert_return(devnum, -EINVAL);
 
         r = device_read_uevent_file(device);
         if (r < 0)
@@ -899,7 +891,8 @@ _public_ int sd_device_get_devnum(sd_device *device, dev_t *devnum) {
         if (major(device->devnum) <= 0)
                 return -ENOENT;
 
-        *devnum = device->devnum;
+        if (devnum)
+                *devnum = device->devnum;
 
         return 0;
 }
@@ -919,11 +912,8 @@ int device_set_driver(sd_device *device, const char *_driver) {
         if (r < 0)
                 return r;
 
-        free_and_replace(device->driver, driver);
-
         device->driver_set = true;
-
-        return 0;
+        return free_and_replace(device->driver, driver);
 }
 
 _public_ int sd_device_get_driver(sd_device *device, const char **ret) {
@@ -945,18 +935,17 @@ _public_ int sd_device_get_driver(sd_device *device, const char **ret) {
                 if (r >= 0) {
                         r = device_set_driver(device, driver);
                         if (r < 0)
-                                return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath);
+                                return log_device_debug_errno(device, r, "sd-device: Failed to set driver for %s: %m", device->devpath);
                 } else if (r == -ENOENT)
                         device->driver_set = true;
                 else
-                        return log_debug_errno(r, "sd-device: could not set driver for %s: %m", device->devpath);
+                        return log_device_debug_errno(device, r, "sd-device: Failed to set driver for %s: %m", device->devpath);
         }
 
         if (!device->driver)
                 return -ENOENT;
 
         *ret = device->driver;
-
         return 0;
 }
 
@@ -968,7 +957,6 @@ _public_ int sd_device_get_devpath(sd_device *device, const char **devpath) {
         assert(device->devpath[0] == '/');
 
         *devpath = device->devpath;
-
         return 0;
 }
 
@@ -988,7 +976,6 @@ _public_ int sd_device_get_devname(sd_device *device, const char **devname) {
         assert(path_startswith(device->devname, "/dev/"));
 
         *devname = device->devname;
-
         return 0;
 }
 
@@ -998,6 +985,9 @@ static int device_set_sysname(sd_device *device) {
         const char *pos;
         size_t len = 0;
 
+        if (!device->devpath)
+                return -EINVAL;
+
         pos = strrchr(device->devpath, '/');
         if (!pos)
                 return -EINVAL;
@@ -1026,13 +1016,9 @@ static int device_set_sysname(sd_device *device) {
         if (len == 0)
                 sysnum = NULL;
 
-        free_and_replace(device->sysname, sysname);
-
-        device->sysnum = sysnum;
-
         device->sysname_set = true;
-
-        return 0;
+        device->sysnum = sysnum;
+        return free_and_replace(device->sysname, sysname);
 }
 
 _public_ int sd_device_get_sysname(sd_device *device, const char **ret) {
@@ -1050,7 +1036,6 @@ _public_ int sd_device_get_sysname(sd_device *device, const char **ret) {
         assert_return(device->sysname, -ENOENT);
 
         *ret = device->sysname;
-
         return 0;
 }
 
@@ -1070,7 +1055,6 @@ _public_ int sd_device_get_sysnum(sd_device *device, const char **ret) {
                 return -ENOENT;
 
         *ret = device->sysnum;
-
         return 0;
 }
 
@@ -1126,6 +1110,7 @@ int device_add_devlink(sd_device *device, const char *devlink) {
 static int device_add_property_internal_from_string(sd_device *device, const char *str) {
         _cleanup_free_ char *key = NULL;
         char *value;
+        int r;
 
         assert(device);
         assert(str);
@@ -1143,26 +1128,28 @@ static int device_add_property_internal_from_string(sd_device *device, const cha
         if (isempty(++value))
                 value = NULL;
 
-        return device_add_property_internal(device, key, value);
+        /* Add the property to both sd_device::properties and sd_device::properties_db,
+         * as this is called by only handle_db_line(). */
+        r = device_add_property_aux(device, key, value, false);
+        if (r < 0)
+                return r;
+
+        return device_add_property_aux(device, key, value, true);
 }
 
-int device_set_usec_initialized(sd_device *device, const char *initialized) {
-        uint64_t usec_initialized;
+int device_set_usec_initialized(sd_device *device, usec_t when) {
+        char s[DECIMAL_STR_MAX(usec_t)];
         int r;
 
         assert(device);
-        assert(initialized);
 
-        r = safe_atou64(initialized, &usec_initialized);
-        if (r < 0)
-                return r;
+        xsprintf(s, USEC_FMT, when);
 
-        r = device_add_property_internal(device, "USEC_INITIALIZED", initialized);
+        r = device_add_property_internal(device, "USEC_INITIALIZED", s);
         if (r < 0)
                 return r;
 
-        device->usec_initialized = usec_initialized;
-
+        device->usec_initialized = when;
         return 0;
 }
 
@@ -1193,12 +1180,19 @@ static int handle_db_line(sd_device *device, char key, const char *value) {
                         return r;
 
                 break;
-        case 'I':
-                r = device_set_usec_initialized(device, value);
+        case 'I': {
+                usec_t t;
+
+                r = safe_atou64(value, &t);
+                if (r < 0)
+                        return r;
+
+                r = device_set_usec_initialized(device, t);
                 if (r < 0)
                         return r;
 
                 break;
+        }
         case 'L':
                 r = safe_atoi(value, &device->devlink_priority);
                 if (r < 0)
@@ -1212,7 +1206,7 @@ static int handle_db_line(sd_device *device, char key, const char *value) {
 
                 break;
         default:
-                log_debug("device db: unknown key '%c'", key);
+                log_device_debug(device, "sd-device: Unknown key '%c' in device db, ignoring", key);
         }
 
         return 0;
@@ -1241,9 +1235,9 @@ int device_get_id_filename(sd_device *device, const char **ret) {
                                      major(devnum), minor(devnum));
                         if (r < 0)
                                 return -ENOMEM;
-                } else if (sd_device_get_ifindex(device, &ifindex) >= 0 && ifindex > 0) {
+                } else if (sd_device_get_ifindex(device, &ifindex) >= 0) {
                         /* use netdev ifindex — n3 */
-                        r = asprintf(&id, "n%u", ifindex);
+                        r = asprintf(&id, "n%u", (unsigned) ifindex);
                         if (r < 0)
                                 return -ENOMEM;
                 } else {
@@ -1276,17 +1270,14 @@ int device_get_id_filename(sd_device *device, const char **ret) {
         }
 
         *ret = device->id_filename;
-
         return 0;
 }
 
-int device_read_db_aux(sd_device *device, bool force) {
+int device_read_db_internal_filename(sd_device *device, const char *filename) {
         _cleanup_free_ char *db = NULL;
-        char *path;
-        const char *id, *value;
+        const char *value;
+        size_t db_len, i;
         char key;
-        size_t db_len;
-        unsigned i;
         int r;
 
         enum {
@@ -1297,28 +1288,22 @@ int device_read_db_aux(sd_device *device, bool force) {
                 INVALID_LINE,
         } state = PRE_KEY;
 
-        if (device->db_loaded || (!force && device->sealed))
-                return 0;
-
-        device->db_loaded = true;
-
-        r = device_get_id_filename(device, &id);
-        if (r < 0)
-                return r;
-
-        path = strjoina("/run/udev/data/", id);
+        assert(device);
+        assert(filename);
 
-        r = read_full_file(path, &db, &db_len);
+        r = read_full_file(filename, &db, &db_len);
         if (r < 0) {
                 if (r == -ENOENT)
                         return 0;
-                else
-                        return log_debug_errno(r, "sd-device: failed to read db '%s': %m", path);
+
+                return log_device_debug_errno(device, r, "sd-device: Failed to read db '%s': %m", filename);
         }
 
         /* devices with a database entry are initialized */
         device->is_initialized = true;
 
+        device->db_loaded = true;
+
         for (i = 0; i < db_len; i++) {
                 switch (state) {
                 case PRE_KEY:
@@ -1331,7 +1316,7 @@ int device_read_db_aux(sd_device *device, bool force) {
                         break;
                 case KEY:
                         if (db[i] != ':') {
-                                log_debug("sd-device: ignoring invalid db entry with key '%c'", key);
+                                log_device_debug(device, "sd-device: Invalid db entry with key '%c', ignoring", key);
 
                                 state = INVALID_LINE;
                         } else {
@@ -1357,37 +1342,48 @@ int device_read_db_aux(sd_device *device, bool force) {
                                 db[i] = '\0';
                                 r = handle_db_line(device, key, value);
                                 if (r < 0)
-                                        log_debug_errno(r, "sd-device: failed to handle db entry '%c:%s': %m", key, value);
+                                        log_device_debug_errno(device, r, "sd-device: Failed to handle db entry '%c:%s', ignoring: %m", key, value);
 
                                 state = PRE_KEY;
                         }
 
                         break;
                 default:
-                        assert_not_reached("invalid state when parsing db");
+                        return log_device_debug_errno(device, SYNTHETIC_ERRNO(EINVAL), "sd-device: invalid db syntax.");
                 }
         }
 
         return 0;
 }
 
-static int device_read_db(sd_device *device) {
-        return device_read_db_aux(device, false);
+int device_read_db_internal(sd_device *device, bool force) {
+        const char *id, *path;
+        int r;
+
+        assert(device);
+
+        if (device->db_loaded || (!force && device->sealed))
+                return 0;
+
+        r = device_get_id_filename(device, &id);
+        if (r < 0)
+                return r;
+
+        path = strjoina("/run/udev/data/", id);
+
+        return device_read_db_internal_filename(device, path);
 }
 
-_public_ int sd_device_get_is_initialized(sd_device *device, int *initialized) {
+_public_ int sd_device_get_is_initialized(sd_device *device) {
         int r;
 
         assert_return(device, -EINVAL);
-        assert_return(initialized, -EINVAL);
 
         r = device_read_db(device);
         if (r < 0)
                 return r;
 
-        *initialized = device->is_initialized;
-
-        return 0;
+        return device->is_initialized;
 }
 
 _public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *usec) {
@@ -1413,7 +1409,6 @@ _public_ int sd_device_get_usec_since_initialized(sd_device *device, uint64_t *u
                 return -EIO;
 
         *usec = now_ts - device->usec_initialized;
-
         return 0;
 }
 
@@ -1473,7 +1468,7 @@ _public_ const char *sd_device_get_devlink_next(sd_device *device) {
         return v;
 }
 
-static int device_properties_prepare(sd_device *device) {
+int device_properties_prepare(sd_device *device) {
         int r;
 
         assert(device);
@@ -1556,7 +1551,6 @@ _public_ const char *sd_device_get_property_first(sd_device *device, const char
 
         if (_value)
                 *_value = value;
-
         return key;
 }
 
@@ -1578,7 +1572,6 @@ _public_ const char *sd_device_get_property_next(sd_device *device, const char *
 
         if (_value)
                 *_value = value;
-
         return key;
 }
 
@@ -1678,7 +1671,6 @@ _public_ int sd_device_get_property_value(sd_device *device, const char *key, co
 
         assert_return(device, -EINVAL);
         assert_return(key, -EINVAL);
-        assert_return(_value, -EINVAL);
 
         r = device_properties_prepare(device);
         if (r < 0)
@@ -1688,8 +1680,8 @@ _public_ int sd_device_get_property_value(sd_device *device, const char *key, co
         if (!value)
                 return -ENOENT;
 
-        *_value = value;
-
+        if (_value)
+                *_value = value;
         return 0;
 }
 
@@ -1716,8 +1708,7 @@ static int device_add_sysattr_value(sd_device *device, const char *_key, char *v
         r = hashmap_put(device->sysattr_values, key, value);
         if (r < 0)
                 return r;
-
-        key = NULL;
+        TAKE_PTR(key);
 
         return 0;
 }
@@ -1734,7 +1725,6 @@ static int device_get_sysattr_value(sd_device *device, const char *_key, const c
 
         if (_value)
                 *_value = value;
-
         return 0;
 }
 
@@ -1818,25 +1808,19 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
 
 static void device_remove_sysattr_value(sd_device *device, const char *_key) {
         _cleanup_free_ char *key = NULL;
-        _cleanup_free_ char *value = NULL;
 
         assert(device);
         assert(_key);
 
-        value = hashmap_remove2(device->sysattr_values, _key, (void **) &key);
-
-        return;
+        free(hashmap_remove2(device->sysattr_values, _key, (void **) &key));
 }
 
 /* set the attribute and save it in the cache. If a NULL value is passed the
  * attribute is cleared from the cache */
 _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr, const char *_value) {
-        _cleanup_close_ int fd = -1;
         _cleanup_free_ char *value = NULL;
-        const char *syspath;
-        char *path;
-        size_t len = 0;
-        ssize_t size;
+        const char *syspath, *path;
+        size_t len;
         int r;
 
         assert_return(device, -EINVAL);
@@ -1844,7 +1828,6 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
 
         if (!_value) {
                 device_remove_sysattr_value(device, sysattr);
-
                 return 0;
         }
 
@@ -1854,25 +1837,6 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
 
         path = strjoina(syspath, "/", sysattr);
 
-        fd = open(path, O_WRONLY | O_CLOEXEC | O_NOFOLLOW);
-        if (fd < 0) {
-                if (errno == ELOOP)
-                        return -EINVAL;
-                if (errno == EISDIR)
-                        return -EISDIR;
-
-                value = strdup("");
-                if (!value)
-                        return -ENOMEM;
-
-                r = device_add_sysattr_value(device, sysattr, value);
-                if (r < 0)
-                        return r;
-                value = NULL;
-
-                return -ENXIO;
-        }
-
         len = strlen(_value);
 
         /* drop trailing newlines */
@@ -1887,17 +1851,29 @@ _public_ int sd_device_set_sysattr_value(sd_device *device, const char *sysattr,
         if (!value)
                 return -ENOMEM;
 
-        size = write(fd, value, len);
-        if (size < 0)
-                return -errno;
+        r = write_string_file(path, value, WRITE_STRING_FILE_DISABLE_BUFFER | WRITE_STRING_FILE_NOFOLLOW);
+        if (r < 0) {
+                if (r == -ELOOP)
+                        return -EINVAL;
+                if (r == -EISDIR)
+                        return r;
 
-        if ((size_t)size != len)
-                return -EIO;
+                r = free_and_strdup(&value, "");
+                if (r < 0)
+                        return r;
+
+                r = device_add_sysattr_value(device, sysattr, value);
+                if (r < 0)
+                        return r;
+                TAKE_PTR(value);
+
+                return -ENXIO;
+        }
 
         r = device_add_sysattr_value(device, sysattr, value);
         if (r < 0)
                 return r;
-        value = NULL;
+        TAKE_PTR(value);
 
         return 0;
 }