訳註: これは作業してる最中のもので、本来、訳作業者以外は見えないものです。 訳作業者以外は見ないでください。意味が反対になったりとか当然にあるから、 内容は利用しないでください。 十進240のコードのキャラクターを<240>に置換してる場合あり Chapter<240>2.<240>File system internals

Table of Contents

2.1. vnode layer overview
2.2. VFS layer overview
2.3. File systems overview
2.4. Initialization and cleanup
2.5. Mounting and unmounting
2.6. File system statistics
2.7. vnode management
2.8. The root vnode
2.9. Path name resolution procedure
2.10. File management
2.11. Symbolic link management
2.12. Directory management
2.13. Special nodes
2.14. NFS support
2.15. Step by step file system writing

この章は NetBSD の下で開発されるファイルシステムの背後の非常に詳細な コンセプトを記述します。架空のファイルシステムを example file system を表す egfs の名前の下に幾つかのコードの例を示します。

この章を通じて、 file という語は ファイルシステム内に存在するかもしれない あらゆる種類のファイル を参照するのに使い; これには ディレクトリー、通常ファイル、シンボリックリンク、 特殊デバイスおよびネームドパイプが含まれます。もし、 データを蓄えるファイルに言及する必要があるときは、 通常ファイル(regular file) という用語を 明示的に使います。***←通常ファイル になってないとこあるかも***

仮想ファイルシステム (VFS) としての複雑なサブシステムの理解は難しいです。 この章は、 vnode (Section<240>2.1, “vnode layer overview”) および、既存ファイルシステムと同様に VFS (Section<240>2.2, “VFS layer overview”) layers 両方の概観を与える事から始め; この順序で読むべきです。これら3つの節 サブシステム全体の概要を提供し、 読者が、必要とする既存コードを読解することを可能にするはずです。

あとは、ファイルシステム実装に関連するその他の詳細を記述し、そして layer の概観に与えられた説明の増量を続けています (しかしどうか注意として、情報は重複していませんので、 概観の節を読む事は "必須" です)。これらの節は いかなる順序で読んだとしても、リファレンスガイドとして、 高度にハイパーリンクされた簡単ナビゲーションで、過不足ない様にできています。

とても最後のほうに要約した節があり、 すぐにコピーアンドペーストできるコード例に基づいて、 スクラッチからのファイルシステムドライバーの書き方があります。 註として、この節には、 per se の説明はありませんが、しかし、 各ポイントが記された適切な節へのリンクだけがあります。

2.1.<240>vnode layer overview

vnode は、 NetBSD カーネル内部の active ファイルの abstract 表現で; それは、 real ファイルがあるファイルシステムに関わらず、 ファイル操作の一晩的な方法を提供します。 この抽象 layer のおかげで、全カーネルサブシステムは vnode だけを 扱います。重要な註として unique vnode for each active file があります。

vnode は struct vnode 構造体で記述され; その定義は src/sys/sys/vnode.h ファイルにあり、 それのフィールドに関する情報は vnode(9) manual page にあります。 以下は この構造体に関する最も重要な ideas の解析です。

大抵いわれるように、 abstract representations[抽象表現]は、 instantiated[例示]される前にspecialized[詳説/限定]されなければなりません。 vnodes は例外ではなく: 各 ファイルシステムは vnode の静的部および動的部両方をつぎのように拡張し:

  • 静的部分 — オブジェクトを表すデータフィールド — は、その作成中に、 custom データ構造 を vnode instance に attaching することで拡張されます。これは、 Section<240>2.1.1, “The vnode data field” に記述される v_data フィールドを通して行われます。

  • 動的部分 — オブジェクトへ適用できる操作 — は、その作成中に、 vnode 操作 vector を vnode instance へ attaching することで拡張されます。 これは、 Section<240>2.1.3, “The vnode operations vector” に記述された v_op フィールドを通して完了します。

2.1.1.<240>The vnode data field

struct vnode 型の中の v_data フィールドは concrete ファイルシステム中のファイルを represent するのに使われる 外部データ構造へのポインターです。 このフィールド は新しい vnode の allocating の後、初期化されることが必要で、 releasing 前には NULL に セットされる必要があります (Section<240>2.7.4, “Deallocation of a vnode” を御覧ください)。

この外部データ構造は、ファイルシステム内部の特有のファイルを記述するため のあらゆる追加情報を含みます。 ディスク上ファイルシステム内では、これは、ファイルの初期 cluster 、 生成時刻、そのサイズ、等を含むかもしれまへん。例として、 NetBSD の Fast File System (FFS) は vnode のデータフィールドとして an inode のthe in-core memory 表現 を用います。

2.1.2.<240>vnode operations

vnode 操作は、つぎの contract に従う関数として実装されていて: 操作の終了ステータスを表す整数を返し、実際の操作の引数を伴なった構造体を 運ぶ void * パラメーター1つをとります。

regular 引数 list の利用の代わりに、操作の引数を記述するのに 外部構造体を利用するのには理由があり: 幾つかのファイルシステムは、追加の非標準操作で vnode を 拡張し; 共通のプロトタイプがこれを可能にします。

次の table は標準 vnode 操作を要約しています。でも、記憶に留めておく事に、各ファイルシステムは このセットを思いのまま自由に拡張できます。また註として、操作名は、 それをコールするマクロとして table 中に現れます (see Section<240>2.1.4, “Executing vnode operations”)。

Table<240>2.1.<240>vnode operations summary

操作 Description See also
VOP_LOOKUP Performs a パス名 lookup. See Section<240>2.9, “Path name resolution procedure”.
VOP_CREATE 新しいファイルの作成。 See Section<240>2.10.1, “Creation of regular files”.
VOP_MKNOD 新しい特殊ファイル (デバイスまたはネームドパイプ) の作成。 See Section<240>2.13, “Special nodes”.
VOP_LINK ファイルへの新しいハードリンクの作成。 See Section<240>2.10.2, “Creation of hard links”.
VOP_RENAME ファイルの Renames 。 See Section<240>2.10.4, “Rename of a file”.
VOP_REMOVE ファイルを Removes 。 See Section<240>2.10.3, “Removal of a file”.
VOP_OPEN ファイルのオープン。 <240>
VOP_CLOSE ファイルのクローズ。 <240>
VOP_ACCESS Checks access permissions on a ファイル。 See Section<240>2.10.8, “Access control”.
VOP_GETATTR ファイルの attributes を取得。 See Section<240>2.10.6.1, “Getting file attributes”.
VOP_SETATTR ファイルの attributes をセット。 See Section<240>2.10.6.2, “Setting file attributes”.
VOP_READ ファイルからデータのチャンクを読む。 See Section<240>2.10.5.4, “The read and write operations”.
VOP_WRITE ファイルへデータのチャンクを書く。 See Section<240>2.10.5.4, “The read and write operations”.
VOP_IOCTL Performs an ioctl(2) on a file. <240>
VOP_FCNTL Performs a fcntl(2) on a ファイル。 <240>
VOP_POLL Performs a poll(2) on a ファイル。 <240>
VOP_KQFILTER XXX <240>
VOP_REVOKE Revoke access to a vnode and all aliases. <240>
VOP_MMAP メモリー領域上にファイルを Maps する。 See Section<240>2.10.5.3, “Memory-mapping a file”.
VOP_FSYNC ファイルをディスク上の 内容と Synchronizes 。 <240>
VOP_SEEK Test and inform ファイルシステム of seek <240>
VOP_MKDIR 新しいディレクトリーを作る。 See Section<240>2.12.1, “Creation of directories”.
VOP_RMDIR Removes a ディレクトリー. See Section<240>2.12.2, “Removal of directories”.
VOP_READDIR ディレクトリーからディレクトリー entries を Reads 。 See Section<240>2.12.3, “Reading directories”.
VOP_SYMLINK ファイルに対する新しいシンボリックリンクの作成。 See Section<240>2.11.1, “Creation of symbolic links”.
VOP_READLINK シンボリックリンクの内容を Reads 。 See Section<240>2.11.2, “Read of symbolic link's contents”.
VOP_TRUNCATE Truncates a ファイル。 See Section<240>2.10.6.2, “Setting file attributes”.
VOP_UPDATE ファイルの時刻の更新。 See Section<240>2.10.7, “Time management”.
VOP_ABORTOP Aborts an in-progress 操作. <240>
VOP_INACTIVE Marks the vnode as inactive. See Section<240>2.7.1, “vnode's life cycle”.
VOP_RECLAIM Reclaims the vnode. See Section<240>2.7.1, “vnode's life cycle”.
VOP_LOCK vnode のロック。 See Section<240>2.7.5, “vnode's locking protocol”.
VOP_UNLOCK vnode を Unlocks 。 See Section<240>2.7.5, “vnode's locking protocol”.
VOP_ISLOCKED vnode が locked か否かを Checks 。 See Section<240>2.7.5, “vnode's locking protocol”.
VOP_BMAP 論理ブロック number を物理ブロック number へ Maps 。 See Section<240>2.10.5.5, “Reading and writing pages”.
VOP_STRATEGY Performs a ファイル transfer between the ファイルシステムの backing store and memory. See Section<240>2.10.5.5, “Reading and writing pages”.
VOP_PATHCONF pathconf(2) 情報を返す。 <240>
VOP_ADVLOCK XXX <240>
VOP_BWRITE システムバッファーの Writes 。 <240>
VOP_GETPAGES Reads ファイルから memory pages 。 See Section<240>2.10.5.2, “Getting and putting pages”.
VOP_PUTPAGES ファイルへ memory pages を Writes 。 See Section<240>2.10.5.2, “Getting and putting pages”.

2.1.3.<240>The vnode operations vector

struct vnode 型内の v_op フィールドは、 (Section<240>2.1.2, “vnode operations” に見られる) real 関数へ の 論理的操作を maps する vnode 操作 vector へのポインターです。 この vector は、ファイルがあるファイルシステムにドッカリと依存した各操作 がとる actions として、ファイルシステム特有です (ファイルの reading を考えると、 それの attributes の設定、等)。

例として、以下の断片を考えると; open 操作を定義し、その引数構造体から 2つのパラメーターを引き出し:

int
egfs_open(void *v)
{
        struct vnode *vp = ((struct vop_open_args *)v)->a_vp;
        int mode = ((struct vop_open_args *)v)->a_mode;

        ...
}

ファイルシステムで定義された vnode 操作の全セット は struct vnodeopv_entry_desc-型 entries の vector に single 操作を記述する各 entry 共に加えられます。この vector の意図は vop_openvop_read のような論理操作から、 egfs_openegfs_read のような real 関数への mapping を定義する事です。 normal 操作のもとでは、 それはシステムから直接使われません 。この vector は 特有の layout とは tied されず: それを記述するファイルシステムで 使える操作のみ lists され、順番は不問です。 最も基本的な物の幾つかが欠けているのと同じような 非標準 (そして未知の) 操作でさえ list できます。(理由はと言うと、 繰り返すと、サードパーティーによる拡張性。)

2つの小さな制約があるのだけども:

  • 最初の item は常に存在しないものが呼ばれた場合に使われる操作へのポインター。 例えば、もしファイルシステム が vop_bmap 操作を実装しないのに、しかし、あるコードがそれを呼ぶと、 call は この default-catch 関数に redirected されます。それなりに、それは generic エラールーチンを提供するのに使われますが、また、 シナリオにおいても便利です。例, layered ファイルシステム call down the スタックに渡すのに使われます。

    重要な註として、この functionality を実装する2つの標準 エラールーチンが利用できて: vn_default_error および genfs_eopnotsupp です。 後者は正しく vnode references を cleans up し locks するのだけども、 前者は traditional なエラーの場合のです。 新しいコードは前者だけを使うべきです。

  • 最後の item は常に null ポインターが一対です。

例として次の vector を考えます:

