]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
xattr-util: Add xsetxattr()
authorDaan De Meyer <daan.j.demeyer@gmail.com>
Mon, 13 Feb 2023 20:49:38 +0000 (21:49 +0100)
committerDaan De Meyer <daan.j.demeyer@gmail.com>
Tue, 14 Feb 2023 11:51:38 +0000 (12:51 +0100)
Like getxattr_malloc() but for setxattr() and friends.

src/basic/xattr-util.c
src/basic/xattr-util.h

index 5b6131b56a36b466404d97ff6be176fd6b2ffe0a..746e9f369e00764b82d1adbe672e7e8ec64c1b6e 100644 (file)
@@ -293,3 +293,77 @@ int listxattr_at_malloc(
                 l = (size_t) n;
         }
 }
+
+int xsetxattr(int fd,
+              const char *path,
+              const char *name,
+              const char *value,
+              size_t size,
+              int flags) {
+
+        _cleanup_close_ int opened_fd = -EBADF;
+        bool by_procfs = false;
+        int r;
+
+        assert(fd >= 0 || fd == AT_FDCWD);
+        assert(name);
+        assert(value);
+        assert((flags & ~(AT_SYMLINK_FOLLOW|AT_EMPTY_PATH)) == 0);
+
+        /* So, this is a single function that does what setxattr()/lsetxattr()/fsetxattr() do, but in one go,
+         * and with additional bells and whistles. Specifically:
+         *
+         * 1. This works on O_PATH fds (which fsetxattr() does not)
+         * 2. Provides full openat()-style semantics, i.e. by-fd, by-path and combination thereof
+         * 3. As extension to openat()-style semantics implies AT_EMPTY_PATH if path is NULL.
+         */
+
+        if (!path) /* If path is NULL, imply AT_EMPTY_PATH. – But if it's "", don't — for safety reasons. */
+                flags |= AT_EMPTY_PATH;
+
+        if (size == SIZE_MAX)
+                size = strlen(value);
+
+        if (isempty(path)) {
+                if (!FLAGS_SET(flags, AT_EMPTY_PATH))
+                        return -EINVAL;
+
+                if (fd == AT_FDCWD) /* Both unspecified? Then operate on current working directory */
+                        path = ".";
+                else
+                        path = NULL;
+
+        } else if (fd != AT_FDCWD) {
+
+                /* If both have been specified, then we go via O_PATH */
+                opened_fd = openat(fd, path, O_PATH|O_CLOEXEC|(FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? 0 : O_NOFOLLOW));
+                if (opened_fd < 0)
+                        return -errno;
+
+                fd = opened_fd;
+                path = NULL;
+                by_procfs = true; /* fsetxattr() is not going to work, go via /proc/ link right-away */
+        }
+
+        for (;;) {
+                if (path)
+                        r = FLAGS_SET(flags, AT_SYMLINK_FOLLOW) ? setxattr(path, name, value, size, 0)
+                                                                : lsetxattr(path, name, value, size, 0);
+                else
+                        r = by_procfs ? setxattr(FORMAT_PROC_FD_PATH(fd), name, value, size, 0)
+                                      : fsetxattr(fd, name, value, size, 0);
+                if (r < 0) {
+                        if (errno == EBADF) {
+                                if (by_procfs || path)
+                                        return -EBADF;
+
+                                by_procfs = true; /* Might be an O_PATH fd, try again via /proc/ link */
+                                continue;
+                        }
+
+                        return -errno;
+                }
+
+                return 0;
+        }
+}
index 0eb745a7a3bd84bb61081b912a94b88ade35ad21..649a842fe262d2582eb5e654ab17960c37e91014 100644 (file)
@@ -36,3 +36,5 @@ static inline int llistxattr_malloc(const char *path, char **ret) {
 static inline int flistxattr_malloc(int fd, char **ret) {
         return listxattr_at_malloc(fd, NULL, AT_EMPTY_PATH, ret);
 }
+
+int xsetxattr(int fd, const char *path, const char *name, const char *value, size_t size, int flags);