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的定义。

除了基础知识中提到的,这里还要补充几点:

  1. client模块中,对 a_entires中的 acl_ea_entry 的顺序也做了要求,(不晓得是posix 规定的还是 client模块中规定的~)。具体实现在 posix_acl.c@posix_acl_check中。

  2. 当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_OBJACL_GROUP_OBJACL_OTHER必须存在,其余可选。

    ACL_USERACL_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_USERACL_GROUPACL_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到文件属性。

其余的函数后续看了再补充。

posted @ 2022-09-27 16:02  liutimo  阅读(160)  评论(0编辑  收藏  举报