ACL


Rev.5を表示中。最新版はこちら

新規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のファイルについては効果ないということです。

最終更新 2013/11/28 12:52:39 - north
(2010/12/15 17:53:13 作成)


検索

アクセス数
2953533
最近のコメント
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
はじめ - ノース
Adsense
広告情報が設定されていません。