在linux文件系统的各种操作里,很重要的一个东西就是权限控制,我们来一起追溯内核源代码,弄清楚内核的权限之谜。
在VFS(linux虚拟文件系统)里,权限控制都是通过vfs_permission来实现的,我们从vfs_permission来看,vfs_permission函数定义在fs/namei.c,定义如下
int vfs_permission(struct nameidata *nd, int mask)
{
return permission(nd->dentry->d_inode, mask, nd);
}
vfs_permission函数直接调用了permission函数,我们进入permission函数看看。permission函数定义在fs/namei.c,定义如下
int permission(struct inode *inode, int mask, struct nameidata *nd)
{
/*得到文件的权限*/
umode_t mode = inode->i_mode;
int retval, submask;
if (mask & MAY_WRITE) {
/*只读文件系统不可以写入*/
if (IS_RDONLY(inode) &&
(S_ISREG(mode) || S_ISDIR(mode) || S_ISLNK(mode)))
return -EROFS;
/*不可修改文件不能写*/
if (IS_IMMUTABLE(inode))
return -EACCES;
}
/*对于普通文件的MAY_EXEC需要可执行权限,如果文件系统不可以执行,就返回错误*/
if ((mask & MAY_EXEC) && S_ISREG(mode) && (!(mode & S_IXUGO) ||
(nd && nd->mnt && (nd->mnt->mnt_flags & MNT_NOEXEC))))
return -EACCES;
/* 一般的权限程序不理解MAY_APPEND*/
submask = mask & ~MAY_APPEND;
/*如果inode的函数操作结构体有permission函数,就调用这个函数*/
if (inode->i_op && inode->i_op->permission)
retval = inode->i_op->permission(inode, submask, nd);
else
/*否则就调用通用的权限检查函数*/
retval = generic_permission(inode, submask, NULL);
if (retval)
return retval;
/*安全操作,最后决定是否有权限,调用内核自己注册的security_ops结构体的函数,新的内核特性*/
return security_inode_permission(inode, mask, nd);
}
接下来进入generic_permission函数看一看通用的权限检查函数的实现。generic_permission函数定义在fs/namei.c,定义如下
/**
* generic_permission - 在POSIX系列操作系统检察权限
* @inode: 检察权限需要的inode
* @mask: 权限检查(%MAY_READ, %MAY_WRITE, %MAY_EXEC)
* @check_acl: 回调函数,我们传入的是NULL,不必考虑
*/
int generic_permission(struct inode *inode, int mask,
int (*check_acl)(struct inode *inode, int mask))
{
umode_t mode = inode->i_mode;
/*所有者的权限*/
if (current->fsuid == inode->i_uid)
mode >>= 6;
else {
if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) {
int error = check_acl(inode, mask);
if (error == -EACCES)
goto check_capabilities;
else if (error != -EAGAIN)
return error;
}
/*如果对于组ID检查成功,就右移三位*/
if (in_group_p(inode->i_gid))
mode >>= 3;
}
/*自主控制位检查*/
if (((mode & mask & (MAY_READ|MAY_WRITE|MAY_EXEC)) == mask))
return 0;
check_capabilities:
/*DAC权限检查*/
if (!(mask & MAY_EXEC) ||
(inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode))
if (capable(CAP_DAC_OVERRIDE))
return 0;
/*
* Searching includes executable on directories, else just read.
*/
if (mask == MAY_READ || (S_ISDIR(inode->i_mode) && !(mask & MAY_WRITE)))
if (capable(CAP_DAC_READ_SEARCH))
return 0;
return -EACCES;
}