Client模块Poisx ACL代码分析
总结
在client模块中,使用 acl_ea_header
来描述一组posix ACL规则,给文件或文件夹设置posix acl的本质就是给其设置名为 system.posix_acl_access 的 扩展属性,对应的value就是 acl_ea_header
。
我们先来看看client模块是如何使用 acl_ea_header
来描述ACL规则的:
typedef struct { ceph_le16 e_tag; ceph_le16 e_perm; ceph_le32 e_id; } acl_ea_entry; typedef struct { ceph_le32 a_version; acl_ea_entry a_entries[0]; } acl_ea_header;
acl_ea_entry
表示一条acl规则,而acl_ea_header
通过变长数组 a_entries
来记录一组acl规则。
acl_ea_entry
各个字段的含义可以自行去阅读posix acl的定义。
除了基础知识中提到的,这里还要补充几点:
-
client模块中,对
a_entires
中的acl_ea_entry
的顺序也做了要求,(不晓得是posix 规定的还是 client模块中规定的~)。具体实现在posix_acl.c@posix_acl_check
中。 -
当acl 规则等价于unix mode(eg:-rwxrwxrwx)时,我们通过
ceph_getattr
获取system.posix_acl_access 扩展属性时,返回 ENODATA,并且此时 ll file不会显示+号。eg: $ touch 1.txt $ getfacl 1.txt # file: 1.txt # owner: 10331600@zte.intra # group: domain\040users@zte.intra user::rw- group::r-- other::r-- $ setfacl -m u::rwx 1.txt $ getfacl 1.txt # file: 1.txt # owner: 10331600@zte.intra # group: domain\040users@zte.intra user::rwx group::r-- other::r-- $ ll 1.txt -rwxr--r-- 1 10331600@zte.intra domain users@zte.intra 0 9月 26 16:17 1.txt
posix acl函数分析
posix_acl.h中,提供了如下函数用于完成client端的acl鉴权。
int posix_acl_check(const void *xattr, size_t size); int posix_acl_equiv_mode(const void *xattr, size_t size, mode_t *mode_p); int posix_acl_inherit_mode(bufferptr& acl, mode_t *mode_p); int posix_acl_access_chmod(bufferptr& acl, mode_t mode); int posix_acl_permits(const bufferptr& acl, uid_t i_uid, gid_t i_gid, const UserPerm& groups, unsigned want);
posix_acl_check
int posix_acl_check(const void *xattr, size_t size) { const acl_ea_header *header; if (size < sizeof(*header)) return -1; header = reinterpret_cast<const acl_ea_header*>(xattr); ceph_le32 expected_version; expected_version = ACL_EA_VERSION; if (header->a_version != expected_version) return -1; const acl_ea_entry *entry = header->a_entries; size -= sizeof(*header); if (size % sizeof(*entry)) return -1; int count = size / sizeof(*entry); if (count == 0) return 0; int state = ACL_USER_OBJ; int needs_mask = 0; for (int i = 0; i < count; ++i) { __u16 tag = entry->e_tag; switch(tag) { case ACL_USER_OBJ: if (state == ACL_USER_OBJ) { state = ACL_USER; break; } return -1; case ACL_USER: if (state != ACL_USER) return -1; needs_mask = 1; break; case ACL_GROUP_OBJ: if (state == ACL_USER) { state = ACL_GROUP; break; } return -1; case ACL_GROUP: if (state != ACL_GROUP) return -1; needs_mask = 1; break; case ACL_MASK: if (state != ACL_GROUP) return -1; state = ACL_OTHER; break; case ACL_OTHER: if (state == ACL_OTHER || (state == ACL_GROUP && !needs_mask)) { state = 0; break; } // fall-thru default: return -1; } ++entry; } return state == 0 ? count : -1; }
从这个代码片段中,我们可以看出:
-
如何解析acl数据
-
acl条目的组成规则
即a_entries中的ea_acl_entry 必须按照如下顺序组成:
<ACL_USER_OBJ> [ACL_USER] <ACL_GROUP> [ACL_GROUP_OBJ] [ACL_MASK] <ACL_OTHER>
ACL_USER_OBJ
、ACL_GROUP_OBJ
和ACL_OTHER
必须存在,其余可选。当
ACL_USER
、ACL_GROUP
任何一个存在时,都必须要有ACL_MASK
。
posix_acl_equiv_mode
int posix_acl_equiv_mode(const void *xattr, size_t size, mode_t *mode_p) { if (posix_acl_check(xattr, size) < 0) return -EINVAL; int not_equiv = 0; mode_t mode = 0; const acl_ea_header *header = reinterpret_cast<const acl_ea_header *>(xattr); const acl_ea_entry *entry = header->a_entries; int count = (size - sizeof(*header)) / sizeof(*entry); for (int i = 0; i < count; ++i) { __u16 tag = entry->e_tag; __u16 perm = entry->e_perm; switch (tag) { case ACL_USER_OBJ: mode |= (perm & S_IRWXO) << 6; break; case ACL_GROUP_OBJ: mode |= (perm & S_IRWXO) << 3; break; case ACL_OTHER: mode |= perm & S_IRWXO; break; case ACL_MASK: mode = (mode & ~S_IRWXG) | ((perm & S_IRWXO) << 3); /* fall through */ case ACL_USER: case ACL_GROUP: not_equiv = 1; break; default: return -EINVAL; } ++entry; } if (mode_p) *mode_p = (*mode_p & ~ACCESSPERMS) | mode; return not_equiv; }
先校验acl是否符合前面提到的规则,然后计算acl对应的 unix mode。
该函数除了计算 ACL规则对应的unix file/dir mode(保存在 mode_p
对应的内存中)外,还会判断 acl 规则 是否等价于 unix file/dir mode。这里就和我们前面补充的第二点对应上了。
当存在 ACL_USER
、ACL_GROUP
和ACL_MASK
时, posix_acl_equiv_mode
返回1(ACL不等价于 unix mode),否则返回0。我们再看一段用到了 posix_acl_equiv_mode
的代码:
int Client::_setxattr(Inode *in, const char *name, const void *value, size_t size, int flags, const UserPerm &perms) { ... if (posix_acl_xattr) { if (!strcmp(name, ACL_EA_ACCESS)) { mode_t new_mode = in->mode; if (value) { int ret = posix_acl_equiv_mode(value, size, &new_mode); if (ret < 0) return ret; if (ret == 0) { value = NULL; size = 0; } if (new_mode != in->mode) { struct ceph_statx stx; stx.stx_mode = new_mode; ret = _do_setattr(in, &stx, CEPH_SETATTR_MODE, perms, NULL); if (ret < 0) return ret; } } } ... } int ret = _do_setxattr(in, name, value, size, flags, 0, perms); if (ret >= 0 && check_realm) { // check if snaprealm was created for quota inode if (in->quota.is_any_enable() && !(in->snaprealm && in->snaprealm->ino == in->ino)) ret = -EOPNOTSUPP; } return ret; }
我们调用Client::_setxattr
设置acl时, 如果posix_acl_equiv_mode
返回0时, value
会被置为null
。最终,只会设置与acl对等的 mode, 而不会设置acl到文件属性。
其余的函数后续看了再补充。
本文作者:liutimo
本文链接:https://www.cnblogs.com/liutimo/p/16734899.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步