
Linux generic_permission是inode权限检查的核心枢纽它位于fs/namei.c是所有基于inode的访问控制决策的最终仲裁者。函数接收inode、请求的权限掩码MAY_READ/MAY_WRITE/MAY_EXEC和一个可选的cred结构体经过UNIX mode位、POSIX ACL和扩展权限检查三层验证后返回0或-EACCES。// fs/namei.cint generic_permission(struct user_namespace *mnt_userns,struct inode *inode, int mask){int ret;ret acl_permission_check(mnt_userns, inode, mask);if (ret ! -EACCES)return ret;if (S_ISDIR(inode-i_mode)) {if (!(inode-i_opflags IOP_EXEC_SEARCH))return -EACCES;}ret security_inode_permission(inode, mask);if (ret)return ret;return -EACCES;}generic_permission首先调用acl_permission_check做基层的mode和ACL检查。如果返回-EACCES表示权限不足函数不会立即返回而是继续进行LSMLinux Security Module钩子的检查。如果LSM允许访问则纠正返回0如果LSM也拒绝最终返回-EACCES。这种设计让LSM层面可以覆盖ACL的拒绝决定。// fs/namei.cstatic int acl_permission_check(struct user_namespace *mnt_userns,struct inode *inode, int mask){unsigned int mode inode-i_mode;kuid_t i_uid;if (likely(uid_eq(current_fsuid(), inode-i_uid)))mode 6;else {if (IS_POSIXACL(inode) (mode S_IRWXG)) {int error check_acl(mnt_userns, inode, mask);if (error ! -EAGAIN)return error;}if (in_group_p(inode-i_gid))mode 3;elsemode ~S_IRWXG;}if ((mask ~mode) 0)return 0;return -EACCES;}acl_permission_check的精髓在于高效的短路设计。首先检查进程的文件系统UID是否与inode的owner匹配如果匹配则直接取owner权限位mode右移6位。如果不匹配检查inode是否设置了IS_POSIXACL标志且group权限位不为空此时调用check_acl遍历文件系统提供的ACL条目。如果check_acl返回-EAGAIN表示没有ACL或ACL不支持当前操作或者ACL检查通过后继续fallthrough则进入group组检查。// fs/namei.cstatic int check_acl(struct user_namespace *mnt_userns,struct inode *inode, int mask){const struct inode_operations *i_op inode-i_op;if (i_op-get_inode_acl) {struct posix_acl *acl;acl i_op-get_inode_acl(inode, ACL_TYPE_ACCESS);if (IS_ERR(acl))return PTR_ERR(acl);if (acl) {int error posix_acl_permission(mnt_userns, inode, acl, mask);posix_acl_release(acl);return error;}}return -EAGAIN;}check_acl通过inode_operations中的get_inode_acl函数指针获取inode关联的ACL对象。POSIX ACL分为ACCESS类型控制访问权限和DEFAULT类型仅目录控制继承。get_inode_acl为ACCESS类型。不同文件系统实现方式不同ext4调用ext4_get_acl从磁盘扩展属性读取并解析XFS调用xfs_get_acl从xattr中加载。// fs/posix_acl.cint posix_acl_permission(struct user_namespace *mnt_userns,struct inode *inode, const struct posix_acl *acl,int want){const struct posix_acl_entry *pa, *pe, *mask_obj;int found 0;FOREACH_ACL_ENTRY(pa, pe, acl) {if (pa-e_tag ACL_USER_OBJ) {if (uid_eq(current_fsuid(), inode-i_uid))goto check_perm;} else if (pa-e_tag ACL_USER) {if (uid_eq(pa-e_uid, current_fsuid()))goto mask;} else if (pa-e_tag ACL_GROUP_OBJ) {if (in_group_p(inode-i_gid)) {found 1;if (mask_obj NULL)mask_obj pa;}} else if (pa-e_tag ACL_GROUP) {if (in_group_p(pa-e_gid)) {found 1;if (mask_obj NULL)mask_obj pa;}} else if (pa-e_tag ACL_MASK)mask_obj pa;else if (pa-e_tag ACL_OTHER)if (!(want ~pa-e_perms))return 0;}if (!found)return -EACCES;// apply ACL_MASK if presentif (mask_obj) {// ...}return -EACCES;mask:if (mask_obj) {if ((pa-e_perms mask_obj-e_perms want) want)return 0;} else if ((pa-e_perms want) want)return 0;return -EACCES;check_perm:if ((pa-e_perms want) want)return 0;return -EACCES;}ACL的处理优先级是ACL_USER_OBJ文件owner ACL_USER具名用户 ACL_GROUP_OBJ/ACL_GROUP组 ACL_OTHER其他人。ACL_MASK条目用于屏蔽ACL_USER、ACL_GROUP_OBJ和ACL_GROUP条目的权限位这是POSIX ACL语义的核心——mask的引入使得chmod修改group位时可以同时调整ACL mask。如果检查通过generic_permission还需要调用security_inode_permission这是LSM框架的钩子例如SELinux、AppArmor、Smack等安全模块在此处施加额外的MAC强制访问控制策略。// security/security.cint security_inode_permission(struct inode *inode, int mask){if (unlikely(IS_PRIVATE(inode)))return 0;return call_int_hook(inode_permission, 0, inode, mask);}LSM钩子的返回值可以覆盖ACL的检查结果。如果一个进程拥有DAC自主访问控制权限但不符合MAC策略security_inode_permission返回-EACCESgeneric_permission最终也返回-EACCES。反之如果ACL拒绝了但LSM允许generic_permission返回0——这在某些特殊安全策略例如允许备份程序读取所有文件中有实际用途。最后inode-i_opflags中的IOP_EXEC_SEARCH标志与目录执行权限相关。当inode是目录且acl_permission_check返回-EACCES后generic_permission会检查这个标志位来决定是否立即拒绝。这是为了优化目录搜索权限检查的性能如果文件系统在lookup阶段已经确认了执行权限不需要重新走完整的ACL检查。