From patchwork Mon Feb 29 08:17:20 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Andreas Gruenbacher X-Patchwork-Id: 8449911 Return-Path: X-Original-To: patchwork-linux-nfs@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork1.web.kernel.org (Postfix) with ESMTP id D7A269FC55 for ; Mon, 29 Feb 2016 08:21:52 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id E97F7200BE for ; Mon, 29 Feb 2016 08:21:51 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id F417E200E6 for ; Mon, 29 Feb 2016 08:21:50 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753407AbcB2IVq (ORCPT ); Mon, 29 Feb 2016 03:21:46 -0500 Received: from mx1.redhat.com ([209.132.183.28]:58174 "EHLO mx1.redhat.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752816AbcB2IS2 (ORCPT ); Mon, 29 Feb 2016 03:18:28 -0500 Received: from int-mx09.intmail.prod.int.phx2.redhat.com (int-mx09.intmail.prod.int.phx2.redhat.com [10.5.11.22]) by mx1.redhat.com (Postfix) with ESMTPS id 7E0E2C0B8D50; Mon, 29 Feb 2016 08:18:27 +0000 (UTC) Received: from schleppi.home.com (vpn1-4-25.ams2.redhat.com [10.36.4.25]) by int-mx09.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u1T8HS6K028000; Mon, 29 Feb 2016 03:18:24 -0500 From: Andreas Gruenbacher To: Alexander Viro Cc: "J. Bruce Fields" , linux-nfs@vger.kernel.org, "Theodore Ts'o" , linux-cifs@vger.kernel.org, linux-api@vger.kernel.org, Trond Myklebust , linux-kernel@vger.kernel.org, xfs@oss.sgi.com, Christoph Hellwig , Andreas Dilger , linux-fsdevel@vger.kernel.org, Jeff Layton , linux-ext4@vger.kernel.org, Anna Schumaker Subject: [PATCH v18 15/22] richacl: Check if an acl is equivalent to a file mode Date: Mon, 29 Feb 2016 09:17:20 +0100 Message-Id: <1456733847-17982-16-git-send-email-agruenba@redhat.com> In-Reply-To: <1456733847-17982-1-git-send-email-agruenba@redhat.com> References: <1456733847-17982-1-git-send-email-agruenba@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.22 Sender: linux-nfs-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-nfs@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP ACLs are considered equivalent to file modes if they only consist of owner@, group@, and everyone@ entries, the owner@ permissions do not depend on whether the owner is a member in the owning group, and no inheritance flags are set. This test is used to avoid storing richacls if the acl can be computed from the file permission bits. Signed-off-by: Andreas Gruenbacher Reviewed-by: J. Bruce Fields --- fs/richacl_base.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/richacl.h | 1 + 2 files changed, 105 insertions(+) diff --git a/fs/richacl_base.c b/fs/richacl_base.c index e4dd779..74e5cb5 100644 --- a/fs/richacl_base.c +++ b/fs/richacl_base.c @@ -382,3 +382,107 @@ __richacl_chmod(struct richacl *acl, umode_t mode) return clone; } EXPORT_SYMBOL_GPL(__richacl_chmod); + +/** + * richacl_equiv_mode - compute the mode equivalent of @acl + * + * An acl is considered equivalent to a file mode if it only consists of + * owner@, group@, and everyone@ entries and the owner@ permissions do not + * depend on whether the owner is a member in the owning group. + */ +int +richacl_equiv_mode(const struct richacl *acl, umode_t *mode_p) +{ + umode_t mode = *mode_p; + + /* + * The RICHACE_DELETE_CHILD flag is meaningless for non-directories, so + * we ignore it. + */ + unsigned int x = S_ISDIR(mode) ? 0 : RICHACE_DELETE_CHILD; + struct { + unsigned int allowed; + unsigned int defined; /* allowed or denied */ + } owner = { + .defined = RICHACE_POSIX_ALWAYS_ALLOWED | + RICHACE_POSIX_OWNER_ALLOWED | x, + }, group = { + .defined = RICHACE_POSIX_ALWAYS_ALLOWED | x, + }, everyone = { + .defined = RICHACE_POSIX_ALWAYS_ALLOWED | x, + }; + const struct richace *ace; + + if (acl->a_flags & ~(RICHACL_WRITE_THROUGH | RICHACL_MASKED)) + return -1; + + richacl_for_each_entry(ace, acl) { + if (ace->e_flags & ~RICHACE_SPECIAL_WHO) + return -1; + + if (richace_is_owner(ace) || richace_is_everyone(ace)) { + x = ace->e_mask & ~owner.defined; + if (richace_is_allow(ace)) { + unsigned int group_denied = + group.defined & ~group.allowed; + + if (x & group_denied) + return -1; + owner.allowed |= x; + } else /* if (richace_is_deny(ace)) */ { + if (x & group.allowed) + return -1; + } + owner.defined |= x; + + if (richace_is_everyone(ace)) { + x = ace->e_mask; + if (richace_is_allow(ace)) { + group.allowed |= + x & ~group.defined; + everyone.allowed |= + x & ~everyone.defined; + } + group.defined |= x; + everyone.defined |= x; + } + } else if (richace_is_group(ace)) { + x = ace->e_mask & ~group.defined; + if (richace_is_allow(ace)) + group.allowed |= x; + group.defined |= x; + } else + return -1; + } + + if (group.allowed & ~owner.defined) + return -1; + + if (acl->a_flags & RICHACL_MASKED) { + if (acl->a_flags & RICHACL_WRITE_THROUGH) { + owner.allowed = acl->a_owner_mask; + everyone.allowed = acl->a_other_mask; + } else { + owner.allowed &= acl->a_owner_mask; + everyone.allowed &= acl->a_other_mask; + } + group.allowed &= acl->a_group_mask; + } + + mode = (mode & ~S_IRWXUGO) | + (richacl_mask_to_mode(owner.allowed) << 6) | + (richacl_mask_to_mode(group.allowed) << 3) | + richacl_mask_to_mode(everyone.allowed); + + /* Mask flags we can ignore */ + x = S_ISDIR(mode) ? 0 : RICHACE_DELETE_CHILD; + + if (((richacl_mode_to_mask(mode >> 6) ^ owner.allowed) & ~x) || + ((richacl_mode_to_mask(mode >> 3) ^ group.allowed) & ~x) || + ((richacl_mode_to_mask(mode) ^ everyone.allowed) & ~x)) + return -1; + + *mode_p = mode; + return 0; +} +EXPORT_SYMBOL_GPL(richacl_equiv_mode); diff --git a/include/linux/richacl.h b/include/linux/richacl.h index a2d5600..b7128bf 100644 --- a/include/linux/richacl.h +++ b/include/linux/richacl.h @@ -185,6 +185,7 @@ extern unsigned int richacl_mode_to_mask(umode_t); extern unsigned int richacl_want_to_mask(unsigned int); extern void richacl_compute_max_masks(struct richacl *); extern struct richacl *__richacl_chmod(struct richacl *, umode_t); +extern int richacl_equiv_mode(const struct richacl *, umode_t *); /* richacl_inode.c */ extern int richacl_permission(struct inode *, const struct richacl *, int);