歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux基礎 >> Linux技術 >> 走馬觀花: Linux 系統調用 open 七日游(六)

走馬觀花: Linux 系統調用 open 七日游(六)

日期:2017/3/3 12:28:42   编辑:Linux技術

還記得在上一個場景中,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 (since Linux 2.6.39)

Obtain a file descriptor that can be used for two purposes: to indicate a location in thefilesystem tree and to perform operations that act purely at the file descriptor level.

The file itself is not opened, and other file operations (e.g., read(2), write(2), fchmod(2), fchown(2), fgetxattr(2), mmap(2)) fail with the error EBADF.The following operations can be performed on the resulting file descriptor:

* close(2); fchdir(2) (since Linux 3.5); fstat(2) (since Linux 3.6).

* Duplicating the file descriptor (dup(2), fcntl(2) F_DUPFD, etc.).

* Getting and setting file descriptor flags (fcntl(2) F_GETFD and F_SETFD).

* Retrieving open file status flags using the fcntl(2) F_GETFL operation: the returned flags will include the bit O_PATH.

* Passing the file descriptor as the dirfd argument of openat(2) and the other "*at()" system calls. This includes linkat(2) with AT_EMPTY_PATH (or via procfs using AT_SYMLINK_FOLLOW) even if the file is not a directory.

* Passing the file descriptor to another process via a UNIX domain socket (see SCM_RIGHTS in unix(7)).

When O_PATH is specified in flags, flag bits other than O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW are ignored.If pathname is a symbolic link and the O_NOFOLLOW flag is also

specified, then the call returns a file descriptor referringto the symbolic link. This file descriptor can be used as the dirfd argument in calls to fchownat(2), fstatat(2), linkat(2), and readlinkat(2) with an empty pathname to have the calls operate on the symbolic

link.

大家可以重點看看字體加粗的部分,大致意思就是使用 O_PATH 將不會真正打開一個文件,而只是准備好該文件的文件描述符,而且如果使用該標志位的話系統會忽略大部分其他的標志位。特別是如果配合使用 O_NOFOLLOW,那麼遇到符號鏈接的時候將會返回這個符號鏈接本身的文件描述符,而非符號鏈接所指的對象。

咱們還是先看看 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) {

/*

* If we have O_PATH

in the open flag.

Then we

* cannot have anything other than the below

set of flags

*/

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;

return 0;

}

首先是 872 行,這裡進行了一個“與”操作,這就將除了 O_DIRECTORY 和 O_NOFOLLOW 的其他標志位全部清零了,這就忽略了其他的標志位。在 open 的說明中還有一個標志位 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

點擊(此處)折疊或打開

static int do_last(struct nameidata

*nd, struct path

*path,

struct file *file,

const struct open_flags

*op,

int *opened, struct filename

*name)

{

...

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_open(nd, path, file, op,

got_write, opened);

...

if (should_follow_link(path->dentry,

!symlink_ok))

{

...

return 1;

}

...

error = finish_open(file, nd->path.dentry,

NULL, opened);

...

}

看,兩百多行的函數讓我們連消帶打就剩這麼點了,所以說小的函數才是好函數嘛。先看 2902 行的 symlink_ok,這個變量名很形象,它為 true 的意思就是“如果最終目標是一個符號鏈接也 OK 啦”,如果為 false 的話就需要跟隨這個符號鏈接。我們來看看什麼情況下符號鏈接是 OK 的?首先必須是 O_PATH,也就是我們假設的場景;同時還需要沒有設置 LOOKUP_FOLLOW(2901)。在 build_open_flags 我們已經見過了一次設置 LOOKUP_FOLLOW 的地方,這裡就是前面所說的補救的地方(2900)。也就是說只要路徑名最後一個字符

+ 1 不為零就一定是“/”(為什麼?不明白的可以回頭看看 link_path_walk 的代碼),那就表示如果這個最終目標是符號鏈接的話就要跟隨。

接下來 lookup_open 就不用說了吧,當它返回的時候 path 會站上最終目標,nd 則原地不動,它在等待在觀望:如果 path 站上的不是符號鏈接或者即使是符號鏈接但是“即使是符號鏈接也 OK 啦”(3003)就會跟著 path 站上最終目標,然後在 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 (unlikely(f->f_flags

& O_PATH))

{

f->f_mode

= FMODE_PATH;

f->f_op

= &empty_fops;

return 0;

}

...

}

為啥 O_PATH 不會真正打開一個文件,看到這裡大家就明白了吧,這裡的代碼很簡單,一切盡在不言中了。當從 finish_open 返回時,file 結構體幾乎就是空的,只有 file.f_path 成員指向了這個文件,就連 f_op 都是空的。這或許就是 O_PATH 使用說明中一開始闡述的那兩個目的具體表現吧。

好像這個 O_PATH 情景比上一個 O_RDONLY 還要簡單,那我們就再假設一個情景。

Copyright © Linux教程網 All Rights Reserved