linux查看link 路径,link_path_walk()路径名查找
link_path_walk()路徑名查找
link_path_walk()函數(shù)。它接收的參數(shù)為要解析的路徑名指針name和擁有目錄項(xiàng)信息和安裝文件系統(tǒng)信息的nameidata數(shù)據(jù)結(jié)構(gòu)的地址nd,此時(shí)nd的path字段存放的是查找的路徑名的基目錄的路徑。其定義如下:
---------------------------------------------------------------------
fs/namei.c
/*
* Name resolution.
* This is the basic name resolution function,
turning a pathname into
* the final dentry. We expect 'base' to be
positive and a directory.
*
* Returns 0 and nd will have valid dentry and
mnt on success.
* Returns error and drops reference to input
namei data on failure.
*/
814 static int
link_path_walk(const char *name, struct nameidata *nd)
815 {
816struct path next;
817struct inode *inode;
818int err;
819unsigned int lookup_flags = nd->flags;
820
821while (*name=='/')
822name++;
823if (!*name)
824goto return_reval;
825
826inode =
nd->path.dentry->d_inode;
827if (nd->depth)
828lookup_flags = LOOKUP_FOLLOW |
(nd->flags & LOOKUP_CONTINUE);
829
830/* At this point we know we have a
real path component. */
831for(;;) {
832unsigned long hash;
833struct qstr this;
834unsigned int c;
835
836nd->flags |=
LOOKUP_CONTINUE;
837err = exec_permission(inode);
838if (err)
839break;
840
841this.name = name;
842c= *(const unsigned char *)name;
843
844hash = init_name_hash();
845do {
846name++;
847hash =
partial_name_hash(c, hash);
848c= *(const unsigned char *)name;
849} while (c && (c !=
'/'));
850this.len = name - (const char
*) this.name;
851this.hash =
end_name_hash(hash);
852
853/* remove trailing slashes? */
854if (!c)
855goto last_component;
856while (*++name == '/');
857if (!*name)
858goto
last_with_slashes;
859
860/*
861* "." and ".."
are special - ".." especially so because it has
862* to be able to know about
the current root directory and
863* parent relationships.
864*/
865if (this.name[0] == '.')
switch (this.len) {
866default:
867break;
868case 2:
869if
(this.name[1] != '.')
870break;
871follow_dotdot(nd);
872inode =
nd->path.dentry->d_inode;
873/* fallthrough
*/
874case 1:
875continue;
876}
877/* This does the actual
lookups.. */
878err = do_lookup(nd, &this,
&next);
879if (err)
880break;
881
882err = -ENOENT;
883inode = next.dentry->d_inode;
884if (!inode)
885goto out_dput;
886
887if
(inode->i_op->follow_link) {
888err =
do_follow_link(&next, nd);
889if (err)
890goto return_err;
891err = -ENOENT;
892inode =
nd->path.dentry->d_inode;
893if (!inode)
894break;
895} else
896path_to_nameidata(&next, nd);
897err = -ENOTDIR;
898if
(!inode->i_op->lookup)
899break;
900continue;
901/* here ends the main loop */
902
903 last_with_slashes:
904lookup_flags |= LOOKUP_FOLLOW
| LOOKUP_DIRECTORY;
905 last_component:
906/* Clear LOOKUP_CONTINUE iff
it was previously unset */
907nd->flags &=
lookup_flags | ~LOOKUP_CONTINUE;
908if (lookup_flags &
LOOKUP_PARENT)
909goto lookup_parent;
910if (this.name[0] == '.')
switch (this.len) {
911default:
912break;
913case 2:
914if
(this.name[1] != '.')
915break;
916follow_dotdot(nd);
917inode =
nd->path.dentry->d_inode;
918/* fallthrough
*/
919case 1:
920goto
return_reval;
921}
922err = do_lookup(nd, &this,
&next);
923if (err)
924break;
925inode =
next.dentry->d_inode;
926if (follow_on_final(inode,
lookup_flags)) {
927err =
do_follow_link(&next, nd);
928if (err)
929goto
return_err;
930inode =
nd->path.dentry->d_inode;
931} else
932path_to_nameidata(&next, nd);
933err = -ENOENT;
934if (!inode)
935break;
936if (lookup_flags &
LOOKUP_DIRECTORY) {
937err = -ENOTDIR;
938if
(!inode->i_op->lookup)
939break;
940}
941goto return_base;
942 lookup_parent:
943nd->last = this;
944nd->last_type = LAST_NORM;
945if (this.name[0] != '.')
946goto return_base;
947if (this.len == 1)
948nd->last_type =
LAST_DOT;
949else if (this.len == 2
&& this.name[1] == '.')
950nd->last_type =
LAST_DOTDOT;
951else
952goto return_base;
953 return_reval:
954/*
955* We bypassed the ordinary
revalidation routines.
956* We may need to check the cached dentry for
staleness.
957*/
958if (nd->path.dentry
&& nd->path.dentry->d_sb &&
959(nd->path.dentry->d_sb->s_type->fs_flags &
FS_REVAL_DOT)) {
960err = -ESTALE;
961/* Note: we do not
d_invalidate() */
962if
(!nd->path.dentry->d_op->d_revalidate(
963nd->path.dentry, nd))
964break;
965}
966 return_base:
967return 0;
968 out_dput:
969path_put_conditional(&next, nd);
970break;
971}
972path_put(&nd->path);
973 return_err:
974return err;
975 }
---------------------------------------------------------------------
這是一個(gè)非常長(zhǎng)的函數(shù)。link_path_walk()執(zhí)行下列步驟:
1、用nd->flags初始化lookup_flags局部變量(819行)。
2、跳過(guò)路徑名第一個(gè)分量前的任何斜杠(/)(821行)。
3、如果剩余的路徑名為空,則返回0。沒(méi)有改變nameidata結(jié)構(gòu)數(shù)據(jù),nd->path中存放將要查找的路徑名的基路徑(823行)。
4、把將要查找的路徑名的基路徑的inode地址存放在局部變量inode中,即初始化最近一個(gè)所解析分量的索引節(jié)點(diǎn)對(duì)象的地址為將要查找的路徑名的基路徑的inode地址(826行)。
5、如果nd描述符中的depth字段(即符號(hào)鏈接嵌套的當(dāng)前級(jí)別)的值為正(大于0),則把lookup_flags局部變量置為L(zhǎng)OOKUP_FOLLOW標(biāo)志(這個(gè)跟符號(hào)鏈接查找相關(guān))(827行)。
6、執(zhí)行一個(gè)循環(huán),把name參數(shù)中傳遞的路徑名分解為分量(中間的“/”被當(dāng)做文件名分隔符對(duì)待)(831行);對(duì)于每個(gè)找到的分量,該函數(shù):
a.設(shè)置lookup_flags局部變量置的LOOKUP_CONTINUE標(biāo)志(836行)。
b.執(zhí)行exec_permission(inode)函數(shù)檢查存放到索引節(jié)點(diǎn)中的最近那個(gè)所解析分量的許可權(quán)是否允許執(zhí)行(在Unix中,只有目錄是可執(zhí)行的,它才可以被遍歷)(837行)。exec_permission()函數(shù)定義如下:
---------------------------------------------------------------------
fs/namei.c
463
static int exec_permission(struct inode *inode)
464
{
465int ret;
466
467if (inode->i_op->permission) {
468ret =
inode->i_op->permission(inode, MAY_EXEC);
469if (!ret)
470goto ok;
471return ret;
472}
473ret = acl_permission_check(inode,
MAY_EXEC, inode->i_op->check_acl);
474if (!ret)
475goto ok;
476
477if (capable(CAP_DAC_OVERRIDE) ||
capable(CAP_DAC_READ_SEARCH))
478goto ok;
479
480return ret;
481
ok:
482return
security_inode_permission(inode, MAY_EXEC);
483
}
---------------------------------------------------------------------
如果文件系統(tǒng)提供了inode->i_op->permission方法,則exec_permission()調(diào)用該例程執(zhí)行EXEC權(quán)限檢查,如果不允許執(zhí)行則返回錯(cuò)誤碼,若允許,則調(diào)用security_inode_permission(),使用LSM的security_ops->inode_permission()方法來(lái)執(zhí)行權(quán)限檢查,并返回該方法的返回值。
inode->i_op->permission方法不存在,則調(diào)用acl_permission_check()執(zhí)行基本的POSIX ACL權(quán)限檢查,若通過(guò)檢查,則調(diào)用security_inode_permission(),使用LSM的security_ops->inode_permission()方法來(lái)執(zhí)行權(quán)限檢查,并返回該方法的返回值。
若不通過(guò),則執(zhí)行權(quán)能檢查,若同樣不允許,則返回錯(cuò)誤碼。若允許,則調(diào)用security_inode_permission(),使用LSM的security_ops->inode_permission()方法來(lái)執(zhí)行權(quán)限檢查,并返回該方法的返回值。
如果最近所解析分量不允許執(zhí)行,那么link_path_walk()跳出循環(huán)并返回一個(gè)錯(cuò)誤碼。
c.考慮要解析的下一個(gè)分量(841行-851行)。從它的名字,函數(shù)為目錄項(xiàng)高速緩存散列表計(jì)算一個(gè)32位的散列值。
注意,這里用到了目錄項(xiàng)名字?jǐn)?shù)據(jù)結(jié)構(gòu)qstr:
---------------------------------------------------------------------
include/linux/dcache.h
33
struct qstr {
34unsigned int hash;
35unsigned int len;
36const unsigned char *name;
37
};
---------------------------------------------------------------------
當(dāng)前目錄分量存放到了指向qstr結(jié)構(gòu)的this局部變量中。
散列表的32位散列值如下計(jì)算:
---------------------------------------------------------------------
include/linux/dcache.h
50
#define init_name_hash()0
51
52
/* partial hash update function. Assume roughly 4 bits per character */
53
static inline unsigned long
54
partial_name_hash(unsigned long c, unsigned long prevhash)
55
{
56return (prevhash + (c << 4) + (c
>> 4)) * 11;
57
}
63
static inline unsigned long end_name_hash(unsigned long hash)
64
{
65return (unsigned int) hash;
66
}
---------------------------------------------------------------------
d.如果要解析的分量是原路徑名中的最后一個(gè)分量,則跳到第last_component標(biāo)號(hào)處去執(zhí)行。后面“l(fā)ink_path_walk()對(duì)于路徑名最后一個(gè)分量的處理”部分會(huì)有更詳細(xì)的說(shuō)明。
e.如果“/”終止了要解析的分量名,則跳過(guò)“/”之后的任何尾部“/”。多么強(qiáng)大的處理路徑名的能力啊,也就是說(shuō)路徑名中兩個(gè)目錄之間是可以插入多個(gè)“/”。這一步為解析下一個(gè)分量做準(zhǔn)備。而如果在一連串的“/”之后沒(méi)有內(nèi)容了,則跳轉(zhuǎn)到標(biāo)號(hào)last_with_slashes處執(zhí)行。這是最后一個(gè)分量的特殊情況,也就是它必須一個(gè)目錄。同樣在后面“l(fā)ink_path_walk()對(duì)于路徑名最后一個(gè)分量的處理”部分說(shuō)明。
f.如果分量名是一個(gè)“.”(單個(gè)圓點(diǎn)),則繼續(xù)下一個(gè)分量(“.”指的是當(dāng)前目錄,因此,這個(gè)點(diǎn)在目錄內(nèi)沒(méi)有什么效果)(874行)。
g.如果分量名是“..”(兩個(gè)圓點(diǎn)),則嘗試回到父目錄(871行)。這里面有個(gè)重要的follow_dotdot(nd)函數(shù):
---------------------------------------------------------------------
fs/namei.c
670
static __always_inline void follow_dotdot(struct nameidata *nd)
671
{
672set_root(nd);
673
674while(1) {
675struct dentry *old =
nd->path.dentry;
676
677if (nd->path.dentry ==
nd->root.dentry &&
678nd->path.mnt ==
nd->root.mnt) {
679break;
680}
681if (nd->path.dentry !=
nd->path.mnt->mnt_root) {
682/* rare case of legitimate
dget_parent()... */
683nd->path.dentry =
dget_parent(nd->path.dentry);
684dput(old);
685break;
686}
687if (!follow_up(&nd->path))
688break;
689}
690follow_mount(&nd->path);
691
}
---------------------------------------------------------------------
(1)、首先,設(shè)置nd的root字段為當(dāng)前進(jìn)程的根路徑。
(2)、如果最近解析的目錄是進(jìn)程的根目錄(nd->path.dentry等于nd->root.dentry,而nd->path.mnt等于nd->root.mnt),那么再向上追蹤是不允許的:在最近解析的分量上調(diào)用follow_mount()(見(jiàn)下面),繼續(xù)下一個(gè)分量。
(3)、如果最近解析的目錄不是nd->path.mnt文件系統(tǒng)的根目錄(nd->path.dentry不等于nd->path.mnt->mnt_root,如果當(dāng)前節(jié)點(diǎn)dentry不等于當(dāng)前節(jié)點(diǎn)vfsmount對(duì)象的根設(shè)備的dentry,說(shuō)明當(dāng)前節(jié)點(diǎn)不是做為根節(jié)點(diǎn)被mount到其它設(shè)備上去的。在這里再來(lái)看vfsmount對(duì)象的mnt_mountpoint字段,它指向它掛載的目錄的目錄項(xiàng),也就是原來(lái)的目錄文件的信息),那么必須回到父目錄:把nd->path.dentry置為其父目錄的目錄項(xiàng),其實(shí)也就是nd-> path.dentry->
d_parent在父目錄上調(diào)用follow_mount(&nd->path)(見(jiàn)下面),繼續(xù)下一個(gè)分量。
(4)、如果最近解析的目錄是nd->mnt文件系統(tǒng)的根目錄,則調(diào)用函數(shù)follow_up(&nd->path)來(lái)處理,這個(gè)函數(shù)定義如下:
---------------------------------------------------------------------
fs/namei.c
599
int follow_up(struct path *path)
600
{
601struct vfsmount *parent;
602struct dentry *mountpoint;
603spin_lock(&vfsmount_lock);
604parent = path->mnt->mnt_parent;
605if (parent == path->mnt) {
606spin_unlock(&vfsmount_lock);
607return 0;
608}
609mntget(parent);
610mountpoint =
dget(path->mnt->mnt_mountpoint);
611spin_unlock(&vfsmount_lock);
612dput(path->dentry);
613path->dentry = mountpoint;
614mntput(path->mnt);
615path->mnt = parent;
616return 1;
617
}
---------------------------------------------------------------------
如果這個(gè)文件系統(tǒng)沒(méi)有被安裝在其他文件系統(tǒng)之上(path->mnt->mnt_parent等于path->mnt),那么
path->mnt文件系統(tǒng)通常就是進(jìn)程命名空間的根文件系統(tǒng):在這種情況下,再向上追蹤是不可能的,因此在最近解析的分量上調(diào)用follow_mount()(參見(jiàn)下面),繼續(xù)下一個(gè)分量。(這種情況是不應(yīng)該出現(xiàn)的,或者說(shuō)這種情況應(yīng)該是在follow_dotdot的步驟(2)中就已經(jīng)檢測(cè)出來(lái)的)。
如果這個(gè)文件系統(tǒng)被安裝在其他文件系統(tǒng)之上,那么就需要文件系統(tǒng)交換。因此,把path->dentry置為path->mnt->mnt_mountpoint,且把path->mnt置為 path->mnt->mnt_parent,然后重新開(kāi)始第6g步(幾個(gè)文件系統(tǒng)可以掛載在同一個(gè)掛載點(diǎn)上,在掛載的時(shí)候,原來(lái)的那個(gè)目錄文件的vfsmount對(duì)象和目錄項(xiàng)信息被保存在新的vfsmount對(duì)象的mnt_parent和mnt_mountpoint字段中)。
最后來(lái)看follow_mount(), follow_mount()定義如下:
---------------------------------------------------------------------
fs/namei.c
639
static void follow_mount(struct path *path)
640
{
641while (d_mountpoint(path->dentry))
{
642struct vfsmount *mounted =
lookup_mnt(path);
643if (!mounted)
644break;
645dput(path->dentry);
646mntput(path->mnt);
647path->mnt = mounted;
648path->dentry =
dget(mounted->mnt_root);
649}
650
}
---------------------------------------------------------------------
follow_mount()函數(shù)檢查path ->dentry是否是某文件系統(tǒng)的掛載點(diǎn)(path-> dentry-> d_mounted的值大于0),如果不是,則直接退出。如果是,則調(diào)用lookup_mnt(),它的定義如下:
---------------------------------------------------------------------
fs/namespace.c
57
static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry)
58
{
59unsigned long tmp = ((unsigned
long)mnt / L1_CACHE_BYTES);
60tmp += ((unsigned long)dentry /
L1_CACHE_BYTES);
61tmp = tmp + (tmp >> HASH_SHIFT);
62return tmp & (HASH_SIZE - 1);
63
}
414
struct vfsmount *__lookup_mnt(struct vfsmount *mnt,
415struct dentry
*dentry, int dir)
416
{
417struct list_head *head =
mount_hashtable + hash(mnt, dentry);
418struct list_head *tmp = head;
419struct vfsmount *p, *found = NULL;
420
421for (;;) {
422tmp = dir ? tmp->next :
tmp->prev;
423p = NULL;
424if (tmp == head)
425break;
426p = list_entry(tmp, struct vfsmount,
mnt_hash);
427if (p->mnt_parent == mnt
&& p->mnt_mountpoint == dentry) {
428found = p;
429break;
430}
431}
432return found;
433
}
439
struct vfsmount *lookup_mnt(struct path *path)
440
{
441struct vfsmount *child_mnt;
442spin_lock(&vfsmount_lock);
443if ((child_mnt =
__lookup_mnt(path->mnt, path->dentry, 1)))
444mntget(child_mnt);
445spin_unlock(&vfsmount_lock);
446return child_mnt;
447
}
---------------------------------------------------------------------
對(duì)于一個(gè)vfsmount來(lái)說(shuō),哈希值是根據(jù)其父vfsmount對(duì)象的地址和掛載點(diǎn)地址來(lái)計(jì)算的。
follow_mount()函數(shù)就是要找到掛載在本路徑上的文件系統(tǒng),即vfsmount對(duì)象的地址和目錄項(xiàng)對(duì)象地址。
h.分量名既不是“.”,也不是“..”,調(diào)用do_lookup(nd, &this, &next)(878行),得到與給定的父目錄(nd->path)和文件名(要解析的路徑名分量&this)相關(guān)的目錄項(xiàng)對(duì)象,存放在結(jié)果參數(shù)next中。這個(gè)函數(shù)完成實(shí)際的查找,是link_path_walk()函數(shù)的核心。后面會(huì)有更詳細(xì)的說(shuō)明。
i.檢查剛解析的分量是否指向一個(gè)符號(hào)鏈接(next.dentry->d_inode具有一個(gè)i_op->follow_link方法)。將在后面“符號(hào)鏈接的查找”有更詳細(xì)的說(shuō)明。如果是則調(diào)用do_follow_link(&next, nd)做相應(yīng)的處理。
j.剛解析的分量不是指向一個(gè)符號(hào)鏈接調(diào)用path_to_nameidata(&next,
nd),把nd->path.dentry和nd->path.mnt分別置為next.dentry和next.mnt,然后繼續(xù)路徑名的下一個(gè)分量:
---------------------------------------------------------------------
fs/namei.c
523
static inline void path_to_nameidata(struct path *path, struct nameidata *nd)
524
{
525dput(nd->path.dentry);
526if (nd->path.mnt != path->mnt)
527mntput(nd->path.mnt);
528nd->path.mnt = path->mnt;
529nd->path.dentry = path->dentry;
530
}
---------------------------------------------------------------------
k.檢查剛解析的分量是否指向一個(gè)目錄(next.dentry->d_inode具有一個(gè)自定義的i_op->lookup方法)。如果沒(méi)有,返回一個(gè)錯(cuò)誤碼-ENOTDIR,因?yàn)檫@個(gè)分量位于原路徑名的中間,然后continue繼續(xù)路徑名的下一個(gè)分量。主要的循環(huán)到此結(jié)束。
7、減少對(duì)查找到的path的引用計(jì)數(shù)并返回。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的linux查看link 路径,link_path_walk()路径名查找的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Effective Java~38. 用
- 下一篇: Java消息服务~消息属性