VOP_RENAME(9)

名称
VOP_RENAME - ファイルの名前変更

書式
<sys/param.h> <sys/vnode.h> int VOP_RENAME(struct vnode *fdvp, struct vnode *fvp, struct componentname *fcnp, struct vnode *tdvp, struct vnode *tvp, struct componentname *tcnp)

解説
これはファイルの名前変更をし、 もしかすると親ディレクトリを変更するかもしれません。 変更先のオブジェクトが存在する場合には、最初に削除されます。

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

fdvp
    古い親ディレクトリの vnode です。

fvp 名前変更されるべきファイルの vnode です。

fcnp
    ファイルの現在の名前についてのパス名情報です。

tdvp
    新しい親ディレクトリの vnode です。

tvp (もし存在するならば) 変更先のファイルの vnode です。

tcnp
    ファイルの新しい名前についてのパス名情報です。

ロック
エントリ時には、変更元のディレクトリおよびファイルはロックされておらず、 それらの参照カウントは増やされています。 この VOP ルーチンは戻る前に両方に対して vrele(9) を呼び出すはずです。

変更先のディレクトリおよびファイルは、それらの参照カウントが増やされるのは もちろん、ロックされています。 この VOP ルーチンは戻る前に両方に対して vput(9) を呼び出すはずです。

