スポンサーリンク

VOP_LOOKUP(9) FreeBSD カーネル開発者マニュアル VOP_LOOKUP(9)

名称

VOP_LOOKUP − パス名の構成要素の調査

書式

#include <sys/param.h>
#include <sys/vnode.h>
#include <sys/namei.h>

int

VOP_LOOKUP(struct vnode *dvp, struct vnode **vpp, struct componentname *cnp);

解説

このエントリポイントは与えられたディレクトリ内の単一のパス名要素を調べま す。

引数は以下のとおりです。

       dvp

検索するディレクトリのロックされた vnode です。

vpp
結果のロックされた vnode が格納されるべき変数のアドレスです。

cnp
検索されるためのパス名要素です。

cnp は以下で定義されている componentname 構造体へのポインタです。

struct componentname {

/*

* lookup への引数です。

*/

u_long

cn_nameiop;

/* namei 操作 */

u_long

cn_flags;

/* namei へのフラグ */

struct thread *cn_thread; /* lookup を要求しているスレッド */

struct

ucred *cn_cred;

/* 証明 */

/*

* 検索ルーチンおよびコミットルーチンで共有されます。

*/

char

*cn_pnbuf;

/* パス名バッファ */

char

*cn_nameptr;

/* 検索された名前へのポインタ */

long

cn_namelen;

/* 検索された要素の長さ */

u_long

cn_hash;

/* 検索された名前のハッシュ値 */

long

cn_consume;

/* lookup() で消費する文字数 */

};

パス名の要素をロックされた vnode へのポインタに変換します。これが正に中心 であり、より複雑なルーチンです。ファイルシステムが厳密なツリー階層に保た れていない場合には、デッドロック状態に陥ることがあります。

cnp->cn_nameiop 引数は、オブジェクトの用途目的に依存し、 LOOKUP, CREATE, RENAME, または DELETE です。 CREATE, RENAME, または DELETE が明示されたと きには、ディレクトリエントリの作成、名前変更、削除に使用する情報が計算で きます。

VOP_LOOKUP の全体の概要です。

ディレクトリのアクセス権をチェックします。キャッシュの中の名前を検 索し、もし見つかれば、その名前を返します。ディレクトリの中の名前を 検索し、found または notfound に適切に進みます。

notfound:

作成または名前変更でパス名の最後の場合には、 EJUSTRETURN を返し、利 用可能な位置に情報を残します。そうでなければ ENOENT を返します。

found:

パス名の最後で削除の場合には、削除を可能にする情報を返します。パス 名の最後で名前変更の場合には、変更先の inode をロックして名前変更を 可能にする情報を返します。最後でない場合には、名前をキャッシュに追 加します。最後で作成でも削除でもない場合には、名前をキャッシュに追 加します。

ロック

ディレクトリ dvp はエントリ時にロックされているべきです。エラー (注: 戻り 値 EJUSTRETURN はエラーとはみなされません) が検出された場合には、ロックさ れたまま戻ります。しかしながら、 cnp->cn_flags に LOCKPARENT および ISLASTCN の両方が明示されていない場合には、アンロックされません。エントリ がそのディレクトリの中で見つかった場合には、ロックされて返されます。

戻り値

要素が見つかった場合には、 *vpp にそのファイルのロックされた vnode が設定 されて、0 が返されます。検索されている要素が "." の場合、その vnode は vref(9) を使用して追加された余分な参照を得ます。この場合は、呼び出し側が 適切にロックを解放することに注意しなければなりません。

要素が見つからず操作が CREATE または RENAME である場合には、フラグ ISLASTCN が明示されて操作は成功し、特別な戻り値 EJUSTRETURN が返されま す。そうでない場合には、適切なエラーコードが返されます。

擬似コード

int
vop_lookup(struct vnode *dvp,

struct vnode **vpp,

struct componentname *cnp)

