/* No ACLs? Then just update the regular mode_t */
return fd_acl_make_read_only_fallback(fd);
}
+
+int fd_acl_make_writable(int fd) {
+ _cleanup_(acl_freep) acl_t acl = NULL;
+ acl_entry_t i;
+ int r;
+
+ /* Safely adds the writable bit to the owner's ACL entry of this inode. (And only the owner's! – This
+ * not the obvious inverse of fd_acl_make_read_only() hence!) */
+
+ r = dlopen_libacl();
+ if (r < 0)
+ return r;
+
+ acl = sym_acl_get_fd(fd);
+ if (!acl) {
+ if (!ERRNO_IS_NOT_SUPPORTED(errno))
+ return -errno;
+
+ /* No ACLs? Then just update the regular mode_t */
+ return fd_acl_make_writable_fallback(fd);
+ }
+
+ for (r = sym_acl_get_entry(acl, ACL_FIRST_ENTRY, &i);
+ r > 0;
+ r = sym_acl_get_entry(acl, ACL_NEXT_ENTRY, &i)) {
+ acl_permset_t permset;
+ acl_tag_t tag;
+ int b;
+
+ if (sym_acl_get_tag_type(i, &tag) < 0)
+ return -errno;
+
+ if (tag != ACL_USER_OBJ)
+ continue;
+
+ if (sym_acl_get_permset(i, &permset) < 0)
+ return -errno;
+
+ b = sym_acl_get_perm(permset, ACL_WRITE);
+ if (b < 0)
+ return -errno;
+
+ if (b)
+ return 0; /* Already set? Then there's nothing to do. */
+
+ if (sym_acl_add_perm(permset, ACL_WRITE) < 0)
+ return -errno;
+
+ break;
+ }
+ if (r < 0)
+ return -errno;
+
+ if (sym_acl_set_fd(fd, acl) < 0) {
+ if (!ERRNO_IS_NOT_SUPPORTED(errno))
+ return -errno;
+
+ return fd_acl_make_writable_fallback(fd);
+ }
+
+ return 1;
+}
#endif
int fd_acl_make_read_only_fallback(int fd) {
return 1;
}
+int fd_acl_make_writable_fallback(int fd) {
+ struct stat st;
+
+ assert(fd >= 0);
+
+ if (fstat(fd, &st) < 0)
+ return -errno;
+
+ if ((st.st_mode & 0200) != 0) /* already set */
+ return 0;
+
+ if (fchmod(fd, (st.st_mode & 07777) | 0200) < 0)
+ return -errno;
+
+ return 1;
+}
+
int inode_type_can_acl(mode_t mode) {
return IN_SET(mode & S_IFMT, S_IFSOCK, S_IFREG, S_IFBLK, S_IFCHR, S_IFDIR, S_IFIFO);
}
#include "shared-forward.h"
int fd_acl_make_read_only_fallback(int fd);
+int fd_acl_make_writable_fallback(int fd);
#if HAVE_ACL
#include <acl/libacl.h> /* IWYU pragma: export */
int fd_add_uid_acl_permission(int fd, uid_t uid, unsigned mask);
int fd_acl_make_read_only(int fd);
+int fd_acl_make_writable(int fd);
/* acl_free() takes multiple argument types. Multiple cleanup functions are necessary. */
DEFINE_TRIVIAL_CLEANUP_FUNC_FULL_RENAME(acl_t, sym_acl_free, acl_freep, NULL);
static inline int fd_acl_make_read_only(int fd) {
return fd_acl_make_read_only_fallback(fd);
}
+
+static inline int fd_acl_make_writable(int fd) {
+ return fd_acl_make_writable_fallback(fd);
+}
#endif
int inode_type_can_acl(mode_t mode);
cmd = strjoina("stat ", fn);
ASSERT_OK_ZERO_ERRNO(system(cmd));
+ log_info("writable");
+ ASSERT_OK_POSITIVE(fd_acl_make_writable(fd));
+
+ ASSERT_OK_ERRNO(fstat(fd, &st));
+ ASSERT_EQ(st.st_mode & 0222, 0200u);
+
+ cmd = strjoina("getfacl -p ", fn);
+ ASSERT_OK_ZERO_ERRNO(system(cmd));
+
+ cmd = strjoina("stat ", fn);
+ ASSERT_OK_ZERO_ERRNO(system(cmd));
+
+ log_info("read-only");
+ ASSERT_OK_POSITIVE(fd_acl_make_read_only(fd));
+
+ ASSERT_OK_ERRNO(fstat(fd, &st));
+ ASSERT_EQ(st.st_mode & 0222, 0000u);
+
+ cmd = strjoina("getfacl -p ", fn);
+ ASSERT_OK_ZERO_ERRNO(system(cmd));
+
+ cmd = strjoina("stat ", fn);
+ ASSERT_OK_ZERO_ERRNO(system(cmd));
+
return 0;
}