您现在的位置是:首页 > 文章详情

linux内核文件权限管理

日期:2017-12-17点击:1385

【摘要】

【正文一:文件管理】

【常见情况分析】

【正文二:目录管理】

【总结】

 

注意:请使用谷歌浏览器阅读(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时系统都做了什么?

[cpp] view plain  copy

 

  1. long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)  
  2. {  
  3.     struct open_flags op;  
  4.     /* 根据open入参flags和mode生产open_flags,以后打开文件操作do_filp_open以此为入参 */    
  5.     int fd = build_open_flags(flags, mode, &op);  
  6.     struct filename *tmp;  
  7.   
  8.     if (fd)  
  9.         return fd;  
  10.   
  11.     tmp = getname(filename);  
  12.     if (IS_ERR(tmp))  
  13.         return PTR_ERR(tmp);  
  14.   
  15.     fd = get_unused_fd_flags(flags);  
  16.     if (fd >= 0) {  
  17.         /* 打开文件操作*/  
  18.         struct file *f = do_filp_open(dfd, tmp, &op);  
  19.         if (IS_ERR(f)) {  
  20.             put_unused_fd(fd);  
  21.             fd = PTR_ERR(f);  
  22.         } else {  
  23.             fsnotify_open(f);  
  24.             fd_install(fd, f);  
  25.         }  
  26.     }  
  27.     putname(tmp);  
  28.     return fd;  
  29. }  

根据open入参flags和mode生产open_flags,以后打开文件操作do_filp_open以此为入参:

调用过程:do_sys_open->build_open_flags

[cpp] view plain  copy

 

  1. static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)  
  2. {  
  3.     int lookup_flags = 0;  
  4.     int acc_mode;  
  5.     /* 
  6.      注意此处:   
  7.     1如果open时flags没有设置O_CREATE标志,则无论open入参mode是何值,系统都认为是0,即未使用mode入参。 
  8.     2如果open时flags设置O_CREATE标志,则需要重新生成mode,其中S_IALLUGO是所有访问权限的并集。 
  9.       例如:以mode=0777打开文件,经此处理后变为0100777; 
  10.     S_IFREG=0100000 表示普通文件 
  11.     S_IFDIR=040000  表示目录文件 
  12.      */  
  13.     if (flags & (O_CREAT | __O_TMPFILE))  
  14.         op->mode = (mode & S_IALLUGO) | S_IFREG;  
  15.     else  
  16.         op->mode = 0;  
  17.   
  18.     flags &= ~FMODE_NONOTIFY & ~O_CLOEXEC;  
  19.   
  20.     if (flags & __O_SYNC)  
  21.         flags |= O_DSYNC;  
  22.   
  23.     if (flags & __O_TMPFILE) {  
  24.         if ((flags & O_TMPFILE_MASK) != O_TMPFILE)  
  25.             return -EINVAL;  
  26.         acc_mode = MAY_OPEN | ACC_MODE(flags);  
  27.         if (!(acc_mode & MAY_WRITE))  
  28.             return -EINVAL;  
  29.     } else if (flags & O_PATH) {  
  30.         flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH;  
  31.         acc_mode = 0;  
  32.     } else {  
  33.     /* 
  34.     acc_mode主要是对读写权限的校验: 
  35.         真正打开文件之前may_open通过inode_permission函数把acc_mode与inode->i_mode做比较,实现访问权限校验,inode->i_mode就是ls中看到的文件形如-rwx-的权限. 
  36.     举例: ls /mnt/testdir/testfile -l :-rwx-rwx-rwx 即0777; 
  37.     open("/mnt/testdir/testfile",O_RW);以读写方式打开一个文件,即flags=O_RW,从而acc_mode=06 (表示rwx中r位和w位都为1) 
  38.     打开文件之前会在inode_permission中校验inode->imode是否有读写权限,如本例中0777显然对所有用户都有读写权限,此处可以参考inode_permission一起分析. 
  39.     acc_mode=MAY_OPEN|ACC_MODE(flags): 
  40.     其中:MAY_OPEN=0x20. 
  41.     ACC_MODE(flags)取值为: 
  42.     当open函数入参flags = O_RDONLY (O_RDONLY=0)时,ACC_MODE(flags)=004 即:004代表bit2=1表示读权限打开,acc_mode=024. 
  43.     当flags = O_WRONLY =1时ACC_MODE(flags)=002 即:002代表bit1=1表示写权限,acc_mode=0x22. 
  44.     当flags = O_RDWR = 2时ACC_MODE(flags)=006 即:006代表bit2=1|bit1=1表示读写权限,acc_mode=0x26. 
  45.     */  
  46.         acc_mode = MAY_OPEN | ACC_MODE(flags);  
  47.     }  
  48.   
  49.     op->open_flag = flags;  
  50.   
  51.     /* O_TRUNC implies we need access checks for write permissions */  
  52.     if (flags & O_TRUNC)  
  53.         acc_mode |= MAY_WRITE;  
  54.   
  55.     /* Allow the LSM permission hook to distinguish append 
  56.        access from general write access. */  
  57.     if (flags & O_APPEND)  
  58.         acc_mode |= MAY_APPEND;  
  59.   
  60.     op->acc_mode = acc_mode;  
  61.   
  62.     op->intent = flags & O_PATH ? 0 : LOOKUP_OPEN;  
  63.   
  64.     if (flags & O_CREAT) {  
  65.         op->intent |= LOOKUP_CREATE;  
  66.         if (flags & O_EXCL)  
  67.             op->intent |= LOOKUP_EXCL;  
  68.     }  
  69.   
  70.     if (flags & O_DIRECTORY)  
  71.         lookup_flags |= LOOKUP_DIRECTORY;  
  72.     if (!(flags & O_NOFOLLOW))  
  73.         lookup_flags |= LOOKUP_FOLLOW;  
  74.     op->lookup_flags = lookup_flags;  
  75.     return 0;  
  76. }  