const struct vnodeopv_entry_desc egfs_vnodeop_entries[] = {
        { vop_default_desc, vn_default_error },
        { vop_open_desc, egfs_open },
        { vop_read_desc, egfs_read },
        ... 更なる操作がここに ...
        { NULL, NULL }
};

上に示されたように、この vector はシステムによって直接利用されていません ; 実際、厳密な順序規則に従った secondary vector の構成のみを供します。 の secondary vector は カーネルによってファイルシステム初期化中に自動生成され、 それで、命令するコードが必要な事は、変換だけです。

この secondary vector は、 int (**vops)(void *) 型の 関数ポインターの配列を指すポインターとして定義されます。 カーネルに、 vector がどこかを訊ねるには、 struct vnodeopv_desc-型 items の third vector を通して確立した vectors 間の mapping です。 これは例があると分かりやすくて:

int (**egfs_vnodeop_p)(void *);
const struct vnodeopv_desc egfs_vnodeop_opv_desc =
        { &egfs_vnodeop_p, egfs_vnodeop_entries };

ファイルシステムのscope[範囲]外では、 vnode layer のユーザーは egfs_vnodeop_p および egfs_vnodeop_opv_desc vectors だけを扱います。

2.1.4.<240>Executing vnode operations

全 vnode 操作は、幾つか他の call および return contracts の間の 非常に厳格な locking プロトコルに服従しています。 さらに、それらのプロトタイプが呼び出しをかなり複雑にしています (現実の引数を伴なった構造体で受け取る事を思い出してください)。 これらがなぜ直接呼ぶ事ができないかという理由です (幾つか例外がありますがここでは論じません)。

NetBSD カーネルは、ありふれた vnode 操作の実行をする マクロおよび関数のセットを提供し; どうか注意として それらは標準 call 手順です。これらのマクロは、 参照する操作に、接頭語として、 VOP_ という文字列を全部大文字で付けた もので命名されています。それから、渡される引数の list を取ります。

例えば、 access 操作の以下の実装を考えると:

int
egfs_access(void *v)
{
        struct vnode *vp = ((struct vop_access_args *)v)->a_vp;
        int mode = ((struct vop_access_args *)v)->a_mode;
        struct ucred *cred = ((struct vop_access_args *)v)->a_cred;
        struct proc *p = ((struct vop_access_args *)v)->a_p;

        ...
}

前の method の call はこのように:

result = VOP_ACCESS(vp, mode, cred, p);

更なる情報は、 vnode 操作と対応するマクロの全 mappings を記述した vnodeops(9) manual page を御覧ください。

2.2.<240>VFS layer overview

カーネルの Virtual File System (VFS) サブシステムは、 まさに active ファイル とで vnodes が行うように抽象的な方法で、 全利用可能ファイルシステムへの access を提供します。 各ファイルシステムは、その status を keeps する データ構造体と共に適用できる明確な操作のリストで記述されます。

2.2.1.<240>The mount structure

ファイルシステムは、マウントポイントによって仮想ディレクトリーツリー に attached されます。マウントポイントは 特定のディレクトリー[1] から異なるファイル システムのルートディレクトリーへのリダイレクト[redirection]で、 一般的な struct mount 型で表され、これは、 src/sys/sys/mount.h に定義されているものです。

ファイルシステムは、 struct mount オブジェクトの静的部分を、 その mnt_data フィールドに custom データ構造 を attaching することで拡張します。 As with vnodes, これは 構造体の割り当てのときに起こります。

ファイルシステムがその mount 構造体に stores した情報の種類は、 その実装に非常に依存します。一般的に、 typically include 更なるアクセスの開始点として使われる ファイルシステムのルートノードへの(物理的か論理的か)ポインターを含みます。 それはまた、マウントポイントに attached された ファイルシステム全体の context とする他の情報 のような幾つかの accounting 変数を含んでいるかもしれません。

2.2.2.<240>VFS operations

ファイルシステムドライバーは、 public 操作のセットによって、 カーネルへ、良く知られたインターフェースを exposes ます。次の table に それら全てを要約しており; 註として、これらは VFS 操作 vector でとられている順序に従ってソートされています (Section<240>2.2.3, “The VFS operations structure”を御覧ください)。

Table<240>2.2.<240>VFS operations summary

操作 説明 考察 See also
fs_mount ファイルシステムの new instance をマウント。 定義されねばなりません。 See Section<240>2.5, “Mounting and unmounting”.
fs_start ファイルシステムを operational にします。 定義されねばなりません。 <240>
fs_unmount ファイルシステムの instance をアンマウント。 定義されねばなりません。 See Section<240>2.5, “Mounting and unmounting”.
fs_root ファイルシステム root vnode を取得。 定義されねばなりません。 See Section<240>2.8, “The root vnode”.
fs_quotactl Queries or modifies space quotas. 定義されねばなりません。 <240>
fs_statvfs ファイルシステム statistics を取得。 定義されねばなりません。 See Section<240>2.6, “File system statistics”.
fs_sync ファイルシステム buffers を Flushes 。 定義されねばなりません。 <240>
fs_vget ファイル識別[子/名]*****identifier から vnode を取得。 定義されねばなりません。 See Section<240>2.7.3, “Allocation of a vnode”.
fs_fhtovp NFS ファイル handle から vnode への変換。 定義されねばなりません。 See Section<240>2.14, “NFS support”.
fs_vptofh vnode から NFS ファイル handle への変換。 定義されねばなりません。 See Section<240>2.14, “NFS support”.
fs_init ファイルシステムドライバーの初期化。 定義されねばなりません。 See Section<240>2.4, “Initialization and cleanup”.
fs_reinit ファイルシステムドライバーの再初期化。 恐らく未定義 (すなわち null)。 See Section<240>2.4, “Initialization and cleanup”.
fs_done ファイルシステムドライバーを Finalizes 。 定義されねばなりません。 See Section<240>2.4, “Initialization and cleanup”.
fs_mountroot ファイルシステムとしてファイルシステムの instance をマウント。 恐らく未定義 (すなわち null)。 <240>
fs_extattrctl 拡張された attributes を Controls 。 一般 vfs_stdextattrctl 関数は、この操作をサポートしないファイルシステムのために a simple hook として提供されます。 <240>

VFS 操作のリストはそのうち変わるかもしれません。 そうなったら、カーネルバージョン番号は bumped 。

2.2.3.<240>The VFS operations structure

マウントポイントに関わらず、ファイルシステムは src/sys/sys/mount.h で定義されている 自身の型を記述する struct vfsops 構造体を提供します。 基本的に、含むものは:

  • public identifier 、通常、ファイルシステムの 名前の後に接尾語 fs 文字列をつけた名前。 As この identifier 多様な場所で使われ — そして 特にカーネル空間およびユーザーランド両方で — src/sys/sys/mount.h でマクロとして typically 定義されています。例えば: #define MOUNT_EGFS "egfs".

  • ファイルシステム操作の関数ポインターセット。 vnode 操作とは対照的に、 VFS のものは、異なる プロトタイプで、なぜなら、可能な VFS 操作のセットが サードパーティーファイルシステム によって拡張できない事が良く知られているからです。 この vector の exact contents についての更なる詳細は、 どうか Section<240>2.2.2, “VFS operations” を御覧ください。

  • struct vnodeopv_desc * const の items のヌル終端 vector へのポインター。 これらのオブジェクトはここに listed され、 Section<240>2.1.3, “The vnode operations vector” に述べられているように、 ファイルシステム startup に、システムはそれらを real vnode 操作 vectors の construct に使うからです。

    興味深い註として、このフィールドは一つ以上のポインターを含むかもしれません。 幾つかのファイルシステムは一つ以上の vnode 操作セットを提供するかも 知れず; 例, vector の一つは 通常操作、もう一つはネームドパイプに関係する操作のため、 そしてもう一つは、特殊デバイス上で振舞われる操作。 この例については FFS code を、そして、これらの特別な vectors については Section<240>2.13, “Special nodes” を 御覧ください。

上の items を説明する以下のコード断片 を考えてみましょう:

const struct vnodeopv_desc * const egfs_vnodeopv_descs[] = {
        &egfs_vnodeop_opv_desc,
        ... more pointers may appear here ...
        NULL
};

struct vfsops egfs_vfsops = {
        MOUNT_EGFS,
        egfs_mount,
        egfs_start,
        egfs_unmount,
        egfs_root,
        egfs_quotactl,
        egfs_statvfs,
        egfs_sync,
        egfs_vget,
        egfs_fhtovp,
        egfs_vptofh,
        egfs_init,
        NULL, /* fs_reinit: optional */
        egfs_done,
        NULL, /* fs_mountroot: optional */
        vfs_stdextattrctl,
        egfs_vnodeopv_descs
};

カーネルは、 live ファイルシステムを憶えておく[keep track]ために、 この構造体の各 instance がどこに位置しているのか知る必要があります。 カーネルの core の中にファイルシステムを組み込むには、 VFS_ATTACH マクロ adds the given VFS 操作ズ 構造体 to the 適正な link set. この機能の 更なる詳細については GNU ld の info manual を御覧ください。

VFS_ATTACH(egfs_vfsops);

Standalone ファイルシステムモジュールはこれをする必要が無くて、 と言うのも、カーネルは モジュールがロードされた後に、情報構造体へのポインターを明示的に 取得するでしょうからです。

2.3.<240>File systems overview

2.3.1.<240>On-disk file systems

ディスク上ファイルシステムは 物理ドライブ上にそれらの内容を蓄えるものです。

  • Fast File System (ffs): XXX

  • Log-structured File System (lfs): XXX

  • Extended 2 File System (ext2fs): XXX

  • FAT (msdosfs): XXX

  • ISO 9660 (cd9660): XXX

  • NTFS (ntfs): XXX

2.3.2.<240>Network file systems

  • ネットワークファイルシステム (nfs): XXX

  • Coda (codafs): XXX

2.3.3.<240>Synthetic file systems

  • メモリーファイルシステム (mfs): XXX

  • カーネルファイルシステム (kernfs): XXX

  • Portal ファイルシステム (portalfs): XXX

  • 擬似端末ファイルシステム (ptyfs): XXX

  • Temporary ファイルシステム (tmpfs): XXX

2.3.4.<240>Layered file systems

  • Null ファイルシステム (nullfs): XXX

  • Union ファイルシステム (unionfs): XXX

  • User-map ファイルシステム (umapfs): XXX

2.3.5.<240>Helper file systems

Helper ファイルシステムは、単に、 他のファイルシステムを簡単に実装するために使われる関数のセット。 それ自体をライブラリーとみなす事ができます。これらは:

  • fifofs: ファイルシステムの ネームドパイプの取り扱いに用いられる全操作を実装。

  • genfs: multiple ファイルシステム全体で 共有する一般的な操作を実装。

  • layerfs: layered ファイルシステム全体で 共有する一般的な操作の実装 (see Section<240>2.3.4, “Layered file systems”)。

  • specfs: ファイルシステム中の特殊ファイルの取り扱いに使われる 全操作を実装しています。

2.4.<240>Initialization and cleanup

ドライバーは大抵、初期化ルーチンと 終了処理ルーチンがあり、それぞれ、ドライバーが active に成ったとき (例, システム startup 時) または inactive に成ったとき(例, それのモジュールを unloading ) に呼ばれます。 ファイルシステムはこれらのルールにも従うので、 総括して、マウントポイントに関わらず、これらは global tasks できます。

これらの初期化および終了処理 tasks はそれぞれ fs_init および fs_done hooks から完了できます。もし、ドライバーがモジュールとして提供されていたら、 それがロードされたとき初期化ルーチンが呼ばれ、そして、 アンロードされたとき cleanup 関数が実行されます。 代わりに、もしカーネル組み込みなら、 初期化コードはカーネル boot の非常に早期 stages に実行され、しかし、例え、システムの shut down であってさえも the cleanup stuff is never run [cleanup stuff は決して実行されません]

それから、 fs_reinit 操作は ... XXX... を提供するんです。

