dir属性に係るfile削除サンプル
o-w:dirユーザ(aaa)でないユーザ(bbb)でのファイル作成が不可能。
o+w:dirユーザ(aaa)でないユーザ(bbb)でファイル作成が可能。
[aaa@north ~]$ mkdir dir [aaa@north ~]$ ls -l 合計 4 drwxrwxr-x 2 aaa aaa 4096 1月 24 12:15 dir [aaa@north ~]$ su -c "touch dir/file" bbb パスワード: touch: `dir/file' に touch できません: 許可がありません
o+w:dirユーザ(aaa)でないユーザ(bbb)でファイル作成が可能。
[aaa@north ~]$ chmod o+w dir [aaa@north ~]$ su -c "touch dir/file" bbb パスワード: [aaa@north ~]$ ls -l dir 合計 0 -rw-rw-r-- 1 bbb bbb 0 1月 23 14:11 fileo-t:fileユーザ(bbb)/dirユーザ(aaa)でないユーザ(ccc)での削除可能
[aaa@north ~]$ su -c "rm dir/file" ccc パスワード: rm: 書き込み保護されたファイル 通常の空ファイル `dir/file' を削除しますか?y [aaa@north ~]$ ls -l dir 合計 0o+t:ディレクトリユーザ(aaa)とファイルユーザ(bbb)でのみ削除可能
[aaa@north ~]$ chmod o+t dir [aaa@north ~]$ su -c "touch dir/file" bbb パスワード: [aaa@north ~]$ ls -l dir 合計 0 -rw-rw-r-- 1 bbb bbb 0 1月 23 14:12 file [aaa@north ~]$ su -c "rm dir/file" ccc パスワード: rm: 書き込み保護されたファイル 通常の空ファイル `dir/file' を削除しますか?y rm: `dir/file' を削除できません: 許可されていない操作です [aaa@north ~]$ su -c "rm dir/file" bbb パスワード: [aaa@north ~]$ ls -l dir 合計 0 [aaa@north ~]$ su -c "touch dir/file" bbb パスワード: [aaa@north ~]$ su -c "rm dir/file" aaa パスワード: rm: 書き込み保護されたファイル 通常の空ファイル `dir/file' を削除しますか?y [aaa@north ~]$ ls -l dir 合計 0
ファイル削除はunlinkシステムコール。
SYSCALL_DEFINE1(unlink, const char __user *, pathname) { return do_unlinkat(AT_FDCWD, pathname); } static long do_unlinkat(int dfd, const char __user *pathname) { int error; struct filename *name; struct dentry *dentry; struct nameidata nd; struct inode *inode = NULL; name = user_path_parent(dfd, pathname, &nd); if (IS_ERR(name)) return PTR_ERR(name); error = -EISDIR; if (nd.last_type != LAST_NORM) goto exit1; nd.flags &= ~LOOKUP_PARENT; error = mnt_want_write(nd.path.mnt); if (error) goto exit1; mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_hash(&nd); error = PTR_ERR(dentry); if (!IS_ERR(dentry)) { if (nd.last.name[nd.last.len]) goto slashes; inode = dentry->d_inode; if (!inode) goto slashes; ihold(inode); error = security_path_unlink(&nd.path, dentry); if (error) goto exit2; error = vfs_unlink(nd.path.dentry->d_inode, dentry); exit2: dput(dentry); } mutex_unlock(&nd.path.dentry->d_inode->i_mutex); if (inode) iput(inode); /* truncate the inode here */ mnt_drop_write(nd.path.mnt); exit1: path_put(&nd.path); putname(name); return error; slashes: error = !dentry->d_inode ? -ENOENT : S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR; goto exit2; } 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; } 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(dir, victim, AUDIT_TYPE_CHILD_DELETE); 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; }タスクデフォルトのcurrent->fsuidは、current->euid、current->euidのデフォルトはcurrent->uid
#define current_fsuid() (current_cred_xxx(fsuid)) ====> current->fsuid static inline int check_sticky(struct inode *dir, struct inode *inode) { kuid_t fsuid = current_fsuid(); if (!(dir->i_mode & S_ISVTX)) <-!S_ISVTXなら削除OK return 0; if (uid_eq(inode->i_uid, fsuid)) <-S_ISVTXならプロセスがfileユーザなら削除OK return 0; if (uid_eq(dir->i_uid, fsuid) <-S_ISVTXならプロセスがdirユーザなら削除OK return 0; return !inode_capable(inode, CAP_FOWNER); <-rootなら削除OK }S_APPEND/S_IMMUTABLE/S_SWAPFILEは、chattrでinode->i_flagsに設定されるファイル毎のinode属性で、ユーザに関わらず削除不可となる。
#define IS_APPEND(inode) ((inode)->i_flags & S_APPEND #define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE) #define IS_SWAPFILE(inode) ((inode)->i_flags & S_SWAPFILE)