do_filp_open->path_openat打开文件:

[cpp] view plain  copy

 

  1. static struct file *path_openat(int dfd, struct filename *pathname,  
  2.         struct nameidata *nd, const struct open_flags *op, int flags)  
  3. {  
  4.     struct file *base = NULL;  
  5.     struct file *file;  
  6.     struct path path;  
  7.     int opened = 0;  
  8.     int error;  
  9.         /* 创建struct *file并初始化,open过程中会把一个文件句柄与该file绑定,用户读写文件时会通过文件句柄找到该file进行一系列操作 */  
  10.     file = get_empty_filp();  
  11.     if (IS_ERR(file))  
  12.         return file;  
  13.   
  14.     file->f_flags = op->open_flag;  
  15.   
  16.     if (unlikely(file->f_flags & __O_TMPFILE)) {  
  17.         error = do_tmpfile(dfd, pathname, nd, flags, op, file, &opened);  
  18.         goto out2;  
  19.     }  
  20.     /*  
  21.     作用: 
  22.     1 初始化nameidata 包括文件名赋值给nd->last.name等; 
  23.     2 初始化nd->path.dentry为该superblock根目录 / 对应的目录项.此时nd->path.dentry->d_iname=/; 
  24.     dentry->d_inode->i_ino为根目录/对应的inode num;  
  25.         该函数为下一步link_path_walk中查找文件所在目录文件的目录项做准备, 
  26.     因为此时dentry初始化为根目录,所以从根目录开始查找文件所在目录. 
  27.     实际上path_init中将nd->path,nd->root都初始化为current->fs->root,nd->root也是struct path结构;而当前进程的current->fs->root是根目录"/"; 
  28.     */  
  29.     error = path_init(dfd, pathname->name, flags | LOOKUP_PARENT, nd, &base);  
  30.     if (unlikely(error))  
  31.         goto out;  
  32.   
  33.     current->total_link_count = 0;  
  34.     /*   
  35.     作用:找到文件所在目录的目录项。     
  36.     举例:open("/mnt/testdir/testfile",flags,mode); 
  37.     此时nd->path.dentry->d_iname为/mnt/testdir 
  38.     即:dentry->d_iname=testdir;dentry->d_inode->i_ino=testdir对应的inode number。 
  39.     */  
  40.     error = link_path_walk(pathname->name, nd);  
  41.     if (unlikely(error))  
  42.         goto out;  
  43.     /*关键函数 该函数中会初始化struct file;*/  
  44.     error = do_last(nd, &path, file, op, &opened, pathname);  
  45.     return file;  
  46. }  

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;

