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になります。
なお、dentry->d_flags &= ~DCACHE_CANT_MOUNTとし、マウント不可であってもnegative dentryのディレクトリは全てマウント可能となります。マウントポイントはvfs管理下でinodeの有無と関係ないからです。ただしunlinkから呼ばれる場合、may_delete()でディレクトリの場合だとエラーとなり、このケースでは削除dentryがディレクトリちなることはありません。他のファーズからコールされるケースの処理です。
切り離されたinodeは匿名inodeとなるわけですが、他から参照されていないなら、iput()でinode自身も削除/開放されます。
hibomaのはてなダイアリ
引数の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;
}