これら3つの操作は次のプロトタイプをとり:

int fs_init( ); <240>
void;
<240>
int fs_reinit( ); <240>
void;
<240>
int fs_done( ); <240>
void;
<240>

注意する事は、何らのパラメーターも、マウントポイントでさえも無いことです。

例として、ファイルシステム固有に定義された malloc 型 (see Section<240>1.2.1, “Malloc types”) を 扱う以下の関数を考えると:

MALLOC_JUSTDEFINE(M_EGFSMNT, "egfs mount", "egfs mount structures");

void
egfs_init(void)
{

        malloc_type_attach(M_EGFSMNT);

        ...
}

void
egfs_done(void)
{

        ...

        malloc_type_detach(M_EGFSMNT);
}

2.5.<240>Mounting and unmounting

マウント操作、すなわち fs_mount は多分、 VFS layer で最も複雑なものの一つです。その目的は、ユーザーランドから 受け取った引数に基づいて、新しいマウントポイントのセットアップのためです。 基本的に、操作するマウントポイントと mount call パラメーターを記述したデータ構造体を受け取ります。

残念にも、この操作は 本当はふさわしくない幾つかの解釈で overloaded です。 さらに specifically 、 ユーザーランドから取得されたものと同様に マウントポイントパラメーターの updating も担当します。 これは幾つかの点で明確にされるべきであります。

これらの全詳細は以下の副節で見ていきます。

2.5.1.<240>Mount call arguments

ほとんどのファイルシステムが、新しいマウントポイントが セットあっぱされた時、ユーザーランドマウント ユーティリティーからカーネルに情報がパスされ; この情報は一般的に、 カーネルへファイルシステムのマウントの仕方を知らせる ユーザー-tunable properties が入ります。このデータセットは mount 引数構造体として知られるものにカプセル化され、 そして、大抵、ファイルシステム名の後ろに _args 文字列が付加された名前がつけられます。

この構造体は、ユーザーランドとカーネルの連絡にのみ 使われることを覚えておきましょう。情報を渡す呼び出しが一度終われば、 カーネル側で廃棄されます。

カーネルとユーザーランドが常に同じフィールド配置および同じサイズを用いるよう 確実に行うために、引数構造体はバージョン付けされます。 オブジェクトのとっても頭の位置にフィールドを入れ込み、 そのバージョンを保持させる事で成し遂げられます。

例えば、仮想ファイルシステムを想像してみましょう — ディスク上に 蓄えられていないものを; 現実の (そして非常に現実に近い) code は、 tmpfs のところで見えます。それの mount 引数構造体は、 ルートディレクトリーの ownership あるいは ファイルシステムが保持できるファイルの最大数 を記述でき:

#define EGFS_ARGSVERSION 1
struct egfs_args {
        int ea_version;

        off_t ea_size_max;

        uid_t ea_root_uid;
        gid_t ea_root_gid;
        mode_t ea_root_mode;

        ...
}

2.5.2.<240>The mount utility

XXX: 書かれるべきモノっす。ユーザーランドのマウントユーティリティーがどう動くのか、 わずかに記述されます。

2.5.3.<240>The fs_mount operation

ユーザーがユーザーランドから mount コマンドを発行する度に fs_mount 操作が呼ばれます。 それは次のプロトタイプで:

int vfs_mount( mp, <240>
<240> path, <240>
<240> data, <240>
<240> ndp, <240>
<240> p); <240>
struct mount *mp;
const char *path;
void *data;
struct nameidata *ndp;
struct proc *p;
<240>

常にカーネルである呼び出し者が、 struct mount オブジェクトをセットアップし、 mp パラメーターを通して このルーチンに渡します。 data パラメーター中の mount 引数構造体 (Section<240>2.5.1, “Mount call arguments” として見られる) もまた渡します。 幾つか他の引数がありますが、この時点では重要ではありません。

mp->mnt_flag フィールドは、 何が行われる必要があるかを示します(この操作は semantically overloaded であることを思い出しましょう)。以下は、この関数が行う全タスクの 概略と、および、また、 mnt_flag フィールドに 可能なフラグの記述で:

  1. もし mp->mnt_flagMNT_GETARGS フラグがセットされていたら、 操作は与えられたマウントポイントの カレント mount パラメーターを返します。

    Section<240>2.5.3.1, “Retrieving mount parameters” に更なる詳細があります。

  2. copyin(9) を用いて、 ユーザーランドからカーネル空間に mount 引数構造体をコピーします。

    Section<240>2.5.3.2, “Getting the arguments structure” に更なる詳細があります。

  3. もし mp->mnt_flagMNT_UPDATE フラグがセットされていたら、 操作は、与えられた新しい引数に基づいて、与えられた マウントポイントの current mount パラメーターを updates します (例,リードオンリーモードから read-write への upgrade)。

    Section<240>2.5.3.3, “Updating mount parameters” に更なる詳細があります。

  4. この時点で、 MNT_GETARGSMNT_UPDATE もセットされていなければ、 操作は新しいマウントポイントをセットアップします。

    Section<240>2.5.3.4, “Setting up a new mount point” に更なる詳細があります。

2.5.3.1.<240>Retrieving mount parameters

mp->mnt_flagMNT_GETARGS フラグ付きで fs_mount 操作が呼ばれた時は、 ルーチンは mount 引数構造体を作成し、与えられた マウントポイントのデータに基づき満たし、 copyout(9) の利用でユーザーランドに戻ります。

これはファイルシステムに非常に依存していますが、しかし 以下のシンプルな例を考えると:

if (mp->mnt_flag & MNT_GETARGS) {
        struct egfs_args args;
        struct egfs_mount *emp;

        if (mp->mnt_data == NULL)
                return EIO;
        emp = (struct egfs_mount *)mp->mnt_data;

        args.ea_version = EGFS_ARGSVERSION;

        ... ここで args 構造体を満たし ...

        return copyout(&args, data, sizeof(args));
}

2.5.3.2.<240>Getting the arguments structure

fs_mount 操作に与えられた data 引数は、 ユーザー空間の memory region を指します。 そのため、安全なアクセスを可能にするために、まず最初に copyin(9) を使って、 カーネル空間中にコピーする必要があります。

ちょっとした例がここに:

int error;
struct egfs_args args;

if (data == NULL)
        return EINVAL;

error = copyin(data, &args, sizeof(args));
if (error)
        return error;

if (args.ea_version != EGFS_ARGSVERSION)
        return EINVAL;

2.5.3.3.<240>Updating mount parameters

mp->mnt_flagMNT_UPDATE フラグ付きで fs_mount 操作が呼ばれる時、 ルーチンは、 mount 引数構造体に与えられた 新しいパラメーターを基に 与えられたマウントポイントの current パラメーターを変更します。

2.5.3.4.<240>Setting up a new mount point

もし、 fs_mount を呼ぶときに、 MNT_GETARGSMNT_UPDATEmp->mnt_flag にセットされてなければ、 操作は新しいマウントポイントを設定します。 言い換えると: 正しいデータで 与えられた mp の中身で struct mount オブジェクトを満たします。

通常行う一番最初の事は、 マウントポイントを定義する構造体の割り当てについてです。この構造体は ファイルシステム名の後ろに _mount 文字列を付加した名前をつけ、そして大抵、 mount 引数構造体に 非常に似ています。一度割り当てされ、適切なデータで満たされると、 オブジェクトは、それの mnt_data フィールドによって、 マウントポイントに attached されます。

後に、 vfs_getnewfsid(9) 関数を用いてマウントポイントのセットアップのために ファイルシステム識別子を取得し割り当てます。

最後に、 set_statvfs_info 関数を 用いる事で、マウントポイントのあらゆる statvfs-related 情報を セットアップします。

これは、シンプルなコードの例をみることで、 all clearer で:

emp = (struct egfs_mount *)malloc(sizeof(struct egfs_mount), M_EGFSMOUNT, M_WAITOK);
KASSERT(emp != NULL);

/* ファイルシステム依存値で emp 構造体を満たす。 */
emp->em_root_uid = args.ea_rood_uid;
... more comes here ...

mp->mnt_data = emp;
mp->mnt_flag = MNT_LOCAL;
mp->mnt_stat.f_namemax = MAXNAMLEN;
vfs_getnewfsid(mp);

return set_statvfs_info(path, UIO_USERSPACE, args.ea_fspec, UIO_SYSSPACE, mp, p);

2.5.4.<240>The vfs_unmount function

ファイルシステムのアンマウントは大抵マウントより簡単で、 加えて、アンマウントするには、ファイルシステム依存のユーザーランド ユーティリティーを書く必要がありません。これは fs_unmount 操作で完了し、つぎの signature で:

int fs_unmount( mp, <240>
<240> mntflags, <240>
<240> p); <240>
struct mount *mp;
int mntflags;
struct proc *p;
<240>

関数の outline は以下に similar で:

  1. 与えられたマウントポイントの未解決の I/O 全てを finalize するように カーネルに頼みます。 これは vflush(9) 関数を通じて行われます。註として、その最後の引数はフラグ bitfield で、 ファイルシステムを強制的にアンマウントするなら FORCECLOSE フラグが carry されなければ なりません — 言い換えると、 MNT_FORCE フラグが mntflags 中にセットされていたならば。

  2. マウントポイントに attached された全資源を解放する — すなわち、 mp->mnt_data で指された mount 構造体を。これは ファイルシステム内部に heavily に依存します。

  3. ファイルシステム特有 mount 構造体を Destroy し、 mp マウントポイントから detach します。

ここに前の outline の単純な例が:

int error, flags;
struct egfs_mount *emp;

flags = (mntflags & MNT_FORCE) ? FORCECLOSE : 0;

error = vflush(mp, NULL, flags);
if (error != 0)
        return error;

emp = (struct egfs_mount *)mp->mnt_data;
... free emp contents here ...

free(mp->mnt_data, M_EGFSMNT);
mp->mnt_data = NULL;

return 0;

2.6.<240>File system statistics

statvfs(2) システムコールは、 マウントされたファイルシステムについて、 ブロックサイズ、使用中ブロック数、等のような統計情報を引き出すのに使われます。 これはファイルシステムドライバーに fs_statvfs 操作として実装されていて、 そのプロトタイプは:

int fs_statvfs( mp, <240>
<240> sbp, <240>
<240> p); <240>
struct mount *mp;
struct statvfs *sbp;
struct proc *p;
<240>

この操作の実行 flow は全く単純で: 基本的に、 sbp のフィールドを適切なデータを fills。 このデータは、ファイルシステムの現在の status から導き出せます。 — 例, mp->mnt_data の内容を通じて。

興味深い註として、この操作によって返される幾つかの情報は、 全ファイルシステムで共有される mp 構造体の一般部分に蓄えられます。 copy_statvfs_info. 関数***<関数名後.付き?>*** が この一般情報を resulting 構造体の中に最小努力でコピーしてくれます。 数ある中で、ファイルシステムの識別子、 the number of writes 、 ファイル名の最大長、等をコピーします。

一般的な経験則として、 fs_statvfs のコードは sbp 構造体の以下のフィールドを manually 初期化し: f_iosizef_frsizef_bsizef_blocksf_bavailf_bfreef_bresvdf_filesf_favailf_ffree および f_fresvd 。各フィールドについての詳細な情報は statvfs(2) にあります。

例えば、操作の content はこのようなもので:

... 上に書かれたように sbp のフィールドを満たす ...

copy_statvfs_info(sbp, mp);

return 0;

2.7.<240>vnode management

2.7.1.<240>vnode's life cycle

vnode は、ほかのあらゆるシステムオブジェクトのように、 利用される前に割り当てられる必要があります。同様に、 unused なとき、 released および deallocated されることが必要です。 vnode の取り扱いを するには、いくらか事が特殊です。よって、この節全体をその説明に割きます。

XXX: この場所にグラフがあると excellent[一流] やなぁ。