[cpp] view plain  copy

 

  1. static int link_path_walk(const char *name, struct nameidata *nd)  
  2. {  
  3.     struct path next;  
  4.     int err;  
  5.       
  6.     while (*name=='/')  
  7.         name++;  
  8.     if (!*name)  
  9.         return 0;  
  10.   
  11.     /* At this point we know we have a real path component. */  
  12.     for(;;) {  
  13.         u64 hash_len;  
  14.         int type;  
  15.         /* 
  16.         for循环里,通过may_lookup函数校验文件所在路径上每个目录项对应的inode权限。 
  17.         其实在may_open和may_create时都需要对inode权限进行检查。 
  18.         注意它和may_open区别: 
  19.         第一点区别:may_open是打开文件前,对文件对应的inode的权限检查,而没有检查文件所在路径上每个目录项对应的inode, 
  20.         正是因为在此检查过了。 
  21.         第二点区别:may_open检查inode的acc_mode主要对读写bit进行检查,即may_open->inode_permission(inode,acc_mode); 
  22.         acc_mode可以参考build_open_flags和acl_permission_check两个函数的分析;may_lookup中检查的是MAY_EXEC|MAY_NOT_BLOCK; 
  23.         即may_lookup->inode_permisson(inode,MAY_EXEC|MAY_NOT_BLOCK),其实主要检查每个inode的rwx中的x位; 
  24.         通过may_lookup和may_open一个文件inode的rwx都检查到了。 
  25.         举例:open(/mnt/testdir/testfile,O_RDWR);testdir权限为0641; 
  26.         处理过程:系统通过link_path_walk遍历了mnt,testdir,testfile对应的目录项,并通过may_lookup检查了每个目录项对应inode的权限 
  27.         注意只检查了执行权限,即x位,如果检查通过,会在以后的may_open中继续检查rw位,may_open的检查可以看后续分析。 
  28.         还有一点如果文件不存在,还会在lookup_open->vfs_create->may_create中检查文件所在目录testdir的写权限.    
  29.         值得注意的是,link_path_walk之后path->dentry指向了testdir对应目录项,而不是testfile对应的目录项; 
  30.         link_path_walk虽然在for循环中遍历了/mnt/testdir/testfile所有目录项和inode,但并没有对testfile文件对应的目录项dentry和inode 
  31.         做校验。而是在以后的处理流程中处理的,可以参考本文以下介绍。 
  32.         */  
  33.         err = may_lookup(nd);  
  34.         if (err)  
  35.             break;  
  36.   
  37.         hash_len = hash_name(name);  
  38.   
  39.         type = LAST_NORM;  
  40.         if (name[0] == '.'switch (hashlen_len(hash_len)) {  
  41.             case 2:  
  42.                 if (name[1] == '.') {  
  43.                     type = LAST_DOTDOT;  
  44.                     nd->flags |= LOOKUP_JUMPED;  
  45.                 }  
  46.                 break;  
  47.             case 1:  
  48.                 type = LAST_DOT;  
  49.         }  
  50.         if (likely(type == LAST_NORM)) {  
  51.             struct dentry *parent = nd->path.dentry;  
  52.             nd->flags &= ~LOOKUP_JUMPED;  
  53.             if (unlikely(parent->d_flags & DCACHE_OP_HASH)) {  
  54.                 struct qstr this = { { .hash_len = hash_len }, .name = name };  
  55.                 err = parent->d_op->d_hash(parent, &this);  
  56.                 if (err < 0)  
  57.                     break;  
  58.                 hash_len = this.hash_len;  
  59.                 name = this.name;  
  60.             }  
  61.         }  
  62.   
  63.         nd->last.hash_len = hash_len;  
  64.         nd->last.name = name;  
  65.         nd->last_type = type;  
  66.   
  67.         name += hashlen_len(hash_len);  
  68.         if (!*name)  
  69.             return 0;  
  70.         /* 
  71.          * If it wasn't NUL, we know it was '/'. Skip that 
  72.          * slash, and continue until no more slashes. 
  73.          */  
  74.         do {  
  75.             name++;  
  76.         } while (unlikely(*name == '/'));  
  77.         if (!*name)  
  78.             return 0;  
  79.         /* 
  80.         for循环中通过wak_component找到文件路径上每个目录项和对应的inode,并在for循环中通过may_lookup检查权限。 
  81.         检查到的目录项和inode赋值给nd->path->dentry和nd->path->dentry->d_inode 
  82.         */  
  83.         err = walk_component(nd, &next, LOOKUP_FOLLOW);  
  84.         if (err < 0)  
  85.             return err;  
  86.   
  87.         if (err) {  
  88.             err = nested_symlink(&next, nd);  
  89.             if (err)  
  90.                 return err;  
  91.         }  
  92.         if (!d_can_lookup(nd->path.dentry)) {  
  93.             err = -ENOTDIR;   
  94.             break;  
  95.         }  
  96.     }  
  97.     terminate_walk(nd);  
  98.     return err;  
  99. }  

path_openat->do_last()打开文件:

[cpp] view plain  copy

 

  1. static int do_last(struct nameidata *nd, struct path *path,  
  2.            struct file *file, const struct open_flags *op,  
  3.            int *opened, struct filename *name)  
  4. {  
  5.     struct dentry *dir = nd->path.dentry;  
  6.     int open_flag = op->open_flag;  
  7.     bool will_truncate = (open_flag & O_TRUNC) != 0;  
  8.     bool got_write = false;  
  9.     int acc_mode = op->acc_mode;  
  10.     struct inode *inode;  
  11.     bool symlink_ok = false;  
  12.     struct path save_parent = { .dentry = NULL, .mnt = NULL };  
  13.     bool retried = false;  
  14.     int error;  
  15.   
  16.     nd->flags &= ~LOOKUP_PARENT;  
  17.     nd->flags |= op->intent;  
  18.   
  19.     if (nd->last_type != LAST_NORM) {  
  20.         error = handle_dots(nd, nd->last_type);  
  21.         if (error)  
  22.             return error;  
  23.         goto finish_open;  
  24.     }  
  25.     /* 没有设置O_CREATE标志,此时op->mode=0 */  
  26.     if (!(open_flag & O_CREAT)) {  
  27.         if (nd->last.name[nd->last.len])  
  28.             nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;  
  29.         if (open_flag & O_PATH && !(nd->flags & LOOKUP_FOLLOW))  
  30.             symlink_ok = true;  
  31.         /* we _can_ be in RCU mode here */  
  32.         error = lookup_fast(nd, path, &inode);  
  33.         /* 
  34.         对于已经存在的文件,lookup_fast找到inode,并跳过lookup_open函数; 
  35.         对于不存在的文件,lookup_open在创建新文件时使用,会为新文件申请inode等操作。 
  36.         */        
  37.         if (likely(!error))  
  38.             goto finish_lookup;  
  39.         /* 未找到 inode,打开文件失败*/  
  40.         if (error < 0)  
  41.             goto out;  
  42.   
  43.         BUG_ON(nd->inode != dir->d_inode);  
  44.     } else {  
  45.         error = complete_walk(nd);  
  46.         if (error)  
  47.             return error;  
  48.         /* 设置了O_CREATE标志 ,op->mode为build_open_flags中生成*/  
  49.         audit_inode(name, dir, LOOKUP_PARENT);  
  50.         error = -EISDIR;  
  51.         /* trailing slashes? */  
  52.         if (nd->last.name[nd->last.len])  
  53.             goto out;  
  54.     }  
  55. /*  
  56. open新建文件时 在此申请新文件的目录项dentry和inode结点 
  57. open已创建文件时,不需要执行,直接goto finish_lookup: 
  58. */  
  59. retry_lookup:  
  60.     if (op->open_flag & (O_CREAT | O_TRUNC | O_WRONLY | O_RDWR)) {  
  61.         error = mnt_want_write(nd->path.mnt);  
  62.         if (!error)  
  63.             got_write = true;  
  64.     }  
  65.     mutex_lock(&dir->d_inode->i_mutex);  
  66.     /* 
  67.     当需要创建新文件时,此函数完成inode申请及初始化 
  68.     其中包括文件权限的初始化:inode->i_mode;目录项的申请和初始化; 
  69.     may_open校验文件权限时会用到inode->i_mode文件权限 
  70.     */  
  71.       
  72.     error = lookup_open(nd, path, file, op, got_write, opened);  
  73.     mutex_unlock(&dir->d_inode->i_mutex);  
  74.   
  75.     if (error <= 0) {  
  76.         if (error)  
  77.             goto out;  
  78.   
  79.         if ((*opened & FILE_CREATED) ||  
  80.             !S_ISREG(file_inode(file)->i_mode))  
  81.             will_truncate = false;  
  82.   
  83.         audit_inode(name, file->f_path.dentry, 0);  
  84.         goto opened;  
  85.     }  
  86. /* 
  87. 新建文件时lookup_open中将opened |= FILE_CREATED;此时acc_mode=MAY_OPEN,所以接下来,打开文件之前 
  88. may_open->inode_permission->acl_permission_check中不再进行rwx权限检查.因为检查的是acc_mode,其没有指定的rwx 
  89. */  
  90.     if (*opened & FILE_CREATED) {  
  91.         /* Don't check for write permission, don't truncate */  
  92.         open_flag &= ~O_TRUNC;  
  93.         will_truncate = false;  
  94.         acc_mode = MAY_OPEN;  
  95.         path_to_nameidata(path, nd);  
  96.         goto finish_open_created;  
  97.     }  
  98.   
  99.     if (d_is_positive(path->dentry))  
  100.         audit_inode(name, path->dentry, 0);  
  101.   
  102.     if (got_write) {  
  103.         mnt_drop_write(nd->path.mnt);  
  104.         got_write = false;  
  105.     }  
  106.   
  107.     error = -EEXIST;  
  108.     if ((open_flag & (O_EXCL | O_CREAT)) == (O_EXCL | O_CREAT))  
  109.         goto exit_dput;  
  110.   
  111.     error = follow_managed(path, nd->flags);  
  112.     if (error < 0)  
  113.         goto exit_dput;  
  114.   
  115.     if (error)  
  116.         nd->flags |= LOOKUP_JUMPED;  
  117.   
  118.     BUG_ON(nd->flags & LOOKUP_RCU);  
  119.     inode = path->dentry->d_inode;  
  120. finish_lookup:  
  121.     /* we _can_ be in RCU mode here */  
  122.     error = -ENOENT;  
  123.     if (!inode || d_is_negative(path->dentry)) {  
  124.         path_to_nameidata(path, nd);  
  125.         goto out;  
  126.     }  
  127.   
  128.     if (should_follow_link(path->dentry, !symlink_ok)) {  
  129.         if (nd->flags & LOOKUP_RCU) {  
  130.             if (unlikely(nd->path.mnt != path->mnt ||  
  131.                      unlazy_walk(nd, path->dentry))) {  
  132.                 error = -ECHILD;  
  133.                 goto out;  
  134.             }  
  135.         }  
  136.         BUG_ON(inode != path->dentry->d_inode);  
  137.         return 1;  
  138.     }  
  139.   
  140.     if ((nd->flags & LOOKUP_RCU) || nd->path.mnt != path->mnt) {  
  141.         path_to_nameidata(path, nd);  
  142.     } else {  
  143.         save_parent.dentry = nd->path.dentry;  
  144.         save_parent.mnt = mntget(path->mnt);  
  145.         nd->path.dentry = path->dentry;  
  146.   
  147.     }  
  148.     nd->inode = inode;  
  149.     /* Why this, you ask?  _Now_ we might have grown LOOKUP_JUMPED... */  
  150. finish_open:  
  151.     error = complete_walk(nd);  
  152.     if (error) {  
  153.         path_put(&save_parent);  
  154.         return error;  
  155.     }  
  156.     audit_inode(name, nd->path.dentry, 0);  
  157.     error = -EISDIR;  
  158.     if ((open_flag & O_CREAT) && d_is_dir(nd->path.dentry))  
  159.         goto out;  
  160.     error = -ENOTDIR;  
  161.     if ((nd->flags & LOOKUP_DIRECTORY) && !d_can_lookup(nd->path.dentry))  
  162.         goto out;  
  163.     if (!S_ISREG(nd->inode->i_mode))  
  164.         will_truncate = false;  
  165.   
  166.     if (will_truncate) {  
  167.         error = mnt_want_write(nd->path.mnt);  
  168.         if (error)  
  169.             goto out;  
  170.         got_write = true;  
  171.     }  
  172. finish_open_created:  
  173.     /* 
  174.     文件访问权限的校验,主要是校验是否有权限访问inode,函数may_open->inode_permission中实现; 
  175.     其中may_open函数能根据nd->path找到文件对应的目录项dentry和inode结点. 
  176.     acc_mode是在build_open_flags根据open函数入参flags生成的.对于新建文件,acc_mode可能被更改. 
  177.     如果新建文件时lookup_open中将opened |= FILE_CREATED则在上面retry_lookup中acc_mode=MAY_OPEN. 
  178.     acc_mode主要是对读写权限的校验: 
  179.         真正打开文件之前may_open通过inode_permission函数把acc_mode与inode->i_mode做比较,实现访问权限校验,inode->i_mode就是ls中看到的文件形如-rwx-的权限。 
  180.     举例: ls /mnt/testdir/testfile -l :-rwx-rwx-rwx 即0777; 
  181.     open("/mnt/testdir/testfile",O_RW);以读写方式打开一个文件,即flags=O_RW,从而acc_mode=06 (表示rwx中r位和w位都为1) 
  182.     打开文件之前会在inode_permission中校验inode->imode是否有读写权限,如本例中0777显然对所有用户都有读写权限,此处可以参考inode_permission一起分析。 
  183.     acc_mode=MAY_OPEN|ACC_MODE(flags): 
  184.     其中:MAY_OPEN=0x20 
  185.     ACC_MODE(flags)取值为: 
  186.          当open函数入参flags=O_RDONLY=0时ACC_MODE(flags)=004 即:004代表bit2=1表示读权限打开; 
  187.          当flags=O_WRONLY=1时ACC_MODE(flags)=002 即:002代表bit1=1表示写权限; 
  188.          当flags=O_RDWR=2时ACC_MODE(flags)=006 即:006代表bit2=1|bit1=1表示读权限; 
  189.     */  
  190.     error = may_open(&nd->path, acc_mode, open_flag);  
  191.     if (error)  
  192.         goto out;  
  193.   
  194.     BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */  
  195.         /* 真正的文件打开操作:vfs_open->do_dentry_open系列调用中初始化struct file*/  
  196.     error = vfs_open(&nd->path, file, current_cred());  
  197.     if (!error) {  
  198.         *opened |= FILE_OPENED;  
  199.     } else {  
  200.         if (error == -EOPENSTALE)  
  201.             goto stale_open;  
  202.         goto out;  
  203.     }  
  204. opened:  
  205.     error = open_check_o_direct(file);  
  206.     if (error)  
  207.         goto exit_fput;  
  208.     error = ima_file_check(file, op->acc_mode, *opened);  
  209.     if (error)  
  210.         goto exit_fput;  
  211.   
  212.     if (will_truncate) {  
  213.         error = handle_truncate(file);  
  214.         if (error)  
  215.             goto exit_fput;  
  216.     }  
  217. out:  
  218.     if (got_write)  
  219.         mnt_drop_write(nd->path.mnt);  
  220.     path_put(&save_parent);  
  221.     terminate_walk(nd);  
  222.     return error;  
  223.   
  224. exit_dput:  
  225.     path_put_conditional(path, nd);  
  226.     goto out;  
  227. exit_fput:  
  228.     fput(file);  
  229.     goto out;  
  230.   
  231. stale_open:  
  232.     /* If no saved parent or already retried then can't retry */  
  233.     if (!save_parent.dentry || retried)  
  234.         goto out;  
  235.   
  236.     BUG_ON(save_parent.dentry != dir);  
  237.     path_put(&nd->path);  
  238.     nd->path = save_parent;  
  239.     nd->inode = dir->d_inode;  
  240.     save_parent.mnt = NULL;  
  241.     save_parent.dentry = NULL;  
  242.     if (got_write) {  
  243.         mnt_drop_write(nd->path.mnt);  
  244.         got_write = false;  
  245.     }  
  246.     retried = true;  
  247.     goto retry_lookup;  
  248. }  

do_last->lookup_open 打开文件:

[cpp] view plain  copy

 

  1. static int lookup_open(struct nameidata *nd, struct path *path,  
  2.             struct file *file,  
  3.             const struct open_flags *op,  
  4.             bool got_write, int *opened)  
  5. {  
  6.     struct dentry *dir = nd->path.dentry;  
  7.     struct inode *dir_inode = dir->d_inode;  
  8.     struct dentry *dentry;  
  9.     int error;  
  10.     bool need_lookup;  
  11.   
  12.     *opened &= ~FILE_CREATED;  
  13.     /* 
  14.     作用:为文件创建目录项。找到文件对应的目录项,如果没找到则为新文件申请目录项; 
  15.     函数link_path_walk中nd->path.dentry赋值为文件所在目录的目录项。此时根据nd->path.dentry生成文件对应的目录项dentry 
  16.     lookup_dcache->d_alloc中申请新建文件对应的dentry,并初始化该dentry    
  17.     */  
  18.     dentry = lookup_dcache(&nd->last, dir, nd->flags, &need_lookup);  
  19.     if (IS_ERR(dentry))  
  20.         return PTR_ERR(dentry);  
  21.   
  22.     /* Cached positive dentry: will open in f_op->open */  
  23.     if (!need_lookup && dentry->d_inode)  
  24.         goto out_no_open;  
  25.   
  26.     if ((nd->flags & LOOKUP_OPEN) && dir_inode->i_op->atomic_open) {  
  27.         return atomic_open(nd, dentry, path, file, op, got_write,  
  28.                    need_lookup, opened);  
  29.     }  
  30.   
  31.     if (need_lookup) {  
  32.         BUG_ON(dentry->d_inode);  
  33.   
  34.         dentry = lookup_real(dir_inode, dentry, nd->flags);  
  35.         if (IS_ERR(dentry))  
  36.             return PTR_ERR(dentry);  
  37.     }  
  38.   
  39.     /* Negative dentry, just create the file */  
  40.     if (!dentry->d_inode && (op->open_flag & O_CREAT)) {  
  41.         umode_t mode = op->mode;  
  42.         /* 
  43.         int current_umask(void) 
  44.         { 
  45.             return current->fs->umask; 
  46.         } 
  47.         VFS使用umask,此处mode=mode&022,即非文件所属用户不能写 
  48.         current->fs->umask是何时设置的:1 用户可通过umask()系统调用设置; 
  49.         2 进程创建时会copy_fs_struct()该函数继承了父进程中的umask;其实都是继承于 INIT_TASK->init_fs.umask=022 
  50.         所以open时即使mode权限为0777,在此也会改为0755;注意最开始build_open_flags也可能根据O_CREATE标志修改mode 
  51.         */        
  52.         if (!IS_POSIXACL(dir->d_inode))  
  53.             mode &= ~current_umask();  
  54.         if (!got_write) {  
  55.             error = -EROFS;  
  56.             goto out_dput;  
  57.         }  
  58.         *opened |= FILE_CREATED;  
  59.         error = security_path_mknod(&nd->path, dentry, mode, 0);  
  60.         if (error)  
  61.             goto out_dput;  
  62.         /* 
  63.         注意:此时dir对应的目录项为文件所在目录对应的目录项: 
  64.         例如文件:/mnt/testdir/testfile 
  65.                 1>dir->d_iname=testdir 
  66.         2>dentry对应的目录项为文件对应的目录项dentry->d_iname=testfile 
  67.         vfs_create中又修改了mode=mode|S_IFREG所以open时既使mode权限为0777,在此也会改为100755 
  68.         注意:对于新建文件,vfs_create->may_create中会针对文件所在目录testdir的目录项对应的inode,即dentry->d_inode进行写权限和执行权限检查,即: 
  69.         may_create->inode_permission(inode,MAY_WRITE|MAY_EXEC);可以参考link_path_walk->may_lookup和may_open中介绍。 
  70.         */  
  71.         error = vfs_create(dir->d_inode, dentry, mode,  
  72.                    nd->flags & LOOKUP_EXCL);  
  73.         if (error)  
  74.             goto out_dput;  
  75.     }  
  76. out_no_open:  
  77.     path->dentry = dentry;  
  78.     path->mnt = nd->path.mnt;  
  79.     return 1;  
  80.   
  81. out_dput:  
  82.     dput(dentry);  
  83.     return error;  
  84. }  

do_last->lookup_open->lookup_dcache->d_alloc->__d_alloc() 申请新建文件对应的目录项dentry:

[cpp] view plain  copy

 

  1. struct dentry *__d_alloc(struct super_block *sb, const struct qstr *name)  
  2. {  
  3.     struct dentry *dentry;  
  4.     char *dname;  
  5.     /* 
  6.     作用:申请新建文件对应的dentry 
  7.     举例:open("/mnt/testdir/testfile",flags,mode); 
  8.     此时申请的dentry->d_iname=testfile 
  9.     dentry->d_inode初始化为NULL,后面vfs_create中申请inode 
  10.     */  
  11.     dentry = kmem_cache_alloc(dentry_cache, GFP_KERNEL);  
  12.     if (!dentry)  
  13.         return NULL;  
  14.   
  15.     dentry->d_iname[DNAME_INLINE_LEN-1] = 0;  
  16.     if (name->len > DNAME_INLINE_LEN-1) {  
  17.         size_t size = offsetof(struct external_name, name[1]);  
  18.         struct external_name *p = kmalloc(size + name->len, GFP_KERNEL);  
  19.         if (!p) {  
  20.             kmem_cache_free(dentry_cache, dentry);   
  21.             return NULL;  
  22.         }  
  23.         atomic_set(&p->u.count, 1);  
  24.         dname = p->name;  
  25.     } else  {  
  26.         dname = dentry->d_iname;  
  27.     }     
  28.   
  29.     dentry->d_name.len = name->len;  
  30.     dentry->d_name.hash = name->hash;  
  31.     memcpy(dname, name->name, name->len);  
  32.     dname[name->len] = 0;  
  33.   
  34.     /* Make sure we always see the terminating NUL character */  
  35.     smp_wmb();  
  36.     dentry->d_name.name = dname;  
  37.   
  38.     dentry->d_lockref.count = 1;  
  39.     dentry->d_flags = 0;  
  40.     spin_lock_init(&dentry->d_lock);  
  41.     seqcount_init(&dentry->d_seq);  
  42.     dentry->d_inode = NULL;  
  43.     dentry->d_parent = dentry;  
  44.     dentry->d_sb = sb;  
  45.     dentry->d_op = NULL;  
  46.     dentry->d_fsdata = NULL;  
  47.     INIT_HLIST_BL_NODE(&dentry->d_hash);  
  48.     INIT_LIST_HEAD(&dentry->d_lru);  
  49.     INIT_LIST_HEAD(&dentry->d_subdirs);  
  50.     INIT_HLIST_NODE(&dentry->d_u.d_alias);  
  51.     INIT_LIST_HEAD(&dentry->d_child);  
  52.     d_set_d_op(dentry, dentry->d_sb->s_d_op);  
  53.   
  54.     this_cpu_inc(nr_dentry);  
  55.   
  56.     return dentry;  
  57. }  

函数继续执行,因为是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

[cpp] view plain  copy

 

  1. struct inode *ubifs_new_inode(struct ubifs_info *c, const struct inode *dir,  
  2.                   umode_t mode)  
  3. {  
  4.     struct inode *inode;  
  5.     struct ubifs_inode *ui;  
  6.         /* 
  7.      inode申请并初始化: 
  8.      new_inode->new_inode_pseudo->alloc_inode->inode_init_always 
  9.     */  
  10.     inode = new_inode(c->vfs_sb);  
  11.     ui = ubifs_inode(inode);  
  12.     if (!inode)  
  13.         return ERR_PTR(-ENOMEM);  
  14.   
  15.     inode->i_flags |= S_NOCMTIME;  
  16.     /* 初始化inode的权限和用户信息*/  
  17.     inode_init_owner(inode, dir, mode);  
  18.     inode->i_mtime = inode->i_atime = inode->i_ctime =  
  19.              ubifs_current_time(inode);  
  20.     inode->i_mapping->nrpages = 0;  
  21.     /* Disable readahead */  
  22.     inode->i_mapping->backing_dev_info = &c->bdi;  
  23.   
  24.     switch (mode & S_IFMT) {  
  25.     case S_IFREG:  
  26.         inode->i_mapping->a_ops = &ubifs_file_address_operations;  
  27.         inode->i_op = &ubifs_file_inode_operations;  
  28.         inode->i_fop = &ubifs_file_operations;  
  29.         break;  
  30.     case S_IFDIR:  
  31.         inode->i_op  = &ubifs_dir_inode_operations;  
  32.         inode->i_fop = &ubifs_dir_operations;  
  33.         inode->i_size = ui->ui_size = UBIFS_INO_NODE_SZ;  
  34.         break;  
  35.     case S_IFLNK:  
  36.         inode->i_op = &ubifs_symlink_inode_operations;  
  37.         break;  
  38.     case S_IFSOCK:  
  39.     case S_IFIFO:  
  40.     case S_IFBLK:  
  41.     case S_IFCHR:  
  42.         inode->i_op  = &ubifs_file_inode_operations;  
  43.         break;  
  44.     default:  
  45.         BUG();  
  46.     }  
  47.   
  48.     ui->flags = inherit_flags(dir, mode);  
  49.     ubifs_set_inode_flags(inode);  
  50.     if (S_ISREG(mode))  
  51.         ui->compr_type = c->default_compr;  
  52.     else  
  53.         ui->compr_type = UBIFS_COMPR_NONE;  
  54.     ui->synced_i_size = 0;  
  55.   
  56.     spin_lock(&c->cnt_lock);  
  57.     /* Inode number overflow is currently not supported */  
  58.     if (c->highest_inum >= INUM_WARN_WATERMARK) {  
  59.         if (c->highest_inum >= INUM_WATERMARK) {  
  60.             spin_unlock(&c->cnt_lock);  
  61.             ubifs_err("out of inode numbers");  
  62.             make_bad_inode(inode);  
  63.             iput(inode);  
  64.             return ERR_PTR(-EINVAL);  
  65.         }  
  66.         ubifs_warn("running out of inode numbers (current %lu, max %d)",  
  67.                (unsigned long)c->highest_inum, INUM_WATERMARK);  
  68.     }  
  69.   
  70.     inode->i_ino = ++c->highest_inum;  
  71.     ui->creat_sqnum = ++c->max_sqnum;  
  72.     spin_unlock(&c->cnt_lock);  
  73.     return inode;  
  74. }  

初始化文件权限,所属用户,包括:inode->i_mode,inode->i_uid,inode->i_gid:ubifs_new_inode->inode_init_owner:

[cpp] view plain  copy

 

  1. void inode_init_owner(struct inode *inode, const struct inode *dir,  
  2.             umode_t mode)  
  3. {  
  4.     /* 
  5.      初始化 inode->i_uid,inode->i_gid为当前进程的fsuid 
  6.     如:打开文件的进程所属用户为uid=1001,gid=1001 
  7.     */  
  8.     inode->i_uid = current_fsuid();  
  9.     if (dir && dir->i_mode & S_ISGID) {  
  10.         inode->i_gid = dir->i_gid;  
  11.         if (S_ISDIR(mode))  
  12.             mode |= S_ISGID;  
  13.     } else  
  14.         inode->i_gid = current_fsgid();  
  15.     /* 
  16.     初始化inode->i_mode为mode 
  17.     如:open时指定0777权限,则此处为100755 
  18.     */  
  19.     inode->i_mode = mode;  
  20. }  

真正打开文件之前对,文件权限的校验:may_open->inode_permission->generic_permission()->acl_permission_check()

[cpp] view plain  copy

 

  1. static int acl_permission_check(struct inode *inode, int mask)  
  2. {  
  3.     unsigned int mode = inode->i_mode;  
  4.     /* 
  5.     作用:操作文件的进程和当前文件属于同一用户,mode>>6 
  6.     这是因为最高3位表示同一用户访问文件的权限。 
  7.     如ls /mnt/testdir/testfile -l 为0777 那么最高3位0700表示同一用户访问权限。 
  8.     中间的7表示,同组不同用户权限,最低位表示其他用户,即不同组,不同用户的权限 
  9.     所以其他用户时mode不用移位,即if和else两个分支都不走。 
  10.     */  
  11.   
  12.     if (likely(uid_eq(current_fsuid(), inode->i_uid)))  
  13.         mode >>= 6;  
  14.     else {  
  15.         /* 
  16.         操作文件的进程和当前文件不在同一用户,但在同一组。如0777中中间的7,所以mode>>3 
  17.         */  
  18.         if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {  
  19.             int error = check_acl(inode, mask);  
  20.             if (error != -EAGAIN)  
  21.                 return error;  
  22.         }  
  23.   
  24.         if (in_group_p(inode->i_gid))  
  25.             mode >>= 3;  
  26.     }  
  27.   
  28.     /* 
  29.      * If the DACs are ok we don't need any capability check. 
  30.      */  
  31.     /* 
  32.     作用:把文件的打开方式与文件所属用户  具有的文件访问权限inode->i_mode做比较 
  33.     如果testfile文件所属用户、所属组、其他用户都是只读权限,即0444, 
  34.      ls testfile -l :   --r--r--r--; 
  35.     那么任何用户:open("testfile",O_WRONLY)都会打开失败。 
  36.     这是因为flags=O_WRONLY,在build_open_flags中acc_mode=02|MAY_OPEN; 
  37.     inode->i_mode=0444对于任何用户,rwx中r位都是1,即bit2=1; 
  38.     (mask=acc_mode=02)&(~mode=03)&007  !=  0 所以不能访问。 
  39.     */  
  40.     if ((mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)  
  41.         return 0;  
  42.     return -EACCES;  
  43. }  

至此完成了文件访问权限的校验,前面的分析,其实都是为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真正打开文件之前会对文件读写权限进行检查。

打开文件时,系统对文件权限的检查,其实就发生在根据文件所属路径查找文件的过程中。

原文链接:https://www.centoschina.cn/course/intermediate/9456.html
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章