ACL
Rev.6を表示中。最新版はこちら。
新規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;
}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]# touch acl/test1 [root@localhost kitamura]# ls -l acl/test1 -rw-r--r-- 1 root root 0 11月 27 03:26 acl/test1