vnode は、 getnewvnode(9) 関数に よって first 生き返らせます; これは、ファイルを表現する のに使われる clean な vnode を returns します。この新 vnode はまた、 used と印され、 inactive と印されるまで存続されます。 vnode は last の参照を解放する呼び出しによって inactivated されます。 これが起こると、 VOP_INACTIVE がその vnode のために呼ばれ、その vnode は free list に載っかります。

free list 、その混乱させる名前にも 関わらず、 real で live 、しかし現在使われていない vnodes を入れています。 それは a big LRU list みたいです。 vnodes は vget(9) 関数を用いる事で、この list から再び生き返らせることができ、 これが起こったときは、 free list を去り、そして、 それらが inactivated されるまでは再び利用されていると marked されます。 だいたい、なぜこの list が存在するの? 例えば、 /usr をパス lookups する必要のある全コマンド について考えてみましょう。 /usr/bin/usr/sbin/usr/pkg/bin および /usr/pkg/sbin 内のどれもが /usr vnode が必要です。もし毎回、 scratch から 再生成されなければならないとしたら、遅いでしょう。よって free list に kept around on しています。

free list 上の vnodes はまた reclaimed でき、この意味はそれらは効果的に killed されると言う事です。このどちらも起こり得て、 vnode は新 vnode に 再利用される(getnewvnode を通して) あるいは shut down されるからです (例, revoke(2) のため)。

註として kern.maxvnodes sysctl(9) node は一度にいくつの vnodes を active に kept できるか指定します。

2.7.2.<240>vnode tags

vnodes は、それらの型を識別するのに tagged 。それらに attached された tag はカーネル中で使ってはいけなくて; それは、 (pstat(8) のような) ユーザーランドアプリケーションに vnodes についての情報を表示させるためだけに提供されます。

註として、 dynamically loadable modules から 拡張可能ではないため、それの利用法は反対されています。しかしながら、 それらは現在使われているため、各ファイルシステムはその固有[own] vnodes を記述するためのタグを定義します。これらのタグは src/sys/sys/vnode.h および vnode(9) にあります。

2.7.3.<240>Allocation of a vnode

vnodes 割り当てには3つのシナリオがあり:

  • 既存ファイルへのアクセス: Section<240>2.9.2, “The lookup algorithm” に記されているように カーネルがファイル名 lookup する。 vnode lookup 操作が match するものを見つけたとき、 選ばれたファイルの vnode を割り当て、 システムにそれを returns します。

  • 新ファイルの作成: 新ファイルの生成が成功した後、 ファイルシステム特有コードが新 vnode を割り当て、 そして、ファイルシステム一般コードにそれを returns します。これは vnode 生成、 mkdir 、 mknod および symlink 操作 の結果として起こります。

  • NFS ファイル handle を通したファイルへのアクセス : ファイルシステムが NFS ファイル handle から fhtovp vnode 操作を通した vnode への変換を 頼まれた時、ファイルを表現するのに new vnode の割り当てが必要かも しれません。 See Section<240>2.14, “NFS support”.

vnodes はファイル毎にユニークである事を思い出すことは大切です。 single 物理ファイルに対して一つを越える vnode を割り当てを防止するために   特別な配慮がとられています。各ファイルシステムにはこれを成し遂げるために 独自の方法があります; 例として、 tmpfs は、ファイルシステム nodes と vnodes の間の map を、前者をそのキーとして keeps します。

しかしながら、どうか注意していただく事として、ファイルには in-core 表現が無い (すなわち、 vnode 無し) かも知れないと言う事です。 active および inactive で、 まだ-reclaimed-されていない ファイルだけが vnode で表現されます。

vnode allocation を解説する単純な例は src/sys/fs/tmpfs/tmpfs_subr.ctmpfs_alloc_vp 関数に見られます。

XXX: わしが考えるにぃー、 fs_vget はこの節に技術されるべきでよー。

2.7.4.<240>Deallocation of a vnode

vnodes を deallocate する手順は大抵 trivial で: vnode に attached されてる かもしれない何らのファイルシステム固有情報も generally[あまねく] cleans up します。

