ACL
Rev.2を表示中。最新版はこちら。
あるユーザにあるファイルの権限を与える場合、そのユーザを、権限のあるグループに属することで実現していました。しかしそのグループが他のファイルに対しても権限があると、このユーザにもその権限を与えることになってしまいます。そのような事を回避する手段がACL(access control list)です。ACLは後に実装されたと言うことで、ファイルのパーミッションのような取り扱いはできません。まずACLを使う場合、aclオプションでマウントしなければなりません。またACLを設定したファイルをcpコマンドで複写すると、ACLは無くなってしまいます。ファイルシステムをまたがるmvコマンドについても同様です。
結論からゆうと、ACL情報そのもはinode内に有してないからです。inodeサイズはできるだけ小さく、しかも有効に使用する必要があります。可変サイズのACL情報をinodeに埋め込むことは不可能なことです。(互換性の問題もあり。)
ファイルをオープンするsys_openはfilp_open関数を、そこからコールされるdo_filp_open関数で、実際の処理が行われます。do_filp_open関数では、まず指定されたファイル名のdentryを、path_lookup_open/ path_lookup_create関数で取得します。そしてこのdentryに紐づくinodeで、そのセキュリティのチェックをmay_open関数を呼ぶことで行っています。
may_open関数はvfs_permission関数をコールし、vfs_permission関数でinode_permission関数をコールして、inodeにかかるパーミッションのチェックを行います。
inode_permission関数では、read-onlyモードのファイルに書き込みでオープンしているか等のチェックをした後、inode->i_op->permissionにコールバック関数が設定されているならそれを、設定されていないなら、generic_permission関数を呼んでいます。inode->i_op->permissionコールバック関数はデバイス依存となります。inode取得時にそのデバイスで設定されているコールバック関数です。
実はinode->i_op->permissionがコールされる時も、generic_permission関数がコールされることになります。ただし、その第三引数にデバイス依存のチェック関数のアドレスを引数にして呼ばれています。
const struct inode_operations ext2_file_inode_operations = { .truncate = ext2_truncate, : .setattr = ext2_setattr, .permission = ext2_permission, }; int ext2_permission(struct inode *inode, int mask) { return generic_permission(inode, mask, ext2_check_acl); }generic_permission関数ではファイルモード(パーミション)のチェックと、第三引数がNULLで無いならその関数をコールします。これがACLにかかるチェックとなります。
int generic_permission(struct inode *inode, int mask, int (*check_acl)(struct inode *inode, int mask)) { umode_t mode = inode->i_mode; mask &= MAY_READ | MAY_WRITE | MAY_EXEC; if (current->fsuid == inode->i_uid) mode >>= 6; else { if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl) { int error = check_acl(inode, mask); if (error == -EACCES) goto check_capabilities; else if (error != -EAGAIN) return error; } if (in_group_p(inode->i_gid)) mode >>= 3; } if ((mask & ~mode) == 0) return 0; : }最初のif (current->fsuid == inode->i_uid)は、そのプロセスがファイルの所有者の権限時の処理です。inode->i_uidはファイルの所有者ですが、current->fsuidというのは、実行uidでチェックするのでなく、ファイルアクセスのみの権限を、その所有者の権限に移譲できるように導入された新しいメンバーだそうです。
プロセスがファイル所有者でないなら、if (IS_POSIXACL(inode) && (mode & S_IRWXG) && check_acl)となります。IS_POSIXACLマクロは、inodeのスーパブロックのフラグにMS_POSIXACLが設定されているかチェックしています。このフラグはmount時に設定されるものです。それ故mountでaclオプションを付加しないと、ACLが使えないわけです。
#define IS_POSIXACL(inode) __IS_FLG(inode, MS_POSIXACL) #define __IS_FLG(inode,flg) ((inode)->i_sb->s_flags & (flg))
ACLが未サポートないし、check_acl関数がNULLなら、if (in_group_p(inode->i_gid))でグループにかかるチェックを行うための処理となっています。
ext2_check_acl関数は、ext2のACLにかかる処理です。ext2_get_acl関数でACLの情報を取得し、posix_acl_permission関数でチェックするわけですが、ACLの取得においてext2_get_acl関数内でsb_bread(inode->i_sb, EXT2_I(inode)->i_file_acl)でデータブロックを読み込んでいます。EXT2_I(inode)はinodeからデバイスのinode(この場合struct ext2_inode_info)を取得します。そしてそのメンバーたるi_file_aclをブロック番号としてACLデータを読み込んでいるのです。
static int ext2_check_acl(struct inode *inode, int mask) { struct posix_acl *acl = ext2_get_acl(inode, ACL_TYPE_ACCESS); if (IS_ERR(acl)) return PTR_ERR(acl); if (acl) { int error = posix_acl_permission(inode, acl, mask); posix_acl_release(acl); return error; } return -EAGAIN; }従って、delコマンドと違ってcpコマンドのようにinodeだけの処理で実装できないコマンドについては、ACLにかかる処理がなされないと言うわけです。なお、ACLとして設定できる情報はEXT2_I(inode)->i_file_aclと配列でないということで、設定できるのは1ブロックのみのようです。
補足
EXT2_I(inode)がデバイスのinodeを取得できる仕組みは、VFSシステム下でのinodeは、デバイス毎のinodeに埋め込まれているのです。デバイス毎のinodeはデバイス依存です。しかしどのデバイスinodeにもvfs_inodeメンバーを有しており、それがvfsのinodeとなっているのです。すなわちvfsのinodeはデバイス毎のinodeのメンバーのvfs_inodeなのです。したがってこれはトッリキーなcontainer_ofマクロで簡単に取得できます。static inline struct ext2_inode_info *EXT2_I(struct inode *inode) { return container_of(inode, struct ext2_inode_info, vfs_inode); }