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; }