[RFC PATCH v2 34/41] ext4: store ACLs with UUID and GUID entries on uid_r filesystems
Jan Koester
jan.koester at tuxist.de
Wed Oct 21 03:15:54 UTC 2026
ACLs whose ACL_USER or ACL_GROUP entries are UUIDs or GUIDs are written
in a new on-disk format, EXT4_ACL_VERSION_R: the short entries stay as
they are, the user and group entries carry an EXT4_ID_R_* kind and 16
bytes of id. Only such ACLs use it, and only uid_r filesystems can have
them (set_posix_acl() refuses them elsewhere); all other ACLs keep
EXT4_ACL_VERSION.
Assisted-by: Claude:claude-opus-5-5
Signed-off-by: Jan Koester <jan.koester at tuxist.de>
---
fs/ext4/acl.c | 109 +++++++++++++++++++++++++++++++++++++++-----------
fs/ext4/acl.h | 34 +++++++++++++---
2 files changed, 115 insertions(+), 28 deletions(-)
diff --git a/fs/ext4/acl.c b/fs/ext4/acl.c
index 3bffe862f9..27f8a1b503 100644
--- a/fs/ext4/acl.c
+++ b/fs/ext4/acl.c
@@ -6,6 +6,7 @@
*/
#include <linux/quotaops.h>
+#include <linux/unaligned.h>
#include "ext4_jbd2.h"
#include "ext4.h"
#include "xattr.h"
@@ -14,22 +15,41 @@
/*
* Convert from filesystem to in-memory representation.
*/
+/* The id of an ACL_USER or ACL_GROUP entry of EXT4_ACL_VERSION_R */
+static int ext4_acl_id_from_disk(const ext4_acl_entry_r *entry, u32 *type,
+ u32 *legacy, uuid_t *id)
+{
+ *type = le32_to_cpu(entry->e_type);
+ if (*type > EXT4_ID_R_GUID)
+ return -EINVAL;
+ *legacy = get_unaligned_le32(entry->e_id);
+ memcpy(id->b, entry->e_id, sizeof(id->b));
+ return 0;
+}
+
static struct posix_acl *
ext4_acl_from_disk(const void *value, size_t size)
{
const char *end = (char *)value + size;
int n, count;
struct posix_acl *acl;
+ size_t entry_size = sizeof(ext4_acl_entry);
+ bool r = false;
if (!value)
return NULL;
if (size < sizeof(ext4_acl_header))
return ERR_PTR(-EINVAL);
- if (((ext4_acl_header *)value)->a_version !=
- cpu_to_le32(EXT4_ACL_VERSION))
+ if (((ext4_acl_header *)value)->a_version ==
+ cpu_to_le32(EXT4_ACL_VERSION_R)) {
+ r = true;
+ entry_size = sizeof(ext4_acl_entry_r);
+ } else if (((ext4_acl_header *)value)->a_version !=
+ cpu_to_le32(EXT4_ACL_VERSION)) {
return ERR_PTR(-EINVAL);
+ }
value = (char *)value + sizeof(ext4_acl_header);
- count = ext4_acl_count(size);
+ count = ext4_acl_count_e(size, entry_size);
if (count < 0)
return ERR_PTR(-EINVAL);
if (count == 0)
@@ -55,21 +75,33 @@ ext4_acl_from_disk(const void *value, size_t size)
break;
case ACL_USER:
- value = (char *)value + sizeof(ext4_acl_entry);
+ case ACL_GROUP: {
+ u32 type = EXT4_ID_R_LEGACY, legacy;
+ uuid_t id;
+
+ value = (char *)value + entry_size;
if ((char *)value > end)
goto fail;
- acl->a_entries[n].e_uid =
- make_kuid(&init_user_ns,
- le32_to_cpu(entry->e_id));
- break;
- case ACL_GROUP:
- value = (char *)value + sizeof(ext4_acl_entry);
- if ((char *)value > end)
+ legacy = le32_to_cpu(entry->e_id);
+ if (r && ext4_acl_id_from_disk((void *)entry, &type,
+ &legacy, &id))
goto fail;
- acl->a_entries[n].e_gid =
- make_kgid(&init_user_ns,
- le32_to_cpu(entry->e_id));
+ if (acl->a_entries[n].e_tag == ACL_USER)
+ acl->a_entries[n].e_uid =
+ type == EXT4_ID_R_LEGACY ?
+ make_kuid(&init_user_ns, legacy) :
+ type == EXT4_ID_R_UUID ?
+ KUIDT_INIT_UUID(id) :
+ KUIDT_INIT_GUID(*(guid_t *)&id);
+ else
+ acl->a_entries[n].e_gid =
+ type == EXT4_ID_R_LEGACY ?
+ make_kgid(&init_user_ns, legacy) :
+ type == EXT4_ID_R_UUID ?
+ KGIDT_INIT_UUID(id) :
+ KGIDT_INIT_GUID(*(guid_t *)&id);
break;
+ }
default:
goto fail;
@@ -87,19 +119,38 @@ ext4_acl_from_disk(const void *value, size_t size)
/*
* Convert from in-memory to filesystem representation.
*/
+/* An ACL_USER or ACL_GROUP id into an entry of EXT4_ACL_VERSION_R */
+static void ext4_acl_id_to_disk(ext4_acl_entry_r *entry, u32 type, u32 legacy,
+ const uuid_t *id)
+{
+ entry->e_type = cpu_to_le32(type);
+ memset(entry->e_id, 0, sizeof(entry->e_id));
+ if (type == EXT4_ID_R_LEGACY)
+ put_unaligned_le32(legacy, entry->e_id);
+ else
+ memcpy(entry->e_id, id->b, sizeof(entry->e_id));
+}
+
+/*
+ * ACLs with UUID or GUID entries (only on uid_r filesystems, see
+ * set_posix_acl()) are written as EXT4_ACL_VERSION_R, others as before.
+ */
static void *
ext4_acl_to_disk(const struct posix_acl *acl, size_t *size)
{
ext4_acl_header *ext_acl;
char *e;
size_t n;
+ bool r = posix_acl_has_id_r(acl);
+ size_t entry_size = r ? sizeof(ext4_acl_entry_r) : sizeof(ext4_acl_entry);
- *size = ext4_acl_size(acl->a_count);
+ *size = ext4_acl_size_e(acl->a_count, entry_size);
ext_acl = kmalloc(sizeof(ext4_acl_header) + acl->a_count *
- sizeof(ext4_acl_entry), GFP_NOFS);
+ entry_size, GFP_NOFS);
if (!ext_acl)
return ERR_PTR(-ENOMEM);
- ext_acl->a_version = cpu_to_le32(EXT4_ACL_VERSION);
+ ext_acl->a_version = cpu_to_le32(r ? EXT4_ACL_VERSION_R :
+ EXT4_ACL_VERSION);
e = (char *)ext_acl + sizeof(ext4_acl_header);
for (n = 0; n < acl->a_count; n++) {
const struct posix_acl_entry *acl_e = &acl->a_entries[n];
@@ -108,14 +159,26 @@ ext4_acl_to_disk(const struct posix_acl *acl, size_t *size)
entry->e_perm = cpu_to_le16(acl_e->e_perm);
switch (acl_e->e_tag) {
case ACL_USER:
- entry->e_id = cpu_to_le32(
- from_kuid(&init_user_ns, acl_e->e_uid));
- e += sizeof(ext4_acl_entry);
+ if (r)
+ ext4_acl_id_to_disk((void *)entry,
+ acl_e->e_uid.type,
+ from_kuid(&init_user_ns, acl_e->e_uid),
+ &acl_e->e_uid.uuid);
+ else
+ entry->e_id = cpu_to_le32(
+ from_kuid(&init_user_ns, acl_e->e_uid));
+ e += entry_size;
break;
case ACL_GROUP:
- entry->e_id = cpu_to_le32(
- from_kgid(&init_user_ns, acl_e->e_gid));
- e += sizeof(ext4_acl_entry);
+ if (r)
+ ext4_acl_id_to_disk((void *)entry,
+ acl_e->e_gid.type,
+ from_kgid(&init_user_ns, acl_e->e_gid),
+ &acl_e->e_gid.uuid);
+ else
+ entry->e_id = cpu_to_le32(
+ from_kgid(&init_user_ns, acl_e->e_gid));
+ e += entry_size;
break;
case ACL_USER_OBJ:
diff --git a/fs/ext4/acl.h b/fs/ext4/acl.h
index 0c5a79c3b5..48bc352ec7 100644
--- a/fs/ext4/acl.h
+++ b/fs/ext4/acl.h
@@ -8,6 +8,8 @@
#include <linux/posix_acl_xattr.h>
#define EXT4_ACL_VERSION 0x0001
+/* uid_r: ACL_USER and ACL_GROUP entries are ext4_acl_entry_r */
+#define EXT4_ACL_VERSION_R 0x0002
typedef struct {
__le16 e_tag;
@@ -20,11 +22,23 @@ typedef struct {
__le16 e_perm;
} ext4_acl_entry_short;
+/*
+ * EXT4_ACL_VERSION_R: e_type an EXT4_ID_R_* kind, a classic id in the
+ * first four bytes of e_id (little endian), a UUID or GUID all of it
+ */
+typedef struct {
+ __le16 e_tag;
+ __le16 e_perm;
+ __le32 e_type;
+ __u8 e_id[16];
+} ext4_acl_entry_r;
+
typedef struct {
__le32 a_version;
} ext4_acl_header;
-static inline size_t ext4_acl_size(int count)
+/* @entry: sizeof(ext4_acl_entry) or, for EXT4_ACL_VERSION_R, of _r */
+static inline size_t ext4_acl_size_e(int count, size_t entry)
{
if (count <= 4) {
return sizeof(ext4_acl_header) +
@@ -32,11 +46,11 @@ static inline size_t ext4_acl_size(int count)
} else {
return sizeof(ext4_acl_header) +
4 * sizeof(ext4_acl_entry_short) +
- (count - 4) * sizeof(ext4_acl_entry);
+ (count - 4) * entry;
}
}
-static inline int ext4_acl_count(size_t size)
+static inline int ext4_acl_count_e(size_t size, size_t entry)
{
ssize_t s;
size -= sizeof(ext4_acl_header);
@@ -46,12 +60,22 @@ static inline int ext4_acl_count(size_t size)
return -1;
return size / sizeof(ext4_acl_entry_short);
} else {
- if (s % sizeof(ext4_acl_entry))
+ if (s % entry)
return -1;
- return s / sizeof(ext4_acl_entry) + 4;
+ return s / entry + 4;
}
}
+static inline size_t ext4_acl_size(int count)
+{
+ return ext4_acl_size_e(count, sizeof(ext4_acl_entry));
+}
+
+static inline int ext4_acl_count(size_t size)
+{
+ return ext4_acl_count_e(size, sizeof(ext4_acl_entry));
+}
+
#ifdef CONFIG_EXT4_FS_POSIX_ACL
/* acl.c */
--
2.56.0
More information about the Linux-security-module-archive
mailing list