無料Wikiサービス | デモページ
検索

アクセス数
最近のコメント
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
はじめ - ノース
はじめ - 楽打連動ユーザー
はじめ - 楽打連動ユーザー
Adsense
広告情報が設定されていません。

negative dentry


negative dentryはinodeを有していないdentryで、unlinkのプロセスの中で作成されるnegative dentryを見てみます。

引数のdirは親ディレクトリ、dentryはunlinkするファイルです。may_delete()/d_mountpoint(dentry)で、対象ファイルをunlinkしてもいいかどうかのチェックし、OKならdir->i_op->unlink()で、実デバイスのdirからdentryを削除し、d_delete()でキャッシュリスト回収の処理を行います。この時、他から参照されていないなら、このdentryはnegative dentryになります。
int vfs_unlink(struct inode *dir, struct dentry *dentry)
{
       int error = may_delete(dir, dentry, 0);

       if (error)
               return error;

       if (!dir->i_op->unlink)
               return -EPERM;

       mutex_lock(&dentry->d_inode->i_mutex);
       if (d_mountpoint(dentry))
               error = -EBUSY;
       else {
               error = security_inode_unlink(dir, dentry);
               if (!error) {
                       error = dir->i_op->unlink(dir, dentry);
                       if (!error)
                               dont_mount(dentry);
               }
       }
       mutex_unlock(&dentry->d_inode->i_mutex);

       if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
               fsnotify_link_count(dentry->d_inode);
               d_delete(dentry);
       }

       return error;
}
dentry->d_count == 1は、unlinkコマンド以外(unlinkでコールされた場合)は、このファイルを参照していない時、dentry_unlink_inode()をコールし、dentry->d_inode = NULLとする事で、inodeを有しないdentryとするnegative dentryとし復帰します。__d_drop()はコールされず、dentryキャッシュは存在し続けることになります。

なお、dentry->d_flags &= ~DCACHE_CANT_MOUNTとし、マウント不可であってもnegative dentryのディレクトリは全てマウント可能となります。マウントポイントはvfs管理下でinodeの有無と関係ないからです。ただしunlinkから呼ばれる場合、may_delete()でディレクトリの場合だとエラーとなり、このケースでは削除dentryがディレクトリちなることはありません。他のファーズからコールされるケースの処理です。
void d_delete(struct dentry * dentry)
{
       struct inode *inode;
       int isdir = 0;

again:
       spin_lock(&dentry->d_lock);
       inode = dentry->d_inode;
       isdir = S_ISDIR(inode->i_mode);
       if (dentry->d_count == 1) {
               if (inode && !spin_trylock(&inode->i_lock)) {
                       spin_unlock(&dentry->d_lock);
                       cpu_relax();
                       goto again;
               }
               dentry->d_flags &= ~DCACHE_CANT_MOUNT;
               dentry_unlink_inode(dentry);
               fsnotify_nameremove(dentry, isdir);
               return;
       }

       if (!d_unhashed(dentry))
               __d_drop(dentry);

       spin_unlock(&dentry->d_lock);

       fsnotify_nameremove(dentry, isdir);
}
削除するdentryをdentry->d_inode = NULとしnegative dentryとし、list_del_init()で親のdentryにリストされているノードd_aliasを初期化して、親からのパスを削除します。

切り離されたinodeは匿名inodeとなるわけですが、他から参照されていないなら、iput()でinode自身も削除/開放されます。
static void dentry_unlink_inode(struct dentry * dentry)
       __releases(dentry->d_lock)
       __releases(dentry->d_inode->i_lock)
{
       struct inode *inode = dentry->d_inode;
       dentry->d_inode = NULL;
       list_del_init(&dentry->d_alias);
       dentry_rcuwalk_barrier(dentry);
       spin_unlock(&dentry->d_lock);
       spin_unlock(&inode->i_lock);
       if (!inode->i_nlink)
               fsnotify_inoderemove(inode);
       if (dentry->d_op && dentry->d_op->d_iput)
               dentry->d_op->d_iput(dentry, inode);
       else
               iput(inode);
}

補足

下記HPを参考にさせて頂きました。negative dentryの目的は?についても説明がされています。
hibomaのはてなダイアリ

追記

may_delete()で親ディレクトリdir下の削除ファイルvictimを削除していいかチェックするのですが、dirがパーミッション及びAPPENDでもエラーとします。内部構造的に、開発者としてディレクトリにもモードとしてAPPENDを有しているのは、自然の流れですが、ユーザとしての視点としてちょっと意外でした。思うに追加のみの削除/変更不可のディレクトリということでしょう。
static int may_delete(struct inode *dir,struct dentry *victim,int isdir)
{
       int error;

       if (!victim->d_inode)
               return -ENOENT;

       BUG_ON(victim->d_parent->d_inode != dir);
       audit_inode_child(victim, dir);

       error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
       if (error)
               return error;
       if (IS_APPEND(dir))
               return -EPERM;
       if (check_sticky(dir, victim->d_inode)||IS_APPEND(victim->d_inode)||
           IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode))
               return -EPERM;
       if (isdir) {
               if (!S_ISDIR(victim->d_inode->i_mode))
                       return -ENOTDIR;
               if (IS_ROOT(victim))
                       return -EBUSY;
       } else if (S_ISDIR(victim->d_inode->i_mode))
               return -EISDIR;
       if (IS_DEADDIR(dir))
               return -ENOENT;
       if (victim->d_flags & DCACHE_NFSFS_RENAMED)
               return -EBUSY;
       return 0;
}

最終更新 2014/03/14 00:20:54 - north
(2014/03/13 23:39:46 作成)