Linux文件系统调用open 七日游 (六)

还记得在上一个场景中,build_open_flags里面有一个对标志位O_PATH的判断么?现在我们就来看看这个标志位是干啥的:
【场景二】open(pathname,O_PATH)
    这个O_PATH似乎是不常用的,咱们先看看它的使用说明:
【open(2)】 http://man7.org/linux/man-pages/man2/open.2.html
  1. O_PATH(自Linux 2.6.39起)
  2. 获取可用于两个目的的文件描述符:指示文件系统树中的位置执行纯粹在文件描述符级别的操作文件本身未打开,其他文件操作(例如,read(2),write(2),fchmod(2),fchown(2),fgetxattr(2),mmap(2))失败,错误为EBADF。

  3. 可以对生成的文件描述符执行以下操作:
  4.  
  5. * close(2); fchdir(2)(自Linux 3.5起); fstat(2)(自Linux 3.6起)。

  6. *复制文件描述符(dup(2),fcntl(2)F_DUPFD等)。

  7. *获取和设置文件描述符标志(fcntl(2)F_GETFD和F_SETFD)。

  8. *使用fcntl(2)F_GETFL操作检索打开文件状态标志:返回的标志将包括位O_PATH。

  9. *将文件描述符作为openat(2)的dirfd参数和另一个“* at()”系统调用传递。这包括linkat(2)与AT_EMPTY_PATH(或通过使用AT_SYMLINK_FOLLOW的procfs),即使该文件不是目录。

  10. *通过UNIX域套接字将文件描述符传递给另一个进程(请参阅unix(7)中的SCM_RIGHTS)。

  11. 在flags中指定O_PATH时,将忽略O_CLOEXEC,O_DIRECTORY和O_NOFOLLOW以外的标志位。

  12. 如果路径是一个符号链接和O_NOFOLLOW标志也s ^ pecified,则调用返回一个文件描述符指以符号链接。此文件描述符可用作调用fchownat(2),fstatat(2),linkat(2)和readlinkat(2)的dirfd参数,其中空路径名可使调用在符号链接上运行。
    大家可以重点看看字体加粗的部分,大致意思就是使用O_PATH将不会真正打开一个文件,而只是准备好该文件的文件描述符,而且如果使用该标志位的话系统会忽略大部分其他的标志位。特别是如果配合使用O_NOFOLLOW,那么遇到符号链接的时候将会返回这个符号链接本身的文件描述符,而非符号链接所指的对象。
    咱们还是先看看build_open_flags针对O_PATH做了什么手脚:
【fs / open.c】 sys_open > do_sys_open > build_open_flags

点击(此处)折叠或打开

  1. static inline int build_open_flags int flags  umode_t mode  struct open_flags * op 
  2. {

  ...

  1.     else if  flags  O_PATH  {
  2.         *
  3.          如果我们打开标志中有O_PATH  然后我们
  4.          *除了以下标志之外,不能有任何其他内容
  5.          /
  6.         flags = O_DIRECTORY O_NOFOLLOW O_PATH ;
  7.         acc_mode = 0 ;
  8.     else {

  ...

  1.     op > intent = flags  O_PATH  LOOKUP_OPEN ;

  ...

  1.     if  flags  O_DIRECTORY 
  2.         lookup_flags = LOOKUP_DIRECTORY ;
  3.     if  flags  O_NOFOLLOW 
  4.         lookup_flags = LOOKUP_FOLLOW ;
  5.     op > lookup_flags = lookup_flags ;
  6.     返回0 ;
  7. }
    首先是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

点击(此处)折叠或打开

  1. static int do_last  struct nameidata * nd  struct path * path 
  2.          struct file * file  const struct open_flags * op 
  3.          int * opens  struct filename * name 
  4. {

  ...

  1.     if  open_flag  O_CREAT  {
  2.         如果  ND >最后[ ND >最后LEN 
  3.             nd > flags = LOOKUP_FOLLOW LOOKUP_DIRECTORY ;
  4.         如果  open_flag  O_PATH   ND >标志 LOOKUP_FOLLOW 
  5.             symlink_ok true ;

  ...

  1.     }

  ...

  1.     error = lookup_open  nd  path  file  op  got_write  opened ;

  ...

  1.     if  should_follow_link  path > dentry   symlink_ok  {

  ...

  1.         返回1 ;
  2.     }

  ...

  1.     误差 = finish_open 文件第二>路径目录项 NULL 打开;

  ...

  1. }
    看,两百多行的函数让我们连消带打就剩这个点了,所以说小的函数才是好函数嘛。先看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

点击(此处)折叠或打开

  1. static int do_dentry_open  struct file * f 
  2.              int * open  struct inode  struct file 
  3.              const struct cred * cred 
  4. {

  ...

  1.     if 不太可能 f > f_flags  O_PATH  {
  2.         f > f_mode = FMODE_PATH ;
  3.         f > f_op  empty_fops ;
  4.         返回0 ;
  5.     }

  ...

  1. }
    为啥O_PATH不会真正打开一个文件,看到这里大家就明白了吧,这里的代码很简单,一切尽在不言中了。当从finish_open返回时,文件结构体几乎就是空的,只有文件。 f_path成员指向了这个文件,就连f_op都是空的。这或许就是O_PATH使用说明中一开始阐述的那两个目的具体表现吧。
    好像这个O_PATH情景比上一个O_RDONLY还要简单,那我们就再假设一个情景。
转自:http://blog.chinaunix.net/uid-20522771-id-4426763.html

猜你喜欢

转载自blog.csdn.net/qq_22613757/article/details/80886194