还记得在上一个场景中,build_open_flags里面有一个对标志位O_PATH的判断么?现在我们就来看看这个标志位是干啥的:
【场景二】open(pathname,O_PATH)
这个O_PATH似乎是不常用的,咱们先看看它的使用说明:
【open(2)】 http://man7.org/linux/man-pages/man2/open.2.html
大家可以重点看看字体加粗的部分,大致意思就是使用O_PATH将不会真正打开一个文件,而只是准备好该文件的文件描述符,而且如果使用该标志位的话系统会忽略大部分其他的标志位。特别是如果配合使用O_NOFOLLOW,那么遇到符号链接的时候将会返回这个符号链接本身的文件描述符,而非符号链接所指的对象。
咱们还是先看看build_open_flags针对O_PATH做了什么手脚:
【fs / open.c】 sys_open > do_sys_open > build_open_flags
首先是872行,这里进行了一个“与”操作,这就将除了O_DIRECTORY和O_NOFOLLOW的其他标志位全部清零了,这就忽略了其他的标志位。在开的说明中还有一个标志位O_CLOEXEC也受到O_PATH的保护,但是这个标志位不允许在用户空间直接设置,所以build_open_flags一开始就把它干掉了。另外O_PATH本身连一个真正的打开操作都不是就跟别提创建了,所以mode当然要置零了(873)。既然不会打开文件那么也就和LOOKUP_OPEN无缘了(891)。接下来就是处理一下受O_PATH保护两个标志位。注意,如果没有设置O_NOFOLLOW的话遇到符号链接是需要跟踪到底的(902)。其实就算设置了O_NOFOLLOW,我们还会看到在do_last里还有一次补救的机会,那就是路径名以“/”结尾的话也会跟踪符号链接到底的。
【fs / namei.c 】 sys_open > do_sys_open > do_filp_open > path_openat> do_last
看,两百多行的函数让我们连消带打就剩这个点了,所以说小的函数才是好函数嘛。先看2902行的symlink_ok,这个变量名很形象,它为真的意思就是“如果最终目标是一个符号链接也OK啦”,如果为假的话就需要跟随这个符号链接。我们来看看什么情况下符号链接是OK的?首先必须是O_PATH,也就是我们假设的场景;同时还需要没有设置LOOKUP_FOLLOW(2901)。在build_open_flags我们已经见过了一次设置LOOKUP_FOLLOW的地方,这里就是前面所说的补救的地方(2900)。也就是说只要路径名最后一个字符+ 1不为零就一定是“/”(为什么?不明白的可以回头看看link_path_walk的代码),那就表示如果这个最终目标是符号链接的话就要跟随。
接下来lookup_open就不用说了吧,当它返回的时候路径会站上最终目标,nd则原地不动,它在等待在观望:如果path站上 不是符号链接或者即使是符号链接但是“即使是符号链接也OK啦”(3003)就会跟着路站上最终目标,然后在finish_open中完成打开。
finish_open主要是调用do_dentry_open,我们进去看看:
【FS /open.c】 sys_open > do_sys_open > do_filp_open > path_openat> do_last
为啥O_PATH不会真正打开一个文件,看到这里大家就明白了吧,这里的代码很简单,一切尽在不言中了。当从finish_open返回时,文件结构体几乎就是空的,只有文件。 f_path成员指向了这个文件,就连f_op都是空的。这或许就是O_PATH使用说明中一开始阐述的那两个目的具体表现吧。
好像这个O_PATH情景比上一个O_RDONLY还要简单,那我们就再假设一个情景。
【场景二】open(pathname,O_PATH)
这个O_PATH似乎是不常用的,咱们先看看它的使用说明:
【open(2)】 http://man7.org/linux/man-pages/man2/open.2.html
- O_PATH(自Linux 2.6.39起)
- 获取可用于两个目的的文件描述符:指示文件系统树中的位置并执行纯粹在文件描述符级别的操作。文件本身未打开,其他文件操作(例如,read(2),write(2),fchmod(2),fchown(2),fgetxattr(2),mmap(2))失败,错误为EBADF。
- 可以对生成的文件描述符执行以下操作:
-
- * close(2); fchdir(2)(自Linux 3.5起); fstat(2)(自Linux 3.6起)。
- *复制文件描述符(dup(2),fcntl(2)F_DUPFD等)。
- *获取和设置文件描述符标志(fcntl(2)F_GETFD和F_SETFD)。
- *使用fcntl(2)F_GETFL操作检索打开文件状态标志:返回的标志将包括位O_PATH。
- *将文件描述符作为openat(2)的dirfd参数和另一个“* at()”系统调用传递。这包括linkat(2)与AT_EMPTY_PATH(或通过使用AT_SYMLINK_FOLLOW的procfs),即使该文件不是目录。
- *通过UNIX域套接字将文件描述符传递给另一个进程(请参阅unix(7)中的SCM_RIGHTS)。
- 在flags中指定O_PATH时,将忽略O_CLOEXEC,O_DIRECTORY和O_NOFOLLOW以外的标志位。
- 如果路径是一个符号链接和O_NOFOLLOW标志也s ^ pecified,则调用返回一个文件描述符指以符号链接。此文件描述符可用作调用fchownat(2),fstatat(2),linkat(2)和readlinkat(2)的dirfd参数,其中空路径名可使调用在符号链接上运行。
咱们还是先看看build_open_flags针对O_PATH做了什么手脚:
【fs / open.c】 sys_open > do_sys_open > build_open_flags
点击(此处)折叠或打开
- static inline int build_open_flags (int flags , umode_t mode , struct open_flags * op )
- {
...
- } else if ( flags & O_PATH ) {
- / *
- * 如果我们在打开标志中有O_PATH 。 然后我们
- *除了以下标志集之外,不能有任何其他内容
- * /
- flags &= O_DIRECTORY | O_NOFOLLOW | O_PATH ;
- acc_mode = 0 ;
- } else {
...
- op - > intent = flags & O_PATH ?0 : LOOKUP_OPEN ;
...
- if ( flags & O_DIRECTORY )
- lookup_flags | = LOOKUP_DIRECTORY ;
- if (!( flags & O_NOFOLLOW ))
- lookup_flags | = LOOKUP_FOLLOW ;
- op - > lookup_flags = lookup_flags ;
- 返回0 ;
- }
【fs / namei.c 】 sys_open > do_sys_open > do_filp_open > path_openat> do_last
点击(此处)折叠或打开
- static int do_last ( struct nameidata * nd , struct path * path ,
- struct file * file , const struct open_flags * op ,
- int * opens , struct filename * name )
- {
...
- if (!( open_flag & O_CREAT )) {
- 如果 ( ND - >最后。名[ ND - >最后。LEN ] )
- nd - > flags | = LOOKUP_FOLLOW | LOOKUP_DIRECTORY ;
- 如果 ( open_flag & O_PATH && !( ND - >标志及 LOOKUP_FOLLOW ))
- symlink_ok = true ;
...
- }
...
- error = lookup_open ( nd , path , file , op , got_write , opened );
...
- if ( should_follow_link ( path - > dentry , ! symlink_ok )) {
...
- 返回1 ;
- }
...
- 误差 = finish_open (文件,第二- >路径。目录项, NULL ,打开);
...
- }
接下来lookup_open就不用说了吧,当它返回的时候路径会站上最终目标,nd则原地不动,它在等待在观望:如果path站上 不是符号链接或者即使是符号链接但是“即使是符号链接也OK啦”(3003)就会跟着路站上最终目标,然后在finish_open中完成打开。
finish_open主要是调用do_dentry_open,我们进去看看:
【FS /open.c】 sys_open > do_sys_open > do_filp_open > path_openat> do_last
点击(此处)折叠或打开
- static int do_dentry_open ( struct file * f ,
- int (* open )( struct inode * , struct file * ),
- const struct cred * cred )
- {
...
- if (不太可能( f - > f_flags & O_PATH )) {
- f - > f_mode = FMODE_PATH ;
- f - > f_op = & empty_fops ;
- 返回0 ;
- }
...
- }
好像这个O_PATH情景比上一个O_RDONLY还要简单,那我们就再假设一个情景。