{
int error;
int nameiop = cnp->cn_nameiop;
int flags = cnp->cn_flags;
int lockparent = flags & LOCKPARENT;
int islastcn = flags & ISLASTCN;
struct vnode *vp = NULL;

/*
* ディレクトリのアクセス権をチェックします。
*/
if (dvp->v_type != VDIR)

return ENOTDIR;

error = VOP_ACCESS(dvp, VEXEC, cred, cnp->cn_thread);
if (error)

return (error);

if (islastcn && (dvp->v_mount->mnt_flag & MNT_RDONLY) &&

(cnp->cn_nameiop == DELETE || cnp->cn_nameiop == RENAME))

return (EROFS);

/*
* ディレクトリ/名前のペアのための名前キャッシュをチェックします。
* これは、存在していない名前の場合には ENOENT を、名前が見つかった
* 場合には -1 を、そうでない場合には 0 を返します。
*/
error = cache_lookup(dvp, vpp, cnp);
if (error) {

int vpid;

if (error = ENOENT)

return error;

vp = *vpp;

if (dvp == vp) {

/* "." 上を探します */

VREF(vp);

error = 0;

} else if (flags & ISDOTDOT) {

/*

* デッドロックを避けるため、".." のロックされた vnode を

* 取得する前に、そのディレクトリをアンロックする必要が

* あります。

*/

VOP_UNLOCK(dvp);

error = vget(vp, 1);

if (!error) {

if (lockparent && islastcn)

error = VOP_LOCK(dvp);

}

} else {

error = vget(vp, 1);

if (error || !(lockparent && islastcn)) {

VOP_UNLOCK(dvp);

}

}

/*

* ロックを待っている間に特性の数が変化していないことを

* チェックします。

*/

if (!error) {

if (vpid == vp->v_id) {

/*

* lockparent && islastcn の場合には dvp がロック

* されます。 vp がロックされます。

*/

return (0);

}

vput(vp);

if (dvp != vp && lockparent && islastcn)

VOP_UNLOCK(pdp);

}

/*

* 以下のディレクトリ検索のために dvp を再度ロックします。

*/

error = VOP_LOCK(dvp);

if (error) {

return (error);

}

*vpp = NULL;

}

/*
* 要素 cnp->cn_nameptr のために dvp を検索します。
*/
...;

if (!found) {

if ((nameiop == CREATE || nameiop == RENAME)

&& islastcn

&& directory dvp has not been removed) {

/*

* ディレクトリの書込み権をチェックします。

*/

/*

* できる限り、ディレクトリの中の新しい要素の名前のために

* 十分に大きい場所の位置を記録します。 これは dvp のための

* vnode の私的データの中に記録されることが可能です。

* VOP_CREATE または VOP_RENAME で後で使用するために、

* パス名に保持するために SAVENAME フラグを設定します。

*/

cnp->cn_flags |= SAVENAME;

if (!lockparent)

/*

* 上記で記録された追加のデータは lockparent が明示

* された時にのみ有用であることに注意してください。

*/

VOP_UNLOCK(dvp);

return EJUSTRETURN;

}

/*

* キャッシュに名前を挿入することを考慮します。

*/

if ((cnp->cn_flags & MAKEENTRY) && nameiop != CREATE)

cache_enter(dvp, NULL, cnp);

return ENOENT;

} else {

/*

* 削除でパス名の最後の場合には、ファイルの削除に使用できる

* パラメータを返します。 wantparent フラグが設定されていない

* 場合にはそのディレクトリのみを返し、そうでない場合には

* 続けてその inode をロックし、"." に注意します。

*/

if (nameiop == DELETE && islastcn) {

/*

* ディレクトリの書込み権をチェックします。

*/

error = VOP_ACCESS(dvp, VWRITE, cred, cnp->cn_thread);

if (error)

return (error);

if (found entry is same as dvp) {

VREF(dvp);

*vpp = dvp;

return 0;

}

error = VFS_VGET(dvp->v_mount, ..., &vp);

if (error)

return error;

if (directory is sticky

&& cred->cr_uid != 0

&& cred->cr_uid != owner of dvp

&& owner of vp != cred->cr_uid) {

vput(vp);

return EPERM;

}

*vpp = vp;

if (!lockparent)

VOP_UNLOCK(dvp);

return 0;

}

/*

* 再書込み (RENAME) の場合には、その inode および現在の

* ディレクトリを再書込みするために要求される情報を返します。

* 通常ファイルまたは空のディレクトリであることを確認するために、

* ディレクトリエントリの inode を取得しなければなりません。

*/

if (nameiop == RENAME && wantparent && islastcn) {

error = VOP_ACCESS(dvp, VWRITE, cred, cnp->cn_thread);

if (error)

return (error);

/*

* "." をチェックします。

*/

if (found entry is same as dvp)

return EISDIR;

error = VFS_VGET(dvp->v_mount, ..., &vp);

if (error)

return error;

*vpp = vp;

/*

* 後で VOP_RENAME で使用するために名前を保存します。

*/

cnp->cn_flags |= SAVENAME;

if (!lockparent)

VOP_UNLOCK(dvp);

return 0;

}

/*

* 名前の変換のステップの全容です。 シンボリックリンクが現在の

* ディレクトリからの相対である場合には、再度必要になるので、

* ディレクトリを ’vput’ しません。 代わりに "pdp" として

* アンロックされたディレクトリを保存します。 取得する前にその

* inode が削除されないことを保証するために、そのディレクトリを

* アンロックする前に目的の inode を取得しなければなりません。

* ルートからディレクトリツリーを下る方向で inode を取得する

* ことで、デッドロックを常に予防します。 従がって、逆向きの

* ポインタ ".." を辿るときには、要求されたディレクトリを取得する

* 前に親ディレクトリをアンロックしなければなりません。 ".." に

* 関連付けられた inode のための VFS_VGET が戻る前に、現在の

* および親ディレクトリの両方が削除された場合には、ここに潜在的な

* 競合状態があります。 きわめて複雑なデッドロック検出アルゴリズム

* の実装なしにこの競合状態を避けることができないので、この

* 競合状態の発生が稀であることを期待しています。 ファイルシステムが、

* ".." 以外に、ディレクトリ構造の中に遡りを指すハードリンクを

* 持つ場合、この簡単なデッドロック検出機構が働かないことにも注意

* してください。

*/

if (flags & ISDOTDOT) {

VOP_UNLOCK(dvp);

/* inode を取得するための競合です */

error = VFS_VGET(dvp->v_mount, ..., &vp);

if (error) {

VOP_LOCK(dvp);

return (error);

}

if (lockparent && islastcn) {

error = VOP_LOCK(dvp);

if (error) {

vput(vp);

return error;

}

}

*vpp = vp;

} else if (found entry is same as dvp) {

VREF(dvp);

/* 例えば "." など、自分自身が必要です */

*vpp = dvp;

} else {

error = VFS_VGET(dvp->v_mount, ..., &vp);

if (error)

return (error);

if (!lockparent || !islastcn)

VOP_UNLOCK(dvp);

*vpp = vp;

}

/*

* 適切であれば名前をキャッシュに挿入します。

*/

if (cnp->cn_flags & MAKEENTRY)

cache_enter(dvp, *vpp, cnp);

return (0);

}
}

エラー

       [ENOTDIR]

vnode dvp がディレクトリを表わしていません。

[ENOENT]
要素 dvp がそのディレクトリには見つかりません。

[EACCES]
明示された操作のためのアクセスが拒否されました。

[EJUSTRETURN]
CREATE または RENAME の操作は成功です。

関連項目

VOP_ACCESS(9), VOP_CREATE(9), VOP_MKDIR(9), VOP_MKNOD(9), VOP_RENAME(9), VOP_SYMLINK(9), vnode(9)

歴史

関数 VOP_LOOKUP は 4.3BSD で登場しました。

作者

このマニュアルページは ufs_lookup.c の中のコメントからのテキストを使っ て、 Doug Rabson が書きました。

FreeBSD 10.0 November 24, 1997 FreeBSD 10.0

スポンサーリンク