追加属性でファイルの削除
[root@localhost tmp]# mkdir hoge [root@localhost tmp]# chattr +a hoge [root@localhost tmp]# touch hoge/test [root@localhost tmp]# rm hoge/test rm: 通常の空ファイル `hoge/test' を削除しますか? y rm: `hoge/test' を削除できません: 許可されていない操作です [root@localhost tmp]# chattr -a hoge/ [root@localhost tmp]# rm hoge/test rm: 通常の空ファイル `hoge/test' を削除しますか? y [root@localhost tmp]#削除できない追加属性。と言う事で、結論は明々白々ですが、とりあえず以下その実装です。
chmodで設定する属性はinode->i_modeに、chattrで設定する属性はinode->i_flagsに設定され、この設定値により削除可能かどうかのチェックが行われます。
ファイル削除はunlinkシステムコールからvfs_unlink()とコールされ、isdir=0でmay_delete()で削除していいかのチェックします。dirは削除ファイルの属するディレクトリで、victimは削除ファイルのdentryです。
inode_permission()で親ディレクトリがWRITE/EXECでなく、if (IS_APPEND(dir))で追加属性なら削除できません。また、親ディレクトリが削除可能でも、削除ファイルがIS_APPEND(victim->d_inode)でも削除できません。
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;
}
補足
BUG_ON(victim->d_parent->d_inode != dir)は、victimがdirディレクトリ下のファイルでないと言うことで、これはカーネルのバグか、may_delete()引数の誤使用と言うことです。IS_IMMUTABLE(victim->d_inode)はchattr -iで設定するフラグで、当然削除できません。親ディレクトリについては、inode_permission(dir, MAY_WRITE | MAY_EXEC)で-iならエラーと返します。