疑似コード

    int
    vop_rename(struct vnode *fdvp, struct vnode *fvp, struct componentname *fcnp,
    	   struct vnode *tdvp, struct vnode *tvp, struct componentname *tcnp)
    {
        int doingdirectory = 0;
        int error = 0;
        /*
         * デバイスをまたがる名前変更のためのチェックです。
         */
        if (fvp->v_mount != tdvp->v_mount) {
    	error = EXDEV;
        abortit:
    	if (tdvp == tvp)
    	    vrele(tdvp);
    	else
    	    vput(tdvp);
    	if (tvp)
    	    vput(tvp);
    	vrele(fdvp);
    	vrele(fvp);
    	return error;
        }
        if (tvp exists and is immutable) {
    	error = EPERM;
    	goto abortit;
        }
        /*
         * リンク名を消そうとしているかどうかのチェックです。
         */
        if (fvp == tvp) {
    	if (fvp->v_type == VDIR) {
    	    error = EINVAL;
    	    goto abortit;
    	}
    	/*
    	 * 変更先を解放します。
    	 */
    	vput(tdvp);
    	vput(tvp);
    	/*
    	 * 変更元を削除します。少し異様な要素です。
    	 */
    	vrele(fdvp);
    	vrele(fvp);
    	fcnp->cn_flags &= ~MODMASK;
    	fcnp->cn_flags |= LOCKPARENT | LOCKLEAF;
    	fcnp->cn_nameiop = DELETE;
    	VREF(fdvp);
    	error = relookup(fdvp, &fvp, fcnp);
    	if (error == 0)
    	    vrele(fdvp);
    	return VOP_REMOVE(fdvp, fvp, fcnp);
        }
        if (fvp is immutable) {
    	error = EPERM;
    	goto abortit;
        }
        error = VOP_LOCK(fvp);
        if (error)
    	goto abortit;
        if (vp is a directory) {
    	/*
    	 * 明白な理由のため、".", ".." と "." の別名を避けます。
    	 */
    	if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.')
    	    || fdvp == fvp
    	    || ((fcnp->cn_flags | tcnp->cn_flags) & ISDOTDOT)) {
    	    VOP_UNLOCK(fvp);
    	    error = EINVAL;
    	    goto abortit;
    	}
    	doingdirectory = 1;
        }
        vrele(fdvp);
        /*
         * 移動要素の活動中の間は、fvp のリンクカウントを増やします。
         * 作業完了の前にクラッシュした場合には、リンクカウントは間違って
         * いるでしょうが、修正可能です。
         */
        ...;
        /*
         * それから (例えばディレクトリが新しい親を得るなど) ".." が変更
         * されなければならない場合には、変更元のディレクトリは変更先よりも
         * 上位のディレクトリ階層に在ってはなりません。これは変更元の
         * ディレクトリ以下の全てが孤児となるからです。また、ユーザは
         * 変更元で ".." を変更できるように書込みパーミッションを持って
         * いなければなりません。
         */
        error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_thread);
        VOP_UNLOCK(fvp);
        if (doingdirectory && fdvp != tdvp) {
    	/*
    	 * パス名の衝突をチェックします。
    	 */
    	...;
        }
        /*
         * 変更先が存在しない場合、変更先を変更元にリンクして、変更元を
         * アンリンクします。そうでない場合、変更先ディレクトリを書き換えて
         * 変更元を参照するようにし、元のエントリを削除します。
         */
        if (tvp == NULL) {
    	/*
    	 * 新しいディレクトリの ".." のアカウント。
    	 */
    	if (doingdirectory && fdvp != tdvp) {
    	    /*
    	     * tdvp のリンクカウントを増やします。
    	     */
    	    ...;
    	}
    	/*
    	 * 新しいディレクトリで名前を追加します。
    	 */
    	...;
    	if (error) {
    	    if (doingdirectory && fdvp != tdvp) {
    		/*
    		 * tdvp であればリンクカウントを減らします。
    		 */
    		...;
    	    }
    	    goto bad;
    	}
    	vput(tdvp);
        } else {
    	/*
    	 * 変更先がディレクトリの場合、これは空でなければならず、これへの
    	 * リンクがあってはなりません。また、変更元と変更先は一致する
    	 * (両方ともディレクトリ、または両方ともディレクトリではない)
    	 * ことを保証してください。
    	 */
    	if (tvp is a directory) {
    	    if (tvp is not empty) {
    		error = ENOTEMPTY;
    		goto bad;
    	    }
    	    if (!doingdirectory) {
    		error = ENOTDIR;
    		goto bad;
    	    }
    	    /*
    	     * ディレクトリが消え去ったため、名前キャッシュを更新します。
    	     */
    	    cache_purge(tdvp);
    	} else if (doingdirectory) {
    	    error = ENOTDIR;
    	    goto bad;
    	}
    	/*
    	 * fvp が指す tdvp の名前 tcnp を変更します。
    	 */
    	...;
    	/*
    	 * 変更先ディレクトリが変更元と同じディレクトリにある場合には、
    	 * 変更先のディレクトリの親のリンクカウントを減らします。
    	 * ".." により、その親に対する戻り方向のリンクとなる事実を、
    	 * 説明するものです。
    	 */
    	if (doingdirectory && fdvp == tdvp) {
    	    /*
    	     * tdvp のリンクカウントを減らします。
    	     */
    	    ...;
    	}
    	vput(tdvp);
    	/*
    	 * ディレクトリがもはやそれを指さないので、tvp のリンクカウントを
    	 * 減らします。
    	 */
    	...;
    	if (doingdirectory) {
    	    /*
    	     * 古いディレクトリ tvp をきれいにします。
    	     */
    	    ...;
    	}
    	vput(tvp);
        }
        /*
         * 変更元をアンリンクします。ディレクトリが新しい親に移動される場合
         * には、その ".." エントリを更新します。ここで、たくさんの厄介な
         * UFS のコードが省かれました。
         */
        ...;
    bad:
        if (tvp)
    	vput(tvp);
        vput(tdvp);
    out:
        if (VOP_LOCK(fvp) == 0) {
    	/*
    	 * fvp のリンクカウントをデクリメント
    	 */
    	...;
            vput(fvp);
        } else
    	vrele(fvp);
        return error;
    }

エラー

[EPERM]
    ファイルが変更可能ではありません。

[EXDEV]
    異なるファイルシステム間の名前変更はできません。

[EINVAL]
    ~. または.. の名前変更、またはディレクトリツリーを破壊するような操作の実行を 行なおうとしました。

[ENOTDIR]
    ディレクトリからファイルにまたはその逆に名前変更しようとしました。

[ENOTEMPTY]
    空ではないディレクトリを削除しようとしました。

関連項目
vnode(9)

作者
このマニュアルページは Doug Rabson が書きました。