1. 程式人生 > >Linux檔案系統呼叫open 七日遊 (六)

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