acl.c分析(二)

2021SC@SDUSC 

        上一篇分析了acl的头文件,这次来看acl.c中具体实现了那些操作。

        第一个函数是将磁盘上的ext2文件系统的acl结构转化成内存上通用的posix标准的acl结构,可以看到函数的返回值类型为posix_acl。开始几个if判断value是否为空、size是否小于ext2的acl头、acl的版本号是否正确,都没问题后value指向acl头后的数据部分,并根据size的大小计算acl数据项的数量count,检查count,无误则分配一定的posix标准的结构体内存,最后的循环则是为posix_acl的每一项赋值,先赋值e_tag和e_perm,再根据e_tag的值来选择value的指向。函数的末尾释放了acl占用的内存空间。观察这段代码,可以看到写的非常严谨,基本把所有能想到的问题都考虑到了,无论是参数的正确和分配空间后的回收。

static struct posix_acl *                                                                                        ext2_acl_from_disk(const void *value, size_t size)
{
    const char *end = (char *)value + size;
    int n, count;
    struct posix_acl *acl;

    if (!value)
        return NULL;
    if (size < sizeof(ext2_acl_header))
         return ERR_PTR(-EINVAL);
    if (((ext2_acl_header *)value)->a_version !=
        cpu_to_le32(EXT2_ACL_VERSION))
        return ERR_PTR(-EINVAL);
    value = (char *)value + sizeof(ext2_acl_header);
    count = ext2_acl_count(size);
    if (count < 0)
        return ERR_PTR(-EINVAL);
    if (count == 0)
        return NULL;
    acl = posix_acl_alloc(count, GFP_KERNEL);
    if (!acl)
        return ERR_PTR(-ENOMEM);
    for (n=0; n < count; n++) {
        ext2_acl_entry *entry =
            (ext2_acl_entry *)value;
        if ((char *)value + sizeof(ext2_acl_entry_short) > end)
            goto fail;
        acl->a_entries[n].e_tag  = le16_to_cpu(entry->e_tag);
        acl->a_entries[n].e_perm = le16_to_cpu(entry->e_perm);
        switch(acl->a_entries[n].e_tag) {
            case ACL_USER_OBJ:
            case ACL_GROUP_OBJ:
            case ACL_MASK:
            case ACL_OTHER:
                value = (char *)value +
                    sizeof(ext2_acl_entry_short);
                break;

            case ACL_USER:
                value = (char *)value + sizeof(ext2_acl_entry);
                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(ext2_acl_entry);
                if ((char *)value > end)
                    goto fail;
                acl->a_entries[n].e_gid =
                    make_kgid(&init_user_ns,
                          le32_to_cpu(entry->e_id));
                break;

            default:
                goto fail;
        }
    }
    if (value != end)
        goto fail;
    return acl;

fail:
    posix_acl_release(acl);
    return ERR_PTR(-EINVAL);
}

        这个函数看名称就知道是和上一个的逆过程,它将posix标准的acl结构体转化成ext2文件系统的acl结构体。理解起来不难,唯一需要注意的是在分配内存的时候,并未区分ext2_acl_entry和ext2_acl_entry_short,而是直接按照ext2_acl_entry的大小来分配的。

static void *
ext2_acl_to_disk(const struct posix_acl *acl, size_t *size)
{
    ext2_acl_header *ext_acl;
    char *e;
    size_t n;

    *size = ext2_acl_size(acl->a_count);
    ext_acl = kmalloc(sizeof(ext2_acl_header) + acl->a_count *
            sizeof(ext2_acl_entry), GFP_KERNEL);
    if (!ext_acl)
        return ERR_PTR(-ENOMEM);
    ext_acl->a_version = cpu_to_le32(EXT2_ACL_VERSION);
    e = (char *)ext_acl + sizeof(ext2_acl_header);
    for (n=0; n < acl->a_count; n++) {
        const struct posix_acl_entry *acl_e = &acl->a_entries[n];
        ext2_acl_entry *entry = (ext2_acl_entry *)e;
        entry->e_tag  = cpu_to_le16(acl_e->e_tag);
        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(ext2_acl_entry);
                break;
            case ACL_GROUP:
                entry->e_id = cpu_to_le32(
                    from_kgid(&init_user_ns, acl_e->e_gid));
                e += sizeof(ext2_acl_entry);
                break;

            case ACL_USER_OBJ:
            case ACL_GROUP_OBJ:
            case ACL_MASK:
            case ACL_OTHER:
                e += sizeof(ext2_acl_entry_short);
                break;

            default:
                goto fail;
        }
    }
    return (char *)ext_acl;

