ACL
Rev.8を表示中。最新版はこちら。
新規Iノード取得時、ファイルシステム依存のiノード取得関数がコールされ、ext2の場合ext2_new_inode()がコールされます。必要なinodeとしての設定およびブロックを割り当てた後、ext2_init_acl()でACLの設定を行います。この設定はinodeのi_file_acl/i_dir_aclにACL情報を書き込むブロックのインデックスを設定します。ext2_init_aclの引数*inodeは、新規に割り当てたファイル、*dirは親ディレクトリです。割り当てたファイルがリンクでない場合で、親ディレクトリがPOSIX_ACLでマウントしていると、ACL_TYPE_DEFAULTのACLを取得します。ACL_TYPE_DEFAULTはディレクトリinodeにだけ設定され、下位のinodeがディレクトリに継承し(下位のinodeのACL_TYPE_DEFAULTに設定)、ACL_TYPE_DEFAULTを雛形として、引数のパーミッションモードを考慮して、ACLをACL_TYPE_ACCESSに設定します。
親ディレクトリがACLがない場合、パミッションはinode->i_mode &= ~current_umask()とし、umaskコマンドの設定が有効となります。
int ext2_init_acl(struct inode *inode, struct inode *dir) { struct posix_acl *acl = NULL; int error = 0; if (!S_ISLNK(inode->i_mode)) { if (test_opt(dir->i_sb, POSIX_ACL)) { acl = ext2_get_acl(dir, ACL_TYPE_DEFAULT); if (IS_ERR(acl)) return PTR_ERR(acl); } if (!acl) inode->i_mode &= ~current_umask(); } if (test_opt(inode->i_sb, POSIX_ACL) && acl) { if (S_ISDIR(inode->i_mode)) { error = ext2_set_acl(inode, ACL_TYPE_DEFAULT, acl); if (error) goto cleanup; } error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode); if (error < 0) return error; if (error > 0) { /* This is an extended ACL */ error = ext2_set_acl(inode, ACL_TYPE_ACCESS, acl); } } cleanup: posix_acl_release(acl); return error; }パーミッションチェックは、ACL有無関係なくacl_permission_check()がコールされます。ACLのチェックはcheck_acl()からposix_acl_permission()をコールします。GROUPが0の場合、inode->i_modeによりチェックします。このケースでは、わざわざACLをチェック必要がないからです。ACLのパーミッションのUSER/GROUP/OTHERはinodeのそれと同じで、inode->i_modeをチェックすればいいのです。グループについては、ACLのMASKに反映されています。(MASKがなければGROUPそのものに設定されています。下記参照)
static int acl_permission_check(struct inode *inode, int mask) { unsigned int mode = inode->i_mode; if (current_user_ns() != inode_userns(inode)) goto other_perms; if (likely(current_fsuid() == inode->i_uid)) mode >>= 6; else { if (IS_POSIXACL(inode) && (mode & S_IRWXG)) { int error = check_acl(inode, mask); if (error != -EAGAIN) return error; } if (in_group_p(inode->i_gid)) mode >>= 3; } other_perms: if ((mask & ~mode & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) return 0; return -EACCES; }ACL_USER_OBJ/ACL_OTHERの時は、ACL要素のpa->e_permのパーミッションを直接チェックしていますが、それ以外はpa->e_permとACL_MASKでチェックします。ACL_MASKはchmodでのグループパーミッションと同等です。(ACL_MASKがない場合、グループパーミッションはACL_GROUP_OBJに設定)
ここで、MASKが機能するのは、ACL_USER/ACL_GROUP_OBJ/ACL_GROUPで、inode->i_modeで機能しないパーミッションです。こうすることで、GROPUのパーミッションをinode->i_modeでは、従来どうりの、ACLではGROUPとしての機能だけでなく、ACLの拡張機能についてはそのMASKとしての機能を持たせようということでないかと。(まどろこしくしてまで、このような事をする意義があるのか疑問ですが・・・)
ACLのGROUPとのMASKですので、chmodでgroupの属性を変更しても、user/otherと違って、ベースのACL_GROUP_OBJとのマスクとなり、元のACL_GROUP_OBJはそのままです。
int posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want) { const struct posix_acl_entry *pa, *pe, *mask_obj; int found = 0; want &= MAY_READ | MAY_WRITE | MAY_EXEC | MAY_NOT_BLOCK; FOREACH_ACL_ENTRY(pa, acl, pe) { switch(pa->e_tag) { case ACL_USER_OBJ: /* (May have been checked already) */ if (inode->i_uid == current_fsuid()) goto check_perm; break; case ACL_USER: if (pa->e_id == current_fsuid()) goto mask; break; case ACL_GROUP_OBJ: if (in_group_p(inode->i_gid)) { found = 1; if ((pa->e_perm & want) == want) goto mask; } break; case ACL_GROUP: if (in_group_p(pa->e_id)) { found = 1; if ((pa->e_perm & want) == want) goto mask; } break; case ACL_MASK: break; case ACL_OTHER: if (found) return -EACCES; else goto check_perm; default: return -EIO; } } return -EIO; mask: for (mask_obj = pa+1; mask_obj != pe; mask_obj++) { if (mask_obj->e_tag == ACL_MASK) { if ((pa->e_perm & mask_obj->e_perm & want) == want) return 0; return -EACCES; } } check_perm: if ((pa->e_perm & want) == want) return 0; return -EACCES; }chmodはnotify_change()をコールします。if (inode->i_op->setattr)なら、すなわちACLならinode->i_op->setattr()し、そうでないならsimple_setattr()をコールします。simple_setattr()はinode->i_modeにパーミッションを設定するだけです。
int notify_change(struct dentry * dentry, struct iattr * attr) { struct inode *inode = dentry->d_inode; umode_t mode = inode->i_mode; int error; struct timespec now; unsigned int ia_valid = attr->ia_valid; if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID | ATTR_TIMES_SET)) { if (IS_IMMUTABLE(inode) || IS_APPEND(inode)) return -EPERM; } if ((ia_valid & ATTR_MODE)) { umode_t amode = attr->ia_mode; /* Flag setting protected by i_mutex */ if (is_sxid(amode)) inode->i_flags &= ~S_NOSEC; } now = current_fs_time(inode->i_sb); attr->ia_ctime = now; if (!(ia_valid & ATTR_ATIME_SET)) attr->ia_atime = now; if (!(ia_valid & ATTR_MTIME_SET)) attr->ia_mtime = now; if (ia_valid & ATTR_KILL_PRIV) { attr->ia_valid &= ~ATTR_KILL_PRIV; ia_valid &= ~ATTR_KILL_PRIV; error = security_inode_need_killpriv(dentry); if (error > 0) error = security_inode_killpriv(dentry); if (error) return error; } if ((ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID)) && (ia_valid & ATTR_MODE)) BUG(); if (ia_valid & ATTR_KILL_SUID) { if (mode & S_ISUID) { ia_valid = attr->ia_valid |= ATTR_MODE; attr->ia_mode = (inode->i_mode & ~S_ISUID); } } if (ia_valid & ATTR_KILL_SGID) { if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { if (!(ia_valid & ATTR_MODE)) { ia_valid = attr->ia_valid |= ATTR_MODE; attr->ia_mode = inode->i_mode; } attr->ia_mode &= ~S_ISGID; } } if (!(attr->ia_valid & ~(ATTR_KILL_SUID | ATTR_KILL_SGID))) return 0; error = security_inode_setattr(dentry, attr); if (error) return error; if (inode->i_op->setattr) error = inode->i_op->setattr(dentry, attr); else error = simple_setattr(dentry, attr); if (!error) { fsnotify_change(dentry, ia_valid); evm_inode_post_setattr(dentry, ia_valid); } return error; }ファイルシステム依存のコールバック関数inode->i_op->setattr()をコールすると、attrブロックを読み出して、posix_acl_chmod()からposix_acl_chmod_masq()をコールします。
ACL_USER_OBJ/ACL_OTHERのみ、パーミッションをそのままACLのパーミッションとしています。ACL_USER/ACL_GROUPはスキップです。chmodはこの設定をできません。ACL_MASKがあるならグループパーミッションを設定し、なければ、常識的にACL_GROUP_OBJに設定します。
すなわち、グループパーミッションはACLでは、ACL_USER/ACL_GROUPのマスクとして機能します。でACL_GROUP_OBJ時もマスク処理しているので、動作としてグループパーミッションとして整合性が整うわけです。要はグループパーミッションをACLではマスクとして使えて、しかもinodeでのグループパーミッションとしての機能を果たすということです。
static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode) { struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; struct posix_acl_entry *pa, *pe; FOREACH_ACL_ENTRY(pa, acl, pe) { switch(pa->e_tag) { case ACL_USER_OBJ: pa->e_perm = (mode & S_IRWXU) >> 6; break; case ACL_USER: case ACL_GROUP: break; case ACL_GROUP_OBJ: group_obj = pa; break; case ACL_MASK: mask_obj = pa; break; case ACL_OTHER: pa->e_perm = (mode & S_IRWXO); break; default: return -EIO; } } if (mask_obj) { mask_obj->e_perm = (mode & S_IRWXG) >> 3; } else { if (!group_obj) return -EIO; group_obj->e_perm = (mode & S_IRWXG) >> 3; } return 0; }
追記
chmodはinode->i_modeを設定するもので、ACLを設定する場合setfaclで行うものと思っていました。上で見てきたように、ACLが設定してあるinodにchmodをおこなうと、ACLに設定されるわけです。特定ユーザ/グループについては、また新規にACLを設定する場合setfaclで設定しなければなりません。また、unmaskは、ACLのファイルについては効果ないということです。検証走査です。umask 777でaclディレクトリ下でファイルを作成すると、配下のファイルのすべてのパーミッションは無効です。
[root@localhost kitamura]# mkdir acl [root@localhost kitamura]# ls -ld acl drwxr-xr-x 2 root root 4096 11月 27 03:23 acl [root@localhost kitamura]# umask 777 [root@localhost kitamura]# touch acl/test [root@localhost kitamura]# ls -l acl/test ---------- 1 root root 0 11月 27 03:24 acl/test [root@localhost kitamura]#ACL_TYPE_DEFAULTでディレクトリにACLを設定すると、配下のファイルはACLとなり、umask 777は無効となります。acl/test1のパーミッションrw-r--r--は、touchコマンド引数で、この設定でACLに設定されます。
[root@localhost kitamura]# setfacl -dm user::rwx acl [root@localhost kitamura]# ls -ld acl drwxr-xr-x+ 2 root root 4096 11月 27 03:24 acl [root@localhost kitamura]# umask 777 [root@localhost kitamura]# touch acl/test1 [root@localhost kitamura]# ls -l acl/test1 -rw-r--r-- 1 root root 0 11月 27 03:26 acl/test1