their underlying nodes から vnodes を detached し、および destroyed する ことができるコード中の たった一つの 場所 であることを覚えて置いてください。この場所は vnode reclaim 操作内にあります。 vnode はまだ active か reusable かも 知れないので、他の場所から行う事は、確実に更なるトラブルを引き起こします (see Section<240>2.7.1, “vnode's life cycle”) 。

註として、 reclaim vnode 操作から抜ける前に v_data ポインターは null にセットされて なければならなくて、さもないと、 vnode が適切に cleaned されてない為、 システムは不平を垂れます。

この関数は、もし必要なら、また、 underlying real node の releasing を担当しています。例えば、ファイルが deleted された時、 対応する vnode 操作 が実行され — delete あるいは rmdir し — しかし、 vnode は reclaimed されるまで released されません。 これが意味する事は、もしこれが起こる前に real node が deleted されると、 vnode は無効な メモリー area を指したままになるでしょう。

次の操作サンプルを考えると:

int
egfs_reclaim(void *v)
{
        struct vnode *vp = ((struct vop_reclaim_args *)v)->a_vp;

        struct egfs_node *node;

        node = (struct egfs_node *)vp->v_data;

        cache_purge(vp);
        vp->v_data = NULL;
        node->en_vnode = NULL;

        if (node->en_nlinks == 0)
                ... free the underlying node ...

        return 0;
}

しかしながら、 releasing (inactive と印された) vnode は reclaiming のものと同じでないことを心に留めて置いてください。 real reclaiming は明示的に要求された物のほかは、大抵、ずっと後に起こります。 ディスクからファイルを remove する操作は大抵、わざと reclaim コードを 実行し、それで、 vnode およびそれに連携するディスク空間 はできるだけ早く released 。 これは vrecycle(9) 関数を用いる事で完了できます。

例として:

int
egfs_inactive(void *v)
{
        struct vnode *vp = ((struct vop_inactive_args *)v)->a_vp;

        struct egfs_node *node;

        node = (struct egfs_node *)vp->v_data;

        if (node->en_nlinks == 0) {
                /* ファイルはディスクから deleted されました; 
                 * free its 物理空間するためにできるだけ早く reclaim it 。 */
                vrecycle(vp, NULL, p);
        }

        return 0;
}

2.7.5.<240>vnode's locking protocol

vnodes は、他のシステムオブジェクトの殆ど全てのように、それに連携した アクセス干渉およびデッドロックを防止するために、 locking protocol があります。これは 2つのシナリオで現れるかも知れなんで:

  • uniprocessor システムでは: vnode 操作は操作を完了する前に returns するので、従って、 vnode の lock は無関係な変更を 操作の終了まで妨げます。ほとんどのファイルシステムは 非同期なのでこれが起こります。

    例えば: read 操作はファイルの read を準備し、 launches し、 read を要求したプロセスを sleep に至らせ、そして他のプロセスに実行を 譲ります。いくらかの時間が経過した後、ディスクは要求されたデータで 応答し、それが original プロセスに返され、そしてプロセスは目覚めます。 システムはプロセスが眠っている[sleeping]間、 vnode が 変化をこうむらないように保証しなければなりません。

  • multiprocessor システムでは: 異なる二つの CPU が同時に同じファイルに アクセスしたくて、このように、それにたどり着くには 同じ vnode を通り抜けることが必要とします。そのうえ、 システムででも同じ問題が見え、またここでも見えます。

各 vnode 操作は従わねばならない特有の locking contract があり、 大抵、他の操作と異なります (これは プロトコルを非常に複雑にし、当然簡単化すべきであります)。 これらの contracts[制約?]は vnode(9) および vnodeops(9) に記されています。 tmpfs のコードには断定された form で見つかり、 論理的表記法で明示されたものが見たいことでしょう。

vnode 操作に関しては、各ファイルシステムは vnode layer に locking primitives を実装します。 これらの primitives は vnode を lock (vop_lock) 、 unlock (vop_unlock) することを許し、そして それが locked されているかどうかを試し (vop_islocked) ます。 Given that これらの 操作は全ファイルシステムで共通で、 genfs 擬似ファイルシステム は custom な関数を書いて用いる代わりに関数のセットを提供します。これらは genfs_lockgenfs_unlock および genfs_islocked で、 非常に rare cases を除いて、これらが常に使われます。

非常に重要な註として、 vop_lock は決して 直接使われません 。 代わりに、 vn_lock(9) 関数を vnodes の lock に使います。 Unlocking は、しかしながら、 vop_unlock の担当です。

2.8.<240>The root vnode

Section<240>2.9, “Path name resolution procedure” に記述されているように、カーネルは 全パス名を反復法で lookups します。これが意味する事は、 マウントポイント中のいかなるファイルにたどり着くには、 マウントポイントそれ自身を最初に traverse する必要があります。 言い換えると、マウントポイントは、システムが経由してファイルシステムに アクセスできる唯一の場所であり、そして、従って、 それは resolve できる必要があります。

これを成し遂げるためには、各ファイルシステムは それの root node を表す vnode を返す fs_root hook を提供します。 この関数のプロトタイプは:

int fs_root( mp, <240>
<240> vpp); <240>
struct mount *mp;
struct vnode **vpp;
<240>

2.9.<240>Path name resolution procedure

XXX 概論 書き やあ〜。

2.9.1.<240>Path name components

パス名 component は 完全パス名の 不可分の部分で — slashes (/) character を含まないものです。 一つ以上の slashes を含むいかなるパス名も 2つ以上の atoms に分割できます。

パス名 components は ( src/sys/sys/namei.h で定義された) struct componentname オブジェクトで表され、 幾つかの vnode 操作で heavily に使われます。その最も重要な フィールドは以下で:

  • cn_flags: element を記述する bitfield 。 このオブジェクトが有効なパス名 buffer (下の cn_pnbuf フィールドを御覧ください) を保持する事を示す HASBUF フラグが 特に興味深いものです。

  • cn_pnbuf: 完全パス名を保持するバッファーへのポインター。これは cn_flags bitfield に HASBUF フラグがあるときだけ有効です。

    ほとんどの状況で、このバッファーはシステムによって自動的に allocated および deallocated 、しかしこれは常に true ではありません。 時々、幾つかの vnode 操作それ自身に よって free される事が必要で; vnodeops(9) に これについての更なる詳細があります。

  • cn_nameptr: cn_pnbuf 中のポインターで、 このオブジェクトが記述するパス名 componentの始まりを指定するもの。 常に cn_namelen と共に使わなければなりません。

  • cn_namelen: このパス名 component の長さで、 cn_nameptr から開始する。

2.9.2.<240>The lookup algorithm

パス名解決 (あるいは lookup a パス名) の意味は、 あらかじめ指定された絶対か相対のパス名に基づいて uniquely に表現する vnode を get することです。

NetBSD カーネル はパス名の解決に 2つの level の反復アルゴリズムを使います。 最初の level はファイルシステム独立で、 namei(9) 関数によってがんばられて、 ところが一方 second one はファイルシステム内部の詳細を頼り、 lookup vnode 操作を通して成し遂げられます。

次のリストは lookup アルゴリズムを説明します。簡単化のために、 多くの詳細を省きますが; 見つからない情報は namei(9) and vnodeops(9) に全て 入っていまして:

XXX: <wrstuden> この記述は簡単化し過ぎると思います。 あなたは lookup() を省いていて、 lookup() で行われたとき、しかるべき動作を namei() によるものとされています。 簡素にしておこうという試みは良いのだけども、 namei() および lookup() 共に 記述が必要だと思う。 lookup() は パス名 をとり vnode に換え、 そして、 namei() は結果を取り、シンボリックリンク解決を handles します。

XXX: <jmmv> 今 lookup() および namei() の内部はあまり解らないので それでこのドキュメントでは簡単な記述のまま置いておきます、一時的に。

  1. namei は、 ( Section<240>2.9.1, “Path name components” に 記述された struct componentname 型の) cnp パス名 component で構成されて いて; それのバッファーは look for するための 完全パス名を保持します。 component のポインターは パス名の最初の component を記するために補正されます。

  2. namei 操作は lookup の開始点 (常にディレクトリー) の vnode を gets します。 絶対パス名には、これはルートディレクトリーの vnode で、 相対パス名には、呼び出し元ユーザーランドプロセスでの current working ディレクトリーの vnode です。

    この vnode は一般的に directory vnode pointer という意味の dvp と呼ばれます。

  3. nameidvp vnode 上の vnode lookup 操作を 呼び出し、与えられたディレクトリーから始め、 解決されなければならない component がどれか (cnp) を教えます。

  4. もし component がディレクトリーにあれば、 vnode lookup 操作は、それの entry 各自の vnode を返さなければなりません。

    しかしながら、もし、 component がディレクトリー内に無ければ、 lookup は適切なエラーコードを返して fail します。 reported しなければならないエラー conditions は他に幾つかあって、 適切なもの全部が vnodeops(9) に記述されています。

  5. 更なる components を look for する場合のみ、 namei は返された vnode を指すよう dvp を更新し、そして、 cnp を次の component に advances[進め] ます。その場合、手続きは 3 から続行されます。

    look for する components がこれ以上ない場合、 namei は it located な last entry の vnode を返します。

この two-level lookup mechanism の背後には幾つかの理由がありますが、 簡単化のために後回しにします。 XXX: 4.4BSD 本はその全てを与え; そこにリンクするか、 これらをここに我々自身の言葉で説明するべき (後者を選ぶべき)。

2.9.3.<240>Lookup hints

lookup algorithm に渡された引数の一つは実行する lookup の種類を指定する hint です。この hint は lookup がファイル creation (CREATE) のためか、 deletion (DELETE) のためか、あるいは name change (RENAME).のための物かどうかを指定します。 ファイルシステムは、これらの hints を 対応する操作のスピードアップのために使い — 一般的には、 後の real 操作で処理する間に使われる幾つかの値をキャッシュするためです。

例えば、与えられたファイル名を delete する目的の unlink(2) システムコールを考えましょう。この操作は、 ファイルの存在を保証し、その vnode を get するために lookup を issues ます。 この方法で、それは vnode の remove 操作を呼ぶ事ができます。 これまではこれでよいです。さて、操作それ自身がファイルを delete しなければ なりませんが、しかし、ファイルの removing が意味するのは、特に、 それが入っているディレクトリーから detaching it 。 remove 操作はどうやって removed されるファイルを指すディレクトリー entry をアクセスするの? 明らかに、 it can do another lookup and traverse a potentially long ディレクトリー。しかしこれは本当に必要ですか?

unlink(2) は 最初に removed される entryの vnode を got することを思い出してください。 これは lookup をしている事を含意し、それの entry を looking for[さがす] ファイルの親ディレクトリーを traversed[横切り]ます。 The algorithm が一旦 entry にたどり着くと、一度 vnode 操作地震に ある処理を繰り返す必要はありません。

上の situation では、 second lookup は lookup 操作が実行されている間に 影響を受けたディレクトリー entry をキャッシュした事により無効化されます。 これは DELETE hint が与えられたときだけ 行われます。

同じ situation がファイル生成 (なぜなら、新 entries がディスク上ファイル システムの、以前に deleted された entries を上書きするかも知れないから)、 あるいは名前変更 (操作は 連携したディレクトリー entry を変更する必要があるから)で発生します。

2.10.<240>File management

XXX: 序説書かれたし。

2.10.1.<240>Creation of regular files

XXX: 書かれるべき。 vop_create を記述せよ。

2.10.2.<240>Creation of hard links

XXX: 書かれるべき。 vop_link を記述せよ。

2.10.3.<240>Removal of a file

XXX: 書かれるべき。 vop_remove を記述せよ。

2.10.4.<240>Rename of a file

XXX: 書かれるべき。 vop_rename を記述せよ。

2.10.5.<240>Reading and writing

vnodes には、システムコールから呼ばれ、それらからデータを読む操作 (vop_read) 、および、それらにデータを書く操作 (vop_write) があり、それぞれ 、 read(2) および write(2) です。 read 操作では、 どこから read が開始されるかのオフセット、 read されるバイト数を指定する総数(長さ)、 およびデータが入れられるバッファー、を受け取ります。 同じく、 write 操作は、どこから write が開始されるかのオフセット、 write するバイト数、 どこからデータを read するかのバッファー、を受け取ります。

ファイルをメモリー中に maps し、そして ユーザーランドに mapped されたメモリー region の direct access を提供する mmap(2) システムコールもあります。

2.10.5.1.<240>uio objects

struct uio 型は、2つの異なるバッファー間の データ転送を記述します。その一つは uio オブジェクトの中に stored 、 そして他方は外部 (大抵ユーザーランド空間) です。 これらのオブジェクトは新しいデータ転送が 開始されるときに作成され、そして、転送が完全に終了する迄 alive し; 言い換えると、それらは固有の 転送を identify します。

以下は struct uio の最も重要なフィールドの記述です (その動作の基本的な理解に必要なもの)。完全なリストは、 uiomove(9) を御覧ください。

  • uio_offset: 転送をどこから始めるのかの、 ファイル中のオフセット。もし、転送が read なら、 オフセットはファイルサイズ limits の範囲内に無ければならず; もし write なら、ファイル終端を越えて伸ばせます — ファイルが拡張された場合。

  • uio_resid ( residual count としても 知られる): このオブジェクトへ転送される残りのバイト数。

  • 読み書きがされるデータのバッファーへのポインターのセット。 これらは直接使われず、故に、その名は省かれます。

  • データが uio オブジェクトに記されたバッファーに対して読み書きされるべきか を示すフラグ。

これは、小さな例で論ずるほうが簡単かもしれません。 次のユーザーランドプログラムを考えると:

char buffer[1024];
lseek(fd, 100, SEEK_SET);
read(fd, buffer, 1024);

read(2) システムコールは、 100 bytes のオフセットと、 1024 バイトの残量カウント、を含む uio オブジェクトを組み立て、 buffer を指す uio のバッファーを作り、 そして、それらをデータの target としてしるし付けます。 もしこれが write 操作であったら、 uio オブジェクトのバッファーは データのソースかも知れません。

uio オブジェクト管理を単純化するために、カーネルは uiomove(9) 関数を提供し、その signature は:

int uiomove( buf, <240>
<240> n, <240>
<240> uio); <240>
void *buf;
size_tn;
struct uio *uio;
<240>

この関数は、 buf で指されたカーネルバッファーと、 uio instance で記されたアドレスの中の間で、 n bytes をコピーします。 転送が成功すると、 uio オブジェクトが更新され、 それで uio_resid がコピーされたデータ量の分 減らされ、 uio_offset が同量増やされ、 そして、内部 buffer ポインターがそれに応じて更新されます。 これは、転送が完了するまで (例, loop 中から) 繰り返し uiomove 呼び出しするのを簡単化します。

2.10.5.2.<240>Getting and putting pages

Section<240>2.10.5.1, “uio objects” で見られたように、データ転送は 高レベルのオブジェクトで記述されていて、 ファイルシステム基底の詳細を考慮に入れません。もっとはっきり書くと、 特定のディスク上ブロック構成に頼っていません。 (殆どのディスク上ファイルシステムは、データをディスク全体に散在して 蓄えている事を思い出してください(fragmentation のため); それゆえ、 転送は、適切なディスクブロックに対し読み書きするために、 データを断片に分割する必要があります。)

断片に分割した転送をディスクに要求し、そしてその結果を扱うのは、 (非常に) 複雑な操作です。 幸いにも、 UVM メモリーサブシステム (see Section<240>1.1, “The UVM virtual memory manager”) が task 全体を簡単化します。 各 vnode はそれに連携した (Section<240>1.1.1, “UVM objects” に記述された) struct uvm_object があり, backed by a vnode.

vnode はそれの vop_getpages および vop_putpages 操作 を通して、 uobj を backs up します。 これらの2つの操作は非常に一般的で (メモリーページ管理の視点から)、 genfs はそれらを実装するために、2つの汎用関数を提供します。 これらは genfs_getpages および genfs_putpagesで、 いかなるディスク上ファイルシステムでも好都合です。 ファイルシステム特有の詳細な取り扱い方については、 Section<240>2.10.5.5, “Reading and writing pages” に何らかの詳細があります。

2.10.5.3.<240>Memory-mapping a file

NetBSD の particular UBC 実装のおかげで ( Section<240>2.10.5.2, “Getting and putting pages” を御覧ください)、ファイルは メモリー内に自明に mapped できます。 mmap(2) システムコール が これを成し遂げるために使われ、カーネルは ファイルシステムから独立してそれを handles します。

VOP_MMAP method が vnode が memory-mapped される 事についてファイルシステムに告げるためだけに使われ、もしそれが mapping を 起こさせたらファイルシステムに訊ねます。

ファイルが memory-mapped された後、ファイルシステム I/O は vnode pager を通して UVM によって handled され、 ends up in vop_getpages および vop_putpages 。 ある意味で、これはとてもとても regular reading および writing のようで、 しかし、 vop_read および vop_write の明示的な呼び出しの 代わりに、 uiomove を使い、メモリー window は直接 アクセスされます。

2.10.5.4.<240>The read and write operations

NetBSD では几帳面な UBC 実装のおかげで (see Section<240>2.10.5.2, “Getting and putting pages”)、 vnode の 読み書き操作 (それぞれ vop_read および vop_write) は非常に simple で、 仮想メモリーだけを扱うからです。基本的に、必要な事は ファイルの影響を受ける部分の memory-map だけで、そしてそれから simple メモリーコピー操作を issue 。

例として、次の read code 例を考えると:

int
egfs_read(void *v)
{
        struct vnode *vp = ((struct vop_read_args *)v)->a_vp;
        struct uio *uio = ((struct vop_read_args *)v)->a_uio;

        int error;
        struct egfs_node *node;

        node = (struct egfs_node *)vp->v_data;

        if (uio->uio_offset < 0)
                return EINVAL;

        if (uio->uio_resid == 0 || uio->uio_offset >= node->en_size)
                return 0;

        if (vp->v_type == VREG) {
                error = 0;
                while (uio->uio_resid > 0 && error == 0) {
                        int flags;
                        off_t len;
                        void *win;

                        len = MIN(uio->uio_resid, node->en_size -
                            uio->uio_offset);
                        if (len == 0)
                                break;

                        win = ubc_alloc(&vp->v_uobj, uio->uio_offset,
                            &len, UBC_READ);
                        error = uiomove(win, len, uio);
                        flags = UBC_WANT_UNMAP(vp) ? UBC_UNMAP : 0;
                        ubc_release(win, flags);
                }
        } else {
                ... left out for simplicity (if needed) ...
        }

        return error;
}

2.10.5.5.<240>Reading and writing pages

Section<240>2.10.5.2, “Getting and putting pages” で見られたように、 genfs_getpages および genfs_putpages 関数は、 ディスク上のファイルシステムのほとんどには十分です。しかし、もしそれらが abstract[抽象的なら]、各ファイルシステム特有の詳細をどう取り扱うの? 例,もしシステムが /foo/bar ファイルの 3番目のページを取得したいとすると、要求されたページをメモリーに 持ってくるために read しなければならないディスク上のブロックがどこか どうやって知るの? real transfer はどこに場所を取るの?

メモリーページおよびディスクブロックの間の mapping が vnode の bmap 操作によって完了したら、 paging 関数によって vop_bmapが呼ばれます。 これは、アクセスされるべきファイルの論理ブロック番号を受け取り、 それを内部の、ファイルシステム特有のブロック番号に変換します。

bmap がアクセスされるべき物理ブロック番号を返すやいなや、 generic page handling 関数はブロックが既にメモリー上にあるか否かを チェックします。もしなければ、転送は vnode の strategy 操作 (vop_strategy) によって行われます。

これらの操作に関する更なる情報は vnodeops(9) manual page にあります。

2.10.6.<240>Attributes management

NetBSD カーネル内部では、ファイルはそれに連携した標準であり 見慣れた attributes セットを持っています。これらは:

  • 型: ファイルが通常ファイル (VREG) 、ディレクトリー (VDIR) 、シンボリックリンク (VLNK) 、特殊デバイス (VCHR あるいは VBLK) 、ネームドパイプ (VFIFO) あるいは socket (VSOCK) かどうかを指定する。 ここで mentioned 定数は vnode 型で、ファイルシステム中の ファイルの内部の型表現と match する必要はありません。

  • An ownership: user id および group id 。

  • アクセスモード。

  • フラグのセット: これらは、 immutable フラグ、 append-only フラグ、 archived フラグ、 opaque フラグおよび nodump フラグを include 。更なる情報は chflags(2) を御覧ください。

  • A hard link count.

  • 時刻のセット: これらは 生成時刻、 change 時刻、 アクセス時刻および modification 時刻です。更なる詳細は Section<240>2.10.7, “Time management” を御覧ください。

  • サイズ: ファイルの正確なサイズ、バイト単位で。

  • デバイス番号: 特殊デバイス (キャラクターデバイスまたは ブロックデバイス)の場合、その番号も stored 。

NetBSD カーネルはこれらの attributes 全てをコンパクトな方式で扱うために struct vattr 型 (詳細は vattr(9) に) を使います。 このセットに基づいて、各ファイルシステムは概して、 それの node 表現構造体内のこれらの attributes をサポートします (それらが架空で、アクセス時に捏造されたものでなければ)。 例えば、 FFS はそれらを inodes に store できるのに対して、 FAT は これらのうちの幾つかだけ save できて、他 (ownership のような)は run time 時に fake だけども。

それの vnode 型を VNON にセットし、そして それのほか全てのフィールドに有効な値を持たない事を示す VNOVAL をセットする、 VATTR_NULL マクロの利用によって、 struct vattr instance は初期化されます。 このマクロの使用後、 caller に対して、要望された全てのフィールドに 正しい値がセットされる事の責任を負います。 オブジェクトの利用者は、それらのフィールドに unset (VNOVAL) を持たしたものを 決して使ってはなりません。

興味深い註として、ファイル ownership 、そのモード、等をセットするのに使われる 正規のregular システムコールに match する vnode 操作はありません。 代わりに、 nodes は attribute 全体をセットする作用をする 2つの操作を提供し: それらを read するための vop_getattrs および それらを set するための vop_setattrs です。 この節の残りはそれらを記述します。

2.10.6.1.<240>Getting file attributes

vop_getattr vnode 操作は 与えられた vnode から全標準 attributes を取得します。それがする事とは 与えられた struct vattr 構造体を正しい値で埋めることです。例えば:

int
egfs_getattr(void *v)
{
        struct vnode *vp = ((struct vop_getattr_args *)v)->a_vp;
        struct vattr *vap = ((struct vop_getattr_args *)v)->a_vap;

        struct egfs_node *node;

        node = (struct egfs_node *)vp->v_data;

        VATTR_NULL(vap);

        switch (node->en_type) {
        case EGFS_NODE_DIR:
                vap->va_type = VDIR;
                break;
        case ...:
        ...
        }
        vap->va_mode = node->en_mode;
        vap->va_uid = node->en_uid;
        vap->va_gid = node->en_gid;
        vap->va_nlink = node->en_nlink;
        vap->va_flags = node->en_flags;
        vap->va_size = node->en_size;
        ... continue filling values ...

        return 0;
}

2.10.6.2.<240>Setting file attributes

vop_getattr 操作に似て、 vop_setattr は一度に ファイル attributes のサブセットをセットします。 VNOVAL でない attributes だけが 変更されます。そのうえ、 caller が設定できない値に設定を試みないよう、操作が請け負って ; 例えば、ファイル型は set (すなわち、 change) できません。

特別興味深い事は、ファイルのサイズは attribute として変更できる事です。 言い換えると、この操作は ファイル truncation calls の entry point で、適切な時に vop_truncate を呼ぶ責任を持ちます。 システムは決して、 vnode の truncate 操作を直接呼びません。

小さな sketch:

int
egfs_setattr(void *v)
{
        struct vnode *vp = ((struct vop_setattr_args *)v)->a_vp;
        struct vattr *vap = ((struct vop_setattr_args *)v)->a_vap;
        struct ucred *cred = ((struct vop_setattr_args *)v)->a_cred;
        struct proc *p = ((struct vop_setattr_args *)v)->a_p;

        /* 設定できない値の設定を許さない。 */
        if (vap->va_type != VNON || vap->va_nlink != VNOVAL || ...)
                return EINVAL;

        if (vap->va_flags != VNOVAL) {
                ... ここで node フラグをセット ...
                if error, return it
        }

        if (vap->va_size != VNOVAL) {
                ... verify ファイル型 ...
                error = VOP_TRUNCATE(vp, size, 0, cred, p);
                if error, return it
        }

        ... etcetera ...

        return 0;
}

2.10.7.<240>Time management

各ノードは、それに連携した 4つの時刻を持ち、それら全ては struct timespec オブジェクトで表されます。 これらの時刻は:

  • Birth time: ファイルが born した時刻。 ファイルが作られた後、変更はできません。

  • Access time: ファイルが最後にアクセスされた時刻。

  • Change time: ファイルの node が最後に変えられた時刻。 例えば、もし既存ファイルに新しいハードリンクが作られたら、 その change time は更新されます。

  • Modification time: ファイルの内容が最後に変更された時刻。

これらの時刻は、 underlying ファイルへの最後のアクセスを反映し、 極度に何度も変更される必要があります。もし、これが synchronously に 行われると、ファイルの繰り返しのアクセスは big performance penalty を負わせる事になります。 これが、時刻更新が delayed 手法で行われる理由です。.

Nodes には通常、フラグのセットがあって (メモリーに保持されるのみで、 ディスクには決して書かれません)、行われる非同期の動作を知るために それらの状態を示します。これらのフラグは ファイルの時刻が更新されなければならないことを示すことの 他の事にも使われます。 それらはファイルが changed されるとすぐに set されますが、 実際には時刻は vnode の update 操作 (vop_update) が called まで modified されません; これについての更なる詳細は vnodeops(9) を御覧ください。

vop_update はカーネルによって、 時々、非同期に呼ばれます。しかしながら、ファイルシステムは希望のように わざと実行しがちで; 同期マウントがされた場面では変更が 起こるとすぐに時刻が更新されるでしょう。

2.10.8.<240>Access control

ファイルシステムは、 permission に関して要求が正当か否か、を 保証する事を担当しています。これは、 vnode の access 操作 (vop_access) で行われ、これは、 caller の credentials[信任状]および要求されたアクセスモードを受け取ります。 それから、これらが、アクセスしようとするファイルの現在の attributes と互換かどうか、操作はチェックします。

操作は通常この構造体に従い:

  1. もし、ファイルシステムが read only でマウントされていて、 caller が ディレクトリー、リンクや通常ファイルに書きたがっていたら、 アクセスは拒まなければなりません。

  2. もし、ファイルがimmutable[変更不可]で、 caller がそれに write しようと すると、 access is denied 。

  3. 最後に、 all remaining アクセス possibilities のチェックに vaccess(9) が使われます。 これはこの操作の沢山のコードを簡約化します。

例えば:

int
egfs_access(void *v)
{
        struct vnode *vp = ((struct vop_access_args *)v)->a_vp;
        int mode = ((struct vop_access_args *)v)->a_mode;
        struct ucred *cred = ((struct vop_access_args *)v)->a_cred;

        struct egfs_node *node;

        node = (struct egfs_node *)vp->v_data;

        if (vp->v_type == VDIR || vp->v_type == VLNK || vp->v_type == VREG)
                if (mode & VWRITE &&
                    vp->v_mount->mnt_flag & MNT_RDONLY)
                        return EROFS;
        }

        if (mode & VWRITE && mode->tn_flags & IMMUTABLE)
                return EPERM;

        return vaccess(vp->v_type, node->en_mode, node->en_uid,
            node->en_gid, mode, cred);
}