fail:
    kfree(ext_acl);
    return ERR_PTR(-EINVAL);
}

        这个函数的作用是从inode获得posix标准的acl结构体,先根据type的值选择name_index,然后看ext2_xattr_get这个函数,当第四个参数为NULL时为获取ext2文件属性需要的内存大小,不为NULL则将该文件中的acl结构体复制给第四个参数,最后检查retval的值,ext2_acl_from_disk上面说过了,大于0就把ext2格式的acl转化成posix标准。最后要释放内存。

struct posix_acl *
ext2_get_acl(struct inode *inode, int type)
{
    int name_index;
    char *value = NULL;
    struct posix_acl *acl;
    int retval;

    switch (type) {
    case ACL_TYPE_ACCESS:
        name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
        break;
    case ACL_TYPE_DEFAULT:
        name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
        break;
    default:
        BUG();
    }
    retval = ext2_xattr_get(inode, name_index, "", NULL, 0);
    if (retval > 0) {
        value = kmalloc(retval, GFP_KERNEL);
        if (!value)
            return ERR_PTR(-ENOMEM);
        retval = ext2_xattr_get(inode, name_index, "", value, retval);
    }
    if (retval > 0)
        acl = ext2_acl_from_disk(value, retval);
    else if (retval == -ENODATA || retval == -ENOSYS)
        acl = NULL;
    else
        acl = ERR_PTR(retval);
    kfree(value);

    return acl;
}

        显然这个函数是上一个的逆过程,设置了inode的acl结构体,type参数依旧是表示设置哪一个。使用ext2_acl_to_disk函数转化成ext2格式的acl结构体,然后ext2_xattr_set将新的acl写入文件属性。释放value,判断error是否存在,不存在就调用set_cached_acl处理。

static int
__ext2_set_acl(struct inode *inode, struct posix_acl *acl, int type)
{
    int name_index;
    void *value = NULL;
    size_t size = 0;
    int error;

    switch(type) {
        case ACL_TYPE_ACCESS:
            name_index = EXT2_XATTR_INDEX_POSIX_ACL_ACCESS;
            break;

        case ACL_TYPE_DEFAULT:
            name_index = EXT2_XATTR_INDEX_POSIX_ACL_DEFAULT;
            if (!S_ISDIR(inode->i_mode))
                return acl ? -EACCES : 0;
            break;

        default:
            return -EINVAL;
    }
     if (acl) {
        value = ext2_acl_to_disk(acl, &size);
        if (IS_ERR(value))
            return (int)PTR_ERR(value);
    }

    error = ext2_xattr_set(inode, name_index, "", value, size, 0);

    kfree(value);
    if (!error)
        set_cached_acl(inode, type, acl);
    return error;
}

        这个函数也为inode设置了acl结构体,但多了struct user_namespace *mnt_userns这个参数,函数的关键部分依然是__ext2_set_acl。posix_acl_update_mode看名称就能知道是更新了inode的mode。
int
ext2_set_acl(struct user_namespace *mnt_userns, struct inode *inode,
         struct posix_acl *acl, int type)
{
    int error;
    int update_mode = 0;
    umode_t mode = inode->i_mode;

    if (type == ACL_TYPE_ACCESS && acl) {
        error = posix_acl_update_mode(&init_user_ns, inode, &mode,
                          &acl);
        if (error)
            return error;
        update_mode = 1;
    }
    error = __ext2_set_acl(inode, acl, type);
    if (!error && update_mode) {
        inode->i_mode = mode;
        inode->i_ctime = current_time(inode);
        mark_inode_dirty(inode);
    }
    return error;
}

        这个函数为一个新的inode初始化acl结构体, 调用posix_acl_create创建posix标准的acl结构体并赋给default_acl和acl,然后检查是否成功赋予,再调用__ext2_set_acl将其转换成ext2格式的acl结构体,最后释放default_acl和acl并返回error。
int
ext2_init_acl(struct inode *inode, struct inode *dir)
{
    struct posix_acl *default_acl, *acl;
    int error;

    error = posix_acl_create(dir, &inode->i_mode, &default_acl, &acl);
    if (error)
        return error;

    if (default_acl) {
        error = __ext2_set_acl(inode, default_acl, ACL_TYPE_DEFAULT);
        posix_acl_release(default_acl);
    } else {
        inode->i_default_acl = NULL;
    }
    if (acl) {
        if (!error)
            error = __ext2_set_acl(inode, acl, ACL_TYPE_ACCESS);
        posix_acl_release(acl);
    } else {
        inode->i_acl = NULL;
    }
    return error;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值