【摘要】
【正文一:文件管理】
【常见情况分析】
【正文二:目录管理】
【总结】
注意:请使用谷歌浏览器阅读(IE浏览器排版混乱)
【摘要】
本文以实例介绍linux系统对文件权限的管理.linux kernel对文件权限的检查、关键处理和难点,都发生在打开文件时.至于读写文件,检查方法非常简单,都是基于打开文件时设置的一些权限标志位,所以本文只针对打开文件做介绍.
【正文一:文件管理】
1 打开文件的接口:
glibc/uclibc中open接口为:
int open(const char *pathname,int flags)
说明:以flags方式打开文件,如果需要创建文件则文件属性为040,即文件所在用户组的用户有读权限.
int open(const char *pathname, int flags, mode_t mode)
说明:以flags方式打开文件。如果需要创建新文件,则指定文件属性为mode;如果不需要创建文件(即flags未设置O_CREATE标志)则mode字段不起作用.
系统中会修改mode&=022;所以即使此时mode权限为0777,也会被改为0755可,参看后文分析,即open创建文件时不允许非文件所属用户具有写权限.不过可以通过chmod重新修改为0777权限.umask()系统调用可以修改022;
内核态对应系统调用为:
sys_open(const char* __user *filename,int flags,umode_t mode);
2 文件打开方式与文件权限
2.1 文件常用打开方式(即open函数的入参flags):
#define O_RDONLY 00000000 表示以只读方式打开文件;
#define O_WRONLY 00000001 表示以只写方式打开文件;
#define O_RDWR 00000002 表示以读写方式打开文件;
#define O_CREATE 00000100 表示如果文件不存在,则创建文件;
2.2 文件访问权限(即open函数的入参mode):
说明:R表示文件有读权限;W表示文件有写权限;X表示文件有执行权限;
U表示文件所属用户;G表示文件所属用户所在组的用户;O表示其他用户。
1)文件所属用户对文件的访问权限:
#define S_IRWXU 00700 表示文件所属用户,对文件有读写和执行权限
#define S_IRUSR 00400 表示文件所属用户,对文件有读权限
#define S_IWUSR 00200 表示文件所属用户,对文件有写权限
#define S_IXUSR 00100 表示文件所属用户,对文件有执行权限
2)文件所属用户所在组用户对文件的访问权限:
#define S_IRWXG 00070 表示对文件有读写和执行权限
#define S_IRGRP 00040 表示对文件有读权限
#define S_IWGRP 00020 表示对文件有写权限
#define S_IXGRP 00010 表示对文件有执行权限
3)其他用户对文件的访问权限:
#define S_IRWXO 00007 表示对文件有读写和执行权限
#define S_IROTH 00004 表示对文件有读权限
#define S_IWOTH 00002 表示对文件有写权限
#define S_IXOTH 00001 表示对文件有执行权限
4)进程权限:
RUID(real user id)是启动该进程的用户的ID,它与父进程用户ID相同,除非被改变.
EUID是内核检查权限时使用的实际ID,因此它确定了进程的权限.
SSUID:设置用户ID,表示执行时设置用户ID程序的进程映像文件的所有者ID.
允许一个进程将其EUID更改为其RUID/SSUID.当然root特权进程可做任何事.
当访问一个文件时,进程的有效用户ID(Effective User ID,EUID)与文件所有者的UID进行对比.如果该用户不是所有者,那么再对GID进行比较.
文件还有一个执行时设置用户ID位(set-user-ID-on-execution)S_ISUID表示的.该位可以设置给一个可执行进程二进制文件,表示以可执行文件所有者的特权而不是用户组的特权运行(即EUID设置为文件所有者ID).exec->bprm_fill_uid()中实现.
原始socket(SOCK_RAW)可以自行组装数据包(伪装本地 IP,本地 MAC),可以接收本机网卡上所有的数据帧(数据包)。另外,必须在管理员权限下才能使用原始套接字.
inet_create创建原始套接字过程中通过if(sock->type==SOCK_RAW&&capable(CAP_NET_RAW))判断权限;可以参考ping命令实现(setuid(getuid));getuid返回的是RUID;
3 常见文件访问权限问题分析
3.1 在/mnt/testdir目录下创建文件.
1)情景再现:
/mnt/testdir目录访问权限0777: drwx-rwx-rwx 2 root root testdir;
open("/mnt/testdir/testfile",flags,mode);
2)分析:open时系统都做了什么?
- long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
- {
- struct open_flags op;
-
- int fd = build_open_flags(flags, mode, &op);
- struct filename *tmp;
-
- if (fd)
- return fd;
-
- tmp = getname(filename);
- if (IS_ERR(tmp))
- return PTR_ERR(tmp);
-
- fd = get_unused_fd_flags(flags);
- if (fd >= 0) {
-
- struct file *f = do_filp_open(dfd, tmp, &op);
- if (IS_ERR(f)) {
- put_unused_fd(fd);
- fd = PTR_ERR(f);
- } else {
- fsnotify_open(f);
- fd_install(fd, f);
- }
- }
- putname(tmp);
- return fd;
- }
根据open入参flags和mode生产open_flags,以后打开文件操作do_filp_open以此为入参:
调用过程:do_sys_open->build_open_flags
- static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)
- {
- int lookup_flags = 0;
- int acc_mode;
-
-
-
-
-
-
-
-
- if (flags & (O_CREAT | __O_TMPFILE))
- op->mode = (mode & S_IALLUGO) | S_IFREG;
- else
- op->mode = 0;
-
- flags &= ~FMODE_NONOTIFY & ~O_CLOEXEC;
-
- if (flags & __O_SYNC)
- flags |= O_DSYNC;
-
- if (flags & __O_TMPFILE) {
- if ((flags & O_TMPFILE_MASK) != O_TMPFILE)
- return -EINVAL;
- acc_mode = MAY_OPEN | ACC_MODE(flags);
- if (!(acc_mode & MAY_WRITE))
- return -EINVAL;
- } else if (flags & O_PATH) {
- flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;
- acc_mode = 0;
- } else {
-
-
-
-
-
-
-
-
-
-
-
-
-
- acc_mode = MAY_OPEN | ACC_MODE(flags);
- }
-
- op->open_flag = flags;
-
-
- if (flags & O_TRUNC)
- acc_mode |= MAY_WRITE;
-
-
-
- if (flags & O_APPEND)
- acc_mode |= MAY_APPEND;
-
- op->acc_mode = acc_mode;
-
- op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;
-
- if (flags & O_CREAT) {
- op->intent |= LOOKUP_CREATE;
- if (flags & O_EXCL)
- op->intent |= LOOKUP_EXCL;
- }
-
- if (flags & O_DIRECTORY)
- lookup_flags |= LOOKUP_DIRECTORY;
- if (!(flags & O_NOFOLLOW))
- lookup_flags |= LOOKUP_FOLLOW;
- op->lookup_flags = lookup_flags;
- return 0;
- }
do_filp_open->path_openat打开文件:
- static struct file *path_openat(int dfd, struct filename *pathname,
- struct nameidata *nd, const struct open_flags *op, int flags)
- {
- struct file *base = NULL;
- struct file *file;
- struct path path;
- int opened = 0;
- int error;
-
- file = get_empty_filp();
- if (IS_ERR(file))
- return file;
-
- file->f_flags = op->open_flag;
-
- if (unlikely(file->f_flags & __O_TMPFILE)) {
- error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened);
- goto out2;
- }
-
-
-
-
-
-
-
-
-
- error = path_init(dfd, pathname->name, flags | LOOKUP_PARENT, nd, &base);
- if (unlikely(error))
- goto out;
-
- current->total_link_count = 0;
-
-
-
-
-
-
- error = link_path_walk(pathname->name, nd);
- if (unlikely(error))
- goto out;
-
- error = do_last(nd, &path, file, op, &opened, pathname);
- return file;
- }
path_openat->link_path_walk()该函数十分重要,它遍历文件所在路径上的所有目录项,而且能够校验每个目录项对应inode的权限;
link_path_walk之前先通过path_openat->path_init初始nameidata它可以表明从哪个目录开始遍历路径:
static int path_init()
{ /*绝对路径名,从进程的根目录遍历*/
if(*name=='/'){
set_root(nd);
}else if (dfd==AT_FDCWD){ /*相对路径名,从当前目录开始遍历*/
get_fs_pwd(current->fs,&nd->path);
}
}
这个函数通过调用walk_component->lookup_fast/lookup_slow找到每个目录项对应的inode.
link_path_walk->walk_componet先通过lookup_fast查找系统是否创建过dentry(dram上存在dentry结构),如果未找到则再通过
walk_componet->lookup_slow->__lookup_hash->lookup_real->(dir->i_op->lookup()=ubifs_lookup())从flash上读取信息,依此信息,系统创建dentry;
- static int link_path_walk(const char *name, struct nameidata *nd)
- {
- struct path next;
- int err;
-
- while (*name=='/')
- name++;
- if (!*name)
- return 0;
-
-
- for(;;) {
- u64 hash_len;
- int type;
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- err = may_lookup(nd);
- if (err)
- break;
-
- hash_len = hash_name(name);
-
- type = LAST_NORM;
- if (name[0] == '.') switch (hashlen_len(hash_len)) {
- case 2:
- if (name[1] == '.') {
- type = LAST_DOTDOT;
- nd->flags |= LOOKUP_JUMPED;
- }
- break;
- case 1:
- type = LAST_DOT;
- }
- if (likely(type == LAST_NORM)) {
- struct dentry *parent = nd->path.dentry;
- nd->flags &= ~LOOKUP_JUMPED;
- if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {
- struct qstr this = { { .hash_len = hash_len }, .name = name };
- err = parent->d_op->d_hash(parent, &this);
- if (err < 0)
- break;
- hash_len = this.hash_len;
- name = this.name;
- }
- }
-
- nd->last.hash_len = hash_len;
- nd->last.name = name;
- nd->last_type = type;
-
- name += hashlen_len(hash_len);
- if (!*name)
- return 0;
-
-
-
-
- do {
- name++;
- } while (unlikely(*name == '/'));
- if (!*name)
- return 0;
-
-
-
-
- err = walk_component(nd, &next, LOOKUP_FOLLOW);
- if (err < 0)
- return err;
-
- if (err) {
- err = nested_symlink(&next, nd);
- if (err)
- return err;
- }
- if (!d_can_lookup(nd->path.dentry)) {
- err = -ENOTDIR;
- break;
- }
- }
- terminate_walk(nd);
- return err;
- }
path_openat->do_last()打开文件:
- static int do_last(struct nameidata *nd, struct path *path,
- struct file *file, const struct open_flags *op,
- int *opened, struct filename *name)
- {
- struct dentry *dir = nd->path.dentry;
- int open_flag = op->open_flag;
- bool will_truncate = (open_flag & O_TRUNC) != 0;
- bool got_write = false;
- int acc_mode = op->acc_mode;
- struct inode *inode;
- bool symlink_ok = false;
- struct path save_parent = { .dentry = NULL, .mnt = NULL };
- bool retried = false;
- int error;
-
- nd->flags &= ~LOOKUP_PARENT;
- nd->flags |= op->intent;
-
- if (nd->last_type != LAST_NORM) {
- error = handle_dots(nd, nd->last_type);
- if (error)
- return error;
- goto finish_open;
- }
-
- if (!(open_flag & O_CREAT)) {
- if (nd->last.name[nd->last.len])
- nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
- if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW))
- symlink_ok = true;
-
- error = lookup_fast(nd, path, &inode);
-
-
-
-
- if (likely(!error))
- goto finish_lookup;
-
- if (error < 0)
- goto out;
-
- BUG_ON(nd->inode != dir->d_inode);
- } else {
- error = complete_walk(nd);
- if (error)
- return error;
-
- audit_inode(name, dir, LOOKUP_PARENT);
- error = -EISDIR;
-
- if (nd->last.name[nd->last.len])
- goto out;
- }
-
-
-
-
- retry_lookup:
- if (op->open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {
- error = mnt_want_write(nd->path.mnt);
- if (!error)
- got_write = true;
- }
- mutex_lock(&dir->d_inode->i_mutex);
-
-
-
-
-
-
- error = lookup_open(nd, path, file, op, got_write, opened);
- mutex_unlock(&dir->d_inode->i_mutex);
-
- if (error <= 0) {
- if (error)
- goto out;
-
- if ((*opened & FILE_CREATED) ||
- !S_ISREG(file_inode(file)->i_mode))
- will_truncate = false;
-
- audit_inode(name, file->f_path.dentry, 0);
- goto opened;
- }
-
-
-
-
- if (*opened & FILE_CREATED) {
-
- open_flag &= ~O_TRUNC;
- will_truncate = false;
- acc_mode = MAY_OPEN;
- path_to_nameidata(path, nd);
- goto finish_open_created;
- }
-
- if (d_is_positive(path->dentry))
- audit_inode(name, path->dentry, 0);
-
- if (got_write) {
- mnt_drop_write(nd->path.mnt);
- got_write = false;
- }
-
- error = -EEXIST;
- if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))
- goto exit_dput;
-
- error = follow_managed(path, nd->flags);
- if (error < 0)
- goto exit_dput;
-
- if (error)
- nd->flags |= LOOKUP_JUMPED;
-
- BUG_ON(nd->flags & LOOKUP_RCU);
- inode = path->dentry->d_inode;
- finish_lookup:
-
- error = -ENOENT;
- if (!inode || d_is_negative(path->dentry)) {
- path_to_nameidata(path, nd);
- goto out;
- }
-
- if (should_follow_link(path->dentry, !symlink_ok)) {
- if (nd->flags & LOOKUP_RCU) {
- if (unlikely(nd->path.mnt != path->mnt ||
- unlazy_walk(nd, path->dentry))) {
- error = -ECHILD;
- goto out;
- }
- }
- BUG_ON(inode != path->dentry->d_inode);
- return 1;
- }
-
- if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path->mnt) {
- path_to_nameidata(path, nd);
- } else {
- save_parent.dentry = nd->path.dentry;
- save_parent.mnt = mntget(path->mnt);
- nd->path.dentry = path->dentry;
-
- }
- nd->inode = inode;
-
- finish_open:
- error = complete_walk(nd);
- if (error) {
- path_put(&save_parent);
- return error;
- }
- audit_inode(name, nd->path.dentry, 0);
- error = -EISDIR;
- if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry))
- goto out;
- error = -ENOTDIR;
- if ((nd->flags & LOOKUP_DIRECTORY) && !d_can_lookup(nd->path.dentry))
- goto out;
- if (!S_ISREG(nd->inode->i_mode))
- will_truncate = false;
-
- if (will_truncate) {
- error = mnt_want_write(nd->path.mnt);
- if (error)
- goto out;
- got_write = true;
- }
- finish_open_created:
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- error = may_open(&nd->path, acc_mode, open_flag);
- if (error)
- goto out;
-
- BUG_ON(*opened & FILE_OPENED);
-
- error = vfs_open(&nd->path, file, current_cred());
- if (!error) {
- *opened |= FILE_OPENED;
- } else {
- if (error == -EOPENSTALE)
- goto stale_open;
- goto out;
- }
- opened:
- error = open_check_o_direct(file);
- if (error)
- goto exit_fput;
- error = ima_file_check(file, op->acc_mode, *opened);
- if (error)
- goto exit_fput;
-
- if (will_truncate) {
- error = handle_truncate(file);
- if (error)
- goto exit_fput;
- }
- out:
- if (got_write)
- mnt_drop_write(nd->path.mnt);
- path_put(&save_parent);
- terminate_walk(nd);
- return error;
-
- exit_dput:
- path_put_conditional(path, nd);
- goto out;
- exit_fput:
- fput(file);
- goto out;
-
- stale_open:
-
- if (!save_parent.dentry || retried)
- goto out;
-
- BUG_ON(save_parent.dentry != dir);
- path_put(&nd->path);
- nd->path = save_parent;
- nd->inode = dir->d_inode;
- save_parent.mnt = NULL;
- save_parent.dentry = NULL;
- if (got_write) {
- mnt_drop_write(nd->path.mnt);
- got_write = false;
- }
- retried = true;
- goto retry_lookup;
- }
do_last->lookup_open 打开文件:
- static int lookup_open(struct nameidata *nd, struct path *path,
- struct file *file,
- const struct open_flags *op,
- bool got_write, int *opened)
- {
- struct dentry *dir = nd->path.dentry;
- struct inode *dir_inode = dir->d_inode;
- struct dentry *dentry;
- int error;
- bool need_lookup;
-
- *opened &= ~FILE_CREATED;
-
-
-
-
-
- dentry = lookup_dcache(&nd->last, dir, nd->flags, &need_lookup);
- if (IS_ERR(dentry))
- return PTR_ERR(dentry);
-
-
- if (!need_lookup && dentry->d_inode)
- goto out_no_open;
-
- if ((nd->flags & LOOKUP_OPEN) && dir_inode->i_op->atomic_open) {
- return atomic_open(nd, dentry, path, file, op, got_write,
- need_lookup, opened);
- }
-
- if (need_lookup) {
- BUG_ON(dentry->d_inode);
-
- dentry = lookup_real(dir_inode, dentry, nd->flags);
- if (IS_ERR(dentry))
- return PTR_ERR(dentry);
- }
-
-
- if (!dentry->d_inode && (op->open_flag & O_CREAT)) {
- umode_t mode = op->mode;
-
-
-
-
-
-
-
-
-
-
- if (!IS_POSIXACL(dir->d_inode))
- mode &= ~current_umask();
- if (!got_write) {
- error = -EROFS;
- goto out_dput;
- }
- *opened |= FILE_CREATED;
- error = security_path_mknod(&nd->path, dentry, mode, 0);
- if (error)
- goto out_dput;
-
-
-
-
-
-
-
-
-
- error = vfs_create(dir->d_inode, dentry, mode,
- nd->flags & LOOKUP_EXCL);
- if (error)
- goto out_dput;
- }
- out_no_open:
- path->dentry = dentry;
- path->mnt = nd->path.mnt;
- return 1;
-
- out_dput:
- dput(dentry);
- return error;
- }
do_last->lookup_open->lookup_dcache->d_alloc->__d_alloc() 申请新建文件对应的目录项dentry:
- struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)
- {
- struct dentry *dentry;
- char *dname;
-
-
-
-
-
-
- dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);
- if (!dentry)
- return NULL;
-
- dentry->d_iname[DNAME_INLINE_LEN-1] = 0;
- if (name->len > DNAME_INLINE_LEN-1) {
- size_t size = offsetof(struct external_name, name[1]);
- struct external_name *p = kmalloc(size + name->len, GFP_KERNEL);
- if (!p) {
- kmem_cache_free(dentry_cache, dentry);
- return NULL;
- }
- atomic_set(&p->u.count, 1);
- dname = p->name;
- } else {
- dname = dentry->d_iname;
- }
-
- dentry->d_name.len = name->len;
- dentry->d_name.hash = name->hash;
- memcpy(dname, name->name, name->len);
- dname[name->len] = 0;
-
-
- smp_wmb();
- dentry->d_name.name = dname;
-
- dentry->d_lockref.count = 1;
- dentry->d_flags = 0;
- spin_lock_init(&dentry->d_lock);
- seqcount_init(&dentry->d_seq);
- dentry->d_inode = NULL;
- dentry->d_parent = dentry;
- dentry->d_sb = sb;
- dentry->d_op = NULL;
- dentry->d_fsdata = NULL;
- INIT_HLIST_BL_NODE(&dentry->d_hash);
- INIT_LIST_HEAD(&dentry->d_lru);
- INIT_LIST_HEAD(&dentry->d_subdirs);
- INIT_HLIST_NODE(&dentry->d_u.d_alias);
- INIT_LIST_HEAD(&dentry->d_child);
- d_set_d_op(dentry, dentry->d_sb->s_d_op);
-
- this_cpu_inc(nr_dentry);
-
- return dentry;
- }
函数继续执行,因为是ubifs文件系统,所以
do_last->lookup_open->vfs_create->ubifs_create->ubifs_new_inode:
注意:操作系统中的inode信息(dram上描述inode的信息,区别于flash上的inode信息)的初始化时机包括:
1>是在do_last->lookup_open->vfs_create->ubifs_create->ubifs_new_inode创建一个文件时初始化的.
2>对于已存在文件,是在do_last->lookup_open->lookup_real->ubifs_lookup->ubifs_iget
- struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,
- umode_t mode)
- {
- struct inode *inode;
- struct ubifs_inode *ui;
-
-
-
-
- inode = new_inode(c->vfs_sb);
- ui = ubifs_inode(inode);
- if (!inode)
- return ERR_PTR(-ENOMEM);
-
- inode->i_flags |= S_NOCMTIME;
-
- inode_init_owner(inode, dir, mode);
- inode->i_mtime = inode->i_atime = inode->i_ctime =
- ubifs_current_time(inode);
- inode->i_mapping->nrpages = 0;
-
- inode->i_mapping->backing_dev_info = &c->bdi;
-
- switch (mode & S_IFMT) {
- case S_IFREG:
- inode->i_mapping->a_ops = &ubifs_file_address_operations;
- inode->i_op = &ubifs_file_inode_operations;
- inode->i_fop = &ubifs_file_operations;
- break;
- case S_IFDIR:
- inode->i_op = &ubifs_dir_inode_operations;
- inode->i_fop = &ubifs_dir_operations;
- inode->i_size = ui->ui_size = UBIFS_INO_NODE_SZ;
- break;
- case S_IFLNK:
- inode->i_op = &ubifs_symlink_inode_operations;
- break;
- case S_IFSOCK:
- case S_IFIFO:
- case S_IFBLK:
- case S_IFCHR:
- inode->i_op = &ubifs_file_inode_operations;
- break;
- default:
- BUG();
- }
-
- ui->flags = inherit_flags(dir, mode);
- ubifs_set_inode_flags(inode);
- if (S_ISREG(mode))
- ui->compr_type = c->default_compr;
- else
- ui->compr_type = UBIFS_COMPR_NONE;
- ui->synced_i_size = 0;
-
- spin_lock(&c->cnt_lock);
-
- if (c->highest_inum >= INUM_WARN_WATERMARK) {
- if (c->highest_inum >= INUM_WATERMARK) {
- spin_unlock(&c->cnt_lock);
- ubifs_err("out of inode numbers");
- make_bad_inode(inode);
- iput(inode);
- return ERR_PTR(-EINVAL);
- }
- ubifs_warn("running out of inode numbers (current %lu, max %d)",
- (unsigned long)c->highest_inum, INUM_WATERMARK);
- }
-
- inode->i_ino = ++c->highest_inum;
- ui->creat_sqnum = ++c->max_sqnum;
- spin_unlock(&c->cnt_lock);
- return inode;
- }
初始化文件权限,所属用户,包括:inode->i_mode,inode->i_uid,inode->i_gid:ubifs_new_inode->inode_init_owner:
- void inode_init_owner(struct inode *inode, const struct inode *dir,
- umode_t mode)
- {
-
-
-
-
- inode->i_uid = current_fsuid();
- if (dir && dir->i_mode & S_ISGID) {
- inode->i_gid = dir->i_gid;
- if (S_ISDIR(mode))
- mode |= S_ISGID;
- } else
- inode->i_gid = current_fsgid();
-
-
-
-
- inode->i_mode = mode;
- }
真正打开文件之前对,文件权限的校验:may_open->inode_permission->generic_permission()->acl_permission_check()
- static int acl_permission_check(struct inode *inode, int mask)
- {
- unsigned int mode = inode->i_mode;
-
-
-
-
-
-
-
-
- if (likely(uid_eq(current_fsuid(), inode->i_uid)))
- mode >>= 6;
- else {
-
-
-
- if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {
- int error = check_acl(inode, mask);
- if (error != -EAGAIN)
- return error;
- }
-
- if (in_group_p(inode->i_gid))
- mode >>= 3;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
- if ((mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
- return 0;
- return -EACCES;
- }
至此完成了文件访问权限的校验,前面的分析,其实都是为acl_permission_check()做铺垫,值得注意的时,超级用户权限不完全受acl_permission_check()检查的限制,如果它检查不过,会在acl_permission_check()之后进一步做其他检查,此处不再分析,感兴趣的同学可以看下.
通过上面分析,可以总结出open一个文件时,几个权限校验的关键节点:
第一步权限检查: 最开始对文件所在路径上每个目录项对应的inode进行执行权限检查.
对应代码:path_openat->link_path_walk->may_lookup->inode_permission;
第二步权限检查:如果是新建文件,对文件所在目录项的inode做写和可执行权限检查.
对应代码:path_openat->do_last->lookup_open->vfs_create->may_create->inode_permission.
第三步权限检查:真正打开文件之前,对文件所对应的inode做读写权限检查.
对应代码:path_openat->do_last->may_open->inode_permission.
可以在常见情况分析中看到,实际使用情况。
【常见情况分析】
1 /mnt/testdir属性为0666,属于root用户。即:ls /mnt -l :-drw-rw-rw root roottestdir
非root用户创建新文件:
操作:open("/mnt/testdir/testfile",O_CREATE|O_RDWR,S_IRWO);以读写方式打开文件,如果新建文件则inode属性设为其他用户可读写,即007;
结果:open失败;
原因分析:根据上面分析,在代码path_openat->link_path_walk->may_lookup->inode_permission中,即在权限校验的第一步中,会校验testdir的执行权限,此处校验失败;
2 /mnt/testdir属性为0661,属于root用户。即:ls /mnt -l :-drw-rw-x root root testdir
操作:open("/mnt/testdir/testfile",O_CREATE|O_RDWR,S_IRWO);以读写方式打开文件,如果新建文件则inode属性设为其他用户可读写,即007;
结果:open失败;
原因分析:根据上面分析,在代码path_openat->do_last->lookup_open->vfs_create->may_create->inode_permission中,即在权限校验的第二步中,会校验testdir的写权限和执行权限,此处校验失败;
3 /mnt/testdir属性为0663,属于root用户。即:ls /mnt -l :-drw-rw-xw root root testdir
操作:open("/mnt/testdir/testfile",O_CREATE|O_RDWR,S_IRWO);以读写方式打开文件,如果新建文件则inode属性设为其他用户可读写,即007;
结果:open成功;
原因分析:根据上面分析,在代码path_openat->do_last->lookup_open->vfs_create->may_create->inode_permission中,即在权限校验的第二步中,会校验testdir的写权限和执行权限,此处testdir权限为0663,所以其他用户权限是003,可见是有写权限和执行权限的;但是在打开文件时需要检查读写权限(可以参考acc_mode的生成),本该打开失败,为什么还会成功呢?这是因为对于新建文件时,do_last中会将acc_mode=MAY_OPEN,所以只要能完成第二步权限检查,在第三步权限检查中就不再进行rwx权限检查了(可参考do_last实现)。
4 创建新文件权限为007,即具有rwx权限
open("/mnt/testdir/testfile",O_CREATE|O_RDWR,S_IRWO)
结果:创建出来的文件为005,即具有rx权限,这是因为创建文件时,权限都要与上~022,即非文件所有者用户不能写文件,可参考上文介绍。
【正文二:目录管理】
目录权限的管理虽然和文件权限的管理不是同一个软件流程,但管理方法大同小异,所有可以参考文件权限管理的流程,去参看目录权限管理.
目前权限管理的简单介绍:
创建目录时mkdir:
第一步权限检查: 最开始对目录所在路径上每个目录项对应的inode进行执行权限检查。
对应代码:path_lookupat->link_path_walk->may_lookup->inode_permission ;
第二步权限检查:目录所在目录项对应的inode做写和可执行权限检查。
对应代码:vfs_mkdir->may_create->inode_permission。
删除目录时rmdir:
第一步权限检查:path_lookupat->link_path_walk->may_lookup->inode_permission ;
第二步权限检查:vfs_rmdir->may_delete中检查目录所在目录项对应的inode的写和可执行权限。
针对几种情况做分析:
1 /mnt/testdir目录访问权限0666:
drw-rw-rw 2 root root testdir
场景:文件所属用户(root用户组 uid=0,gid=0)可读写;同组用户(uid!=0,gid=0)可读写、其他用户(uid!=0,gid!=0)可读写
操作:如果其他用户(如uid=1001,gid=1001)要在testdir目录下创建子目录:
结果:mkdir("/mnt/testdir/subdir",0666) 创建会失败;
原因:这是因为目录创建和文件创建一样,也会在may_create中对目录项对应的inode的写权限和可执行权限做检查。
2 /mnt/testdir目录访问权限0777:
drwx-rwx-rwx 2 root root testdir
1)如果其他用户(如uid=1001,gid=1001)要在testdir目录下创建子目录:mkdir("/mnt/testdir/subdir",0777);
drwx-rwx-rwx 2 1001 1001 subdir
rmdir("/mnt/testdir")成功;
rmdir("/mnt/testdir/subidr")成功;
【总结】
通过上面的分析,我们看到一个文件同时对应一个目录项dentry和一个inode,而且一个文件的目录项dentry->d_inode保存了这个文件对应的inode,所以如果查找一个文件对应的inode,要先查找这个文件对应的目录项dentry。系统为什么要这么做,为什么不直接用一个结构体表达文件,而要同时用dentry和inode表示文件。这是因为文件一般都存在于一个绝对路径之下,比如/mnt/testdir/testfile,一般来说系统里目录项dentry更倾向于表达一个文件整个路径上每个目录,如:mnt,testdir,testfile都对应一个目录项,虽然他们也同时对应inode,但是因为用户操作文件时通常只是想操作testfile对应的inode,所以inode更倾向于表示testfile的inode。不知道是否描述清楚,举个实际例子说明问题吧:
例子:用户操作文件/mnt/testdir/testfile,系统查找testfile对应inode的过程:
1 首先从根目录查起/,根目录“/“”对应一个目录项。
2 link_path_walk中遍历路径上的所有目录项,一直查到testdir对应的目录项,都可以查找到。此过程会对每个目录项对应的inode进行执行权限检查。
3 lookup_dcache中查找到了文件testfile对应的目录项,注意他是基于第2步中查找到的testdir对应的目录项开始查找的。
4 当查找testfile对应的目录项时,有两种情况:
一、文件已经存在,则查找到testfile对应目录项,接下来根据dentry->d_inode,又查找到了文件对应的inode,然后就可以对文件进行操作了。
二、文件不存在,如果时open函数,则可以创建testfile文件,创建文件testfile,首先创建testfile对应的目录项dentry(looup_open->lookup_dcache中完成),目录项创建成功后,再创建inode结点(lookup_open->vfs_create中完成),vfs_create过程会通过d_instantatiate将inode赋值给dentry->d_inode,所以第一种情况,可以根据先查找到的testfile对应的目录项dentry找到testfile对应的inode。创建inode过程会对文件所属目录项对应的inode,进行写权限和执行权限检查。
5 may_open真正打开文件之前会对文件读写权限进行检查。
打开文件时,系统对文件权限的检查,其实就发生在根据文件所属路径查找文件的过程中。