2.11.<240>Symbolic link management

2.11.1.<240>Creation of symbolic links

XXX: 書かれるべき。 vop_symlink の記述。

2.11.2.<240>Read of symbolic link's contents

XXX: 書かれるべき。 vop_readlink の記述。

2.12.<240>Directory management

ディレクトリーはファイル名をファイルシステム nodes に maps します。 ディレクトリーの内部表現はドッカリとファイルシステムに依存しますが、 しかし、 vnode layer はそれらをアクセスする抽象的な方法が提供しています。 これには、 vop_lookupvop_mkdirvop_rmdir および vop_readdir 操作が含まれます。

この節の残りでは、ディレクトリー entry を記述する、下のシンプルな struct egfs_dirent を想定します:

struct egfs_dirent {
        char ed_name[MAXNAMLEN];
        int ed_namelen;
        off_t ed_fileid;
};

2.12.1.<240>Creation of directories

XXX: 書かれるべき。 vop_mkdir の記述。

2.12.2.<240>Removal of directories

XXX: かかれるべき。 vop_rmdir の記述。

2.12.3.<240>Reading directories

vop_readdir 操作は、 ファイルシステム非依存の方法でディレクトリーの contents を reads 。 その全てが返すものがディレクトリー の exact contents だけども regular read 操作はこの目的のためにも使えることを思い出してください; これは、 portable を目指すプログラムでは使えません (この functionality をサポートしない幾つかのファイルシステムはいうまでもなく).

この操作は、オフセットからオフセット plus 転送長まで reads する 各ディレクトリー entry のための struct dirent オブジェクト (dirent(5) に見られる) を返します。 オブジェクト全体を read せねばならないため、オフセットは常に 物理的ディレクトリー entry 境界に aligned してなければならなく; さもなければ、関数はエラーを返さねばなりません。これは常に正しい訳では ありませんが、けれども: 幾つかのファイルシステムでは 可変-サイズの entries を持ち、 どの entry を read すべきかの決定に another metric を使います (such as its ordering index)。

重要な註として、結果として帰ってくる struct dirent オブジェクトのサイズは 可変で: 蓄えられている名前に依存します。故に、 code は最初に constructs これらのオブジェクト (その全フィールドを手作業で設定し) 、そして、それから _DIRENT_SIZE マクロをサイズ計算に使い、 のちに、 d_reclen フィールドに割り当てます。 例えば:

struct egfs_dirent de;
struct egfs_node *node;
struct dirent d;

... ディレクトリー entry をディスクから de に読み込み...
... make node point to the de.ed_fileid node ...

switch (node->ed_type) {
case EGFS_NODE_DIR:
        d.d_type = DT_DIR;
case ...:
...
}

d.d_namlen = de.ed_namelen;
(void)memcpy(d.d_name, de.ed_name, de.ed_namelen);
d.d_name[de.ed_namelen] = '\0';
d.d_reclen = _DIRENT_SIZE(&d);

この考えで、操作はまた、オフセットが正確な事を 確実にし、 return するために 最初の entry の場所を見つけ、 転送長が[枯渇した/使い尽くされる]までループします。 以下に処理を説明し:

int
egfs_readdir(void *v)
{
        struct vnode *vp = ((struct vop_readdir_args *)v)->a_vp;
        struct uio *uio = ((struct vop_readdir_args *)v)->a_uio;
        int *eofflag = ((struct vop_readdir_args *)v)->a_eofflag;

        int entry_counter;
        int error;
        off_t startoff;
        struct egfs_dirent de;
        struct egfs_node *dnode;
        struct egfs_node *node;

        if (vp->v_type != VDIR)
                return ENOTDIR;

        if (uio->uio_offset % sizeof(struct egfs_dirent) > 0)
                return EINVAL;

        dnode = (struct egfs_node *)vp->v_data;

        ... read the first ディレクトリー entry into de ...
        ... make node point to the de.ed_fileid node ...

        entry_counter = 0;
        startoff = uio->uio_offset;
        do {
                struct dirent d;

                ... construct d from de ...

                error = uiomove(&d, d.d_reclen, uio);

                entry_counter++;
                ... read the next ディレクトリー entry into de ...
                ... make node point to the de.ed_fileid node ...
        } while (error == 0 && uio->uio_resid > 0
            && de is valid)


        /* 重要: Update transfer オフセット to match on-disk
         * ディレクトリー entries, not virtual ones. */
        uio->uio_offset = entry_counter * sizeof(egfs_dirent);

        if (eofflag != NULL)
                *eofflag = (de is invalid?);

        return error;
}

NFS をサポートするファイルシステムはこの関数に幾つか特別なステップを取ります。 更なる詳細は vnodeops(9) を御覧ください。 XXX: クッキーと eof フラグはホンマにここで説明されるべき。

2.13.<240>Special nodes

ネームドパイプおよび/または特殊デバイスをサポートするファイルシステムは それらを作るために vnode の mknod 操作 (vop_mknod) を実装します。 これは、すこぶる vop_create に似ています。 しかしながら、ネームドパイプおよび特殊デバイスは 通常ファイルの様ではないので幾つかの特別なステップを取り: それらの中身は ファイルシステム内に蓄えられなく、そしてそれらはアクセス方法が特有のものです。 それゆえ、それらはファイルシステムの regular vnode 操作ズ vector を 用いる事ができません。

言い換えると: ファイルシステムは2つの追加的 vnode 操作 vectors を定義し: 一つはネームドパイプ用で、一つは特殊デバイス用です。 幸いにも、 仮想 fifofs (src/sys/miscfs/fifofs) および specfs (src/sys/miscfs/specfs) ファイルシステム が一般 vnode 操作を提供するので、このtask は簡単です。 大抵、これらの vectors は、幾つかの関数以外の一般操作全部で使います。

ディスク上ファイルシステムは これら特殊ファイルのアクセス時にノードの時刻を更新する必要があるので、 幾つかの操作はファイルシステム basis として実装されていて、そして、後に fifofs および specfs で実装されている一般操作を呼びます。 これが基本的に意味する事は、それらのファイルシステムは、 ネームドパイプのための、および特殊デバイスのための 独自の vop_closevop_read および vop_write 操作を実装するということです。

そのような操作のちょっとした例として:

int
egfs_fifo_read(void *v)
{
        struct vnode *vp = ((struct vop_read_args *)v)->a_vp;

        ((struct egfs_node *)vp->v_data)->tn_status |= TMPFS_NODE_ACCESSED;
        return VOCALL(fifo_vnodeop_p, VOFFSET(vop_read), v);
}

覚えとくべき事に、これらの2つの追加の操作 vectors は vnode 操作記述構造体に追加され; さもなければ、 初期化されず、故に動作しません。 See Section<240>2.2.3, “The VFS operations structure”.

更なるサンプルコードは src/sys/fs/tmpfs/fifofs_vnops.csrc/sys/fs/tmpfs/fifofs_vnops.hsrc/sys/fs/tmpfs/specfs_vnops.c および src/sys/fs/tmpfs/specfs_vnops.h を調べてください。

2.14.<240>NFS support

XXX: 書かれるべき。 vop_fhtovp および vfs_vptofh の記述。

2.15.<240>Step by step file system writing

  1. src/sys/fs/egfs ディレクトリーを作ります。

  2. 最小限の src/sys/fs/egfs/files.egfs ファイルを作ります:

    deffs fs_egfs.h EGFS
    file fs/egfs/egfs_vfsops.c egfs
    file fs/egfs/egfs_vnops.c egfs
  3. files.egfs を include するために src/sys/conf/files を変更します。 すなわち次の行を加え:

    include "fs/egfs/files.egfs"
  4. ファイルシステム名を src/sys/sys/mount.h に定義します。すなわち、 次の行を追加し:

    #define MOUNT_EGFS "egfs"
  5. ファイルシステムの vnode tag 型 を定義します。

    See Section<240>2.7.2, “vnode tags”.

  6. Linux 互換 layer の src/sys/compat/linux/common/linux_misc.c および、適切なら、 src/sys/compat/linux/common/linux_misc.h に、ファイルシステムの magic number を加えます。このファイルシステムに 適正なものが無ければ default number に Fallback する。

  7. 全 vnode 操作の stubs を入れた、最小限の src/sys/fs/egfs/egfs_vnops.c ファイルを 作成します。

    #include <sys/cdefs.h>
    __KERNEL_RCSID(0, "$NetBSD: chap-file-system.html,v 1.18 2010/04/30 16:19:12 jakllsch Exp $");
    
    #include <sys/param.h>
    #include <sys/vnode.h>
    
    #include <miscfs/genfs/genfs.h>
    
    #define egfs_lookup genfs_eopnotsupp
    #define egfs_create genfs_eopnotsupp
    #define egfs_mknod genfs_eopnotsupp
    #define egfs_open genfs_eopnotsupp
    #define egfs_close genfs_eopnotsupp
    #define egfs_access genfs_eopnotsupp
    #define egfs_getattr genfs_eopnotsupp
    #define egfs_setattr genfs_eopnotsupp
    #define egfs_read genfs_eopnotsupp
    #define egfs_write genfs_eopnotsupp
    #define egfs_fcntl genfs_eopnotsupp
    #define egfs_ioctl genfs_eopnotsupp
    #define egfs_poll genfs_eopnotsupp
    #define egfs_kqfilter genfs_eopnotsupp
    #define egfs_revoke genfs_eopnotsupp
    #define egfs_mmap genfs_eopnotsupp
    #define egfs_fsync genfs_eopnotsupp
    #define egfs_seek genfs_eopnotsupp
    #define egfs_remove genfs_eopnotsupp
    #define egfs_link genfs_eopnotsupp
    #define egfs_rename genfs_eopnotsupp
    #define egfs_mkdir genfs_eopnotsupp
    #define egfs_rmdir genfs_eopnotsupp
    #define egfs_symlink genfs_eopnotsupp
    #define egfs_readdir genfs_eopnotsupp
    #define egfs_readlink genfs_eopnotsupp
    #define egfs_abortop genfs_eopnotsupp
    #define egfs_inactive genfs_eopnotsupp
    #define egfs_reclaim genfs_eopnotsupp
    #define egfs_lock genfs_eopnotsupp
    #define egfs_unlock genfs_eopnotsupp
    #define egfs_bmap genfs_eopnotsupp
    #define egfs_strategy genfs_eopnotsupp
    #define egfs_print genfs_eopnotsupp
    #define egfs_pathconf genfs_eopnotsupp
    #define egfs_islocked genfs_eopnotsupp
    #define egfs_advlock genfs_eopnotsupp
    #define egfs_blkatoff genfs_eopnotsupp
    #define egfs_valloc genfs_eopnotsupp
    #define egfs_reallocblks genfs_eopnotsupp
    #define egfs_vfree genfs_eopnotsupp
    #define egfs_truncate genfs_eopnotsupp
    #define egfs_update genfs_eopnotsupp
    #define egfs_bwrite genfs_eopnotsupp
    #define egfs_getpages genfs_eopnotsupp
    #define egfs_putpages genfs_eopnotsupp
    
    int (**egfs_vnodeop_p)(void *);
    const struct vnodeopv_entry_desc egfs_vnodeop_entries[] = {
            { &vop_default_desc, vn_default_error },
            { &vop_lookup_desc, egfs_lookup },
            { &vop_create_desc, egfs_create },
            { &vop_mknod_desc, egfs_mknod },
            { &vop_open_desc, egfs_open },
            { &vop_close_desc, egfs_close },
            { &vop_access_desc, egfs_access },
            { &vop_getattr_desc, egfs_getattr },
            { &vop_setattr_desc, egfs_setattr },
            { &vop_read_desc, egfs_read },
            { &vop_write_desc, egfs_write },
            { &vop_ioctl_desc, egfs_ioctl },
            { &vop_fcntl_desc, egfs_fcntl },
            { &vop_poll_desc, egfs_poll },
            { &vop_kqfilter_desc, egfs_kqfilter },
            { &vop_revoke_desc, egfs_revoke },
            { &vop_mmap_desc, egfs_mmap },
            { &vop_fsync_desc, egfs_fsync },
            { &vop_seek_desc, egfs_seek },
            { &vop_remove_desc, egfs_remove },
            { &vop_link_desc, egfs_link },
            { &vop_rename_desc, egfs_rename },
            { &vop_mkdir_desc, egfs_mkdir },
            { &vop_rmdir_desc, egfs_rmdir },
            { &vop_symlink_desc, egfs_symlink },
            { &vop_readdir_desc, egfs_readdir },
            { &vop_readlink_desc, egfs_readlink },
            { &vop_abortop_desc, egfs_abortop },
            { &vop_inactive_desc, egfs_inactive },
            { &vop_reclaim_desc, egfs_reclaim },
            { &vop_lock_desc, egfs_lock },
            { &vop_unlock_desc, egfs_unlock },
            { &vop_bmap_desc, egfs_bmap },
            { &vop_strategy_desc, egfs_strategy },
            { &vop_print_desc, egfs_print },
            { &vop_islocked_desc, egfs_islocked },
            { &vop_pathconf_desc, egfs_pathconf },
            { &vop_advlock_desc, egfs_advlock },
            { &vop_blkatoff_desc, egfs_blkatoff },
            { &vop_valloc_desc, egfs_valloc },
            { &vop_reallocblks_desc, egfs_reallocblks },
            { &vop_vfree_desc, egfs_vfree },
            { &vop_truncate_desc, egfs_truncate },
            { &vop_update_desc, egfs_update },
            { &vop_bwrite_desc, egfs_bwrite },
            { &vop_getpages_desc, egfs_getpages },
            { &vop_putpages_desc, egfs_putpages },
            { NULL, NULL }
    };
    const struct vnodeopv_desc egfs_vnodeop_opv_desc =
            { &egfs_vnodeop_p, egfs_vnodeop_entries };
  8. 全 VFS 操作の stubs を入れる、最小限の src/sys/fs/egfs/egfs_vfsops.c ファイルを 作成します。

    #include <sys/cdefs.h>
    __KERNEL_RCSID(0, "$NetBSD: chap-file-system.html,v 1.18 2010/04/30 16:19:12 jakllsch Exp $");
    
    #include <sys/param.h>
    #include <sys/mount.h>
    
    static int egfs_mount(struct mount *, const char *, void *,
        struct nameidata *, struct proc *);
    static int egfs_start(struct mount *, int, struct proc *);
    static int egfs_unmount(struct mount *, int, struct proc *);
    static int egfs_root(struct mount *, struct vnode **);
    static int egfs_quotactl(struct mount *, int, uid_t, void *,
        struct proc *);
    static int egfs_vget(struct mount *, ino_t, struct vnode **);
    static int egfs_fhtovp(struct mount *, struct fid *, struct vnode **);
    static int egfs_vptofh(struct vnode *, struct fid *);
    static int egfs_statvfs(struct mount *, struct statvfs *, struct proc *);
    static int egfs_sync(struct mount *, int, struct ucred *, struct proc *);
    static void egfs_init(void);
    static void egfs_done(void);
    static int egfs_checkexp(struct mount *, struct mbuf *, int *,
        struct ucred **);
    static int egfs_snapshot(struct mount *, struct vnode *,
        struct timespec *);
    
    extern const struct vnodeopv_desc egfs_vnodeop_opv_desc;
    
    const struct vnodeopv_desc * const egfs_vnodeopv_descs[] = {
            &egfs_vnodeop_opv_desc,
            NULL,
    };
    
    struct vfsops egfs_vfsops = {
            MOUNT_EGFS,
            egfs_mount,
            egfs_start,
            egfs_unmount,
            egfs_root,
            egfs_quotactl,
            egfs_statvfs,
            egfs_sync,
            egfs_vget,
            egfs_fhtovp,
            egfs_vptofh,
            egfs_init,
            NULL, /* vfs_reinit: not yet (optional) */
            egfs_done,
            NULL, /* vfs_wassysctl: deprecated */
            NULL, /* vfs_mountroot: not yet (optional) */
            egfs_checkexp,
            egfs_snapshot,
            vfs_stdextattrctl,
            egfs_vnodeopv_descs
    };
    VFS_ATTACH(egfs_vfsops);
    
    static int
    egfs_mount(struct mount *mp, const char *path, void *data,
        struct nameidata *ndp, struct proc *p)
    {
    
            return EOPNOTSUPP;
    }
    
    static int
    egfs_start(struct mount *mp, int, struct proc *p)
    {
    
            return EOPNOTSUPP;
    }
    
    static int
    egfs_unmount(struct mount *mp, int, struct proc *p)
    {
    
            return EOPNOTSUPP;
    }
    
    static int
    egfs_root(struct mount *mp, struct vnode **vpp)
    {
    
            return EOPNOTSUPP;
    }
    
    static int
    egfs_quotactl(struct mount *mp, int cmd, uid_t uid, void *arg,
        struct proc *p)
    {
    
            return EOPNOTSUPP;
    }
    
    static int
    egfs_vget(struct mount *mp, ino_t ino, struct vnode **vpp)
    {
    
            return EOPNOTSUPP;
    }
    
    static int
    egfs_fhtovp(struct mount *mp, struct fid *fhp, struct vnode **vpp)
    {
    
            return EOPNOTSUPP;
    }
    
    static int
    egfs_vptofh(struct vnode *mp, struct fid *fhp)
    {
    
            return EOPNOTSUPP;
    }
    
    static int
    egfs_statvfs(struct mount *mp, struct statvfs *sbp, struct proc *p)
    {
    
            return EOPNOTSUPP;
    }
    
    static int
    egfs_sync(struct mount *mp, int waitfor, struct ucred *uc, struct proc *p)
    {
    
            return EOPNOTSUPP;
    }
    
    static void
    egfs_init(void)
    {
    
            return EOPNOTSUPP;
    }
    
    static void
    egfs_done(void)
    {
    
            return EOPNOTSUPP;
    }
    
    static int
    egfs_checkexp(struct mount *mp, struct mbuf *mb, int * wh,
        struct ucred **anon)
    {
    
            return EOPNOTSUPP;
    }
    
    static int
    egfs_snapshot(struct mount *mp, struct vnode *vp, struct timespec *ctime)
    {
    
            return EOPNOTSUPP;
    }
  9. このファイルシステムのための新 malloc 型を定義し、 egfs_init を変更し、および LKM の場合の egfs_done hooks を attach および detach するように変更する。

    See Section<240>2.4, “Initialization and cleanup”.

  10. ファイルシステムに必要な構造体全部を定義する src/sys/fs/egfs/egfs.h ファイルを作ります。

    #if !defined(_EGFS_H_)
    #  define _EGFS_H_
    #else
    #  error "egfs.h cannot be included multiple times."
    #endif
    
    #if defined(_KERNEL)
    
    struct egfs_mount {
            ...
    };
    
    struct egfs_node {
            ...
    };
    
    #endif /* defined(_KERNEL) */
    
    #define EGFS_ARGSVERSION 1
    struct egfs_args {
            char *ea_fspec;
    
            int ea_version;
    
            ...
    };
  11. src/sbin/mount_egfs ディレクトリーを作ります。

  12. 簡単な src/sbin/mount_egfs/Makefile ファイルを作り:

    .include <bsd.own.mk>
    
    PROG= mount_egfs
    SRCS= mount_egfs.c
    MAN= mount_egfs.8
    
    CPPFLAGS+= -I${NETBSDSRCDIR}/sys
    WARNS= 4
    
    .include <bsd.prog.mk>
  13. mount(2) システムコールを呼ぶ 簡単な src/sbin/mount_egfs/mount_egfs.c プログラムを 作ります。

    XXX: 例や関連する節へのリンクを加える。

  14. 空の src/sbin/mount_egfs/mount_egfs.8 manual page を作ります。詳細はこのガイドでは省きます。

  15. egfs_mount および egfs_unmount 関数を埋めます。

    See Section<240>2.5, “Mounting and unmounting”.

  16. egfs_statvfs 関数を埋めます。 この時点で可能なら正しいデータを返すようにし、さもなければ、 後のステップのために保留しておきます。

  17. genfs_nullopvop_fsyncvop_bwrite および vop_putpages 操作をセット。 これらは定義される必要があり、そして、 sync(2) および mount(2) 中に クラッシュするのを防ぐために successfully を戻す必要があります。 後の段階でこれらを埋めます。

  18. vop_abortop 操作を genfs_abortop にセットします。

  19. genfs_lockgenfs_unlock および genfs_islocked に locking 操作をセット。 locking はどうも必要だろうから、 それで、始めから正しくできるならそれが良いです。

    See Section<240>2.7.5, “vnode's locking protocol”.

  20. 正しく vnode を destroy するために、 vop_reclaim および vop_inactive 操作を実装します。

    See Section<240>2.7.4, “Deallocation of a vnode”.

  21. egfs_sync 関数を満たします。 何を入れるべきかわからない場合、単に return success (ゼロ)し; さもなければ、オペレーティングシステムが your ファイルシステムを flush することが不可能になるので深刻な問題が起こるでしょう。

  22. egfs_root 関数を満たします。 既に、ディスク(あるいは you use の backing store の whichever) から ファイルシステムの root node を読んでメモリー上にあると仮定すると、 単に allocate および その vnode を lock します。

    See Section<240>2.7.3, “Allocation of a vnode”.

    int
    egfs_root(struct mount *mp, struct vnode **vpp)
    {
    
            return egfs_alloc_vp(mp, ((struct egfs_mount *)mp)->em_root, vpp);
    }
  23. 標準オプション (see getmntopts(3)) および some 可能なファイルシステム特有のオプションの幾つかも のサポートのための mount ユーティリティー の改善。

  24. egfs_getattr および egfs_setattr 関数の操作を実装します。 副作用として、 egfs_update および egfs_sync も実装します。今は、 必要なものは、後のため、 success を returns する stub だけです。

    See Section<240>2.10.6, “Attributes management”.

  25. egfs_access 操作を実装します。

    See Section<240>2.10.8, “Access control”.

  26. egfs_print 関数を実装します。 これは、やるべき事が vnode 情報 (ほとんどが、その属性) の画面へのダンプなのでつまらなくて、 ですが、デバッグの助けになります。

    See Section<240>2.10.8, “Access control”.

  27. 与えられたあらゆるファイルを locate できる、単純な egfs_lookup 関数を実装し; vnodeops(9) に 記述されている locking protocol に適合させることに 慎重になってください。この部分は非常に tricky なので。 この時点では、あなたは lookup hints (CREATEDELETE あるいは RENAME) について忘れる事ができ; 必要なときに追加しましょう。

    See Section<240>2.9, “Path name resolution procedure”.

  28. egfs_open 関数を実装します。 一般的な場合、これは、ファイルフラグに対して open mode が正しいか確認するだけが必要です。

    int
    egfs_open(void *v)
    {
            struct vnode *vp = ((struct vop_open_args *)v)->a_vp;
            int mode = ((struct vop_open_args *)v)->a_mode;
    
            struct egfs_node *node;
    
            node = (struct egfs_node *)vp->v_data;
    
            if (node->en_flags & APPEND &&
                mode & (FWRITE | O_APPEND)) == FWRITE)
                    return EPERM;
    
            return 0;
    }
  29. egfs_close 関数を実装します。 一般的な場合、これは success を返す以外保留して 何もする必要がありません。

  30. egfs_readdir 操作を実装し、 そうすっと、あなたはファイルシステムとの interacting が始められます。 この関数の追加の後は、あらゆるディレクトリー を中にリストでき、ファイルの属性が正しく表れるかチェックできます。 そして、どうもおそらく、バグが見え始めるっしょ ;-)

    See Section<240>2.12.3, “Reading directories”.

  31. egfs_mkdir 操作を実装します。 to honour the CREATE hint に egfs_lookup 関数を変更する必要があるかもしれません。

    See Section<240>2.9.3, “Lookup hints”.

  32. egfs_rmdir 操作を実装します。 to honour the DELETE hint に egfs_lookup 関数を変更する必要があるかもしれません。註として、 ファイルシステムから stuff を removes する 操作の追加は is tricky で; もし vnode allocation コード、あるいは、 egfs_inactive 関数や egfs_reclaim 関数の中にバグがあったら、 問題が間違いなく持ち上がるでしょう。

    See Section<240>2.9.3, “Lookup hints” and Section<240>2.7.4, “Deallocation of a vnode”.

  33. 通常ファイル (VREG) および local sockets (VSOCK) を作成するために egfs_create 操作を実装します。

  34. ファイルの削除のための egfs_remove 操作を実装します。

  35. ハードリンクを作るための egfs_link 操作を 実装します。ファイルのハードリンク カウントを正しくコントロールすることを確実に行ってください。

  36. egfs_rename 操作を実装します。 これは、引数の量のため複雑なように見えるかもしれませんが、 しかしそれは実装するにはそう難しくありません。単に、 moves および、それが起こった状況と同じように renames を manage しなければならない事を覚えていてください。

  37. egfs_read および egfs_write 操作を実装します。 vnode の UVM オブジェクトが提供する indirection のおかげで、 これらは quite simple 。

    See Section<240>2.10.5, “Reading and writing”.

  38. egfs_getpages および egfs_putpages を、それぞれ genfs_getpages および genfs_putpages に Redirect します。 ほとんどのファイルシステムで十分なはずです。

    See Section<240>2.10.5.2, “Getting and putting pages”.

  39. egfs_bmap および egfs_strategy 操作を実装します。

    See Section<240>2.10.5.5, “Reading and writing pages”.

  40. egfs_truncate 操作を実装します。

  41. egfs_fcntlegfs_ioctlegfs_pollegfs_revoke および egfs_mmap 操作を genfs の それぞれが対応するものに Redirect します。 ほとんどのファイルシステムで十分なはずで; 註として FFS さえ、こうです。

  42. egfs_pathconf 操作を実装します。 これは[つまらない/自明]で、 pathconf(2) および vnodeops(9) 内のドキュメンテーションは 少し矛盾しているけれども。

    int
    egfs_pathconf(void *v)
    {
            int name = ((struct vop_pathconf_args *)v)->a_name;
            register_t *retval = ((struct vop_pathconf_args *)v)->a_retval;
    
            int error;
    
            switch (name) {
            case _PC_LINK_MAX:
                    *retval = LINK_MAX;
                    break;
            case ...:
            ...
            }
    
            return 0;
    }
  43. シンボリックリンクを管理する egfs_symlink および egfs_readlink 操作を実装します。

    See Section<240>2.11, “Symbolic link management”.

  44. ネームドパイプおよび特殊デバイスのサポートを追加する egfs_mknod 操作を実装します。

    See Section<240>2.13, “Special nodes”.

  45. NFS サポートを追加します。これは基本的に、 egfs_vptofhegfs_checkexp および egfs_fhtovp VFS 操作の実装を意味します。

    See Section<240>2.14, “NFS support”.



[1] 技術的に言うと、通常ファイルに NFS-mount できるように、 マウントポイントはディレクトリーでなくても良く; マウントポイントは通常ファイルであろうのに、しかし、この制限は 故意に押し付けていて、なぜならさもなければ、システムはすぐに [名前/ネーム]空間を使い果たすことができるからです。