追加属性でファイルの削除
[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ならエラーと返します。