On Thu, 2016-06-30 at 15:47 +0200, Andreas Gruenbacher wrote:
> 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 <[email protected]>
> Reviewed-by: J. Bruce Fields <[email protected]>
> ---
>  fs/richacl.c            | 104 
> ++++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/richacl.h |   1 +
>  2 files changed, 105 insertions(+)
> 
> diff --git a/fs/richacl.c b/fs/richacl.c
> index ba110a6..e8a383b 100644
> --- a/fs/richacl.c
> +++ b/fs/richacl.c
> @@ -618,3 +618,107 @@ richacl_chmod(struct inode *inode, umode_t mode)
>       return retval;
>  }
>  EXPORT_SYMBOL(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 db82fab..9212edb 100644
> --- a/include/linux/richacl.h
> +++ b/include/linux/richacl.h
> @@ -191,5 +191,6 @@ extern unsigned int richacl_want_to_mask(unsigned int);
>  extern void richacl_compute_max_masks(struct richacl *);
>  extern int richacl_permission(struct inode *, const struct richacl *, int);
>  extern int richacl_chmod(struct inode *, umode_t);
> +extern int richacl_equiv_mode(const struct richacl *, umode_t *);
>  
>  #endif /* __RICHACL_H */

Reviewed-by: Jeff Layton <[email protected]>

Reply via email to