[RFC,v7,14/41] richacl: Create-time inheritance
diff mbox

Message ID 1441448856-13478-15-git-send-email-agruenba@redhat.com
State New
Headers show

Commit Message

Andreas Gruenbacher Sept. 5, 2015, 10:27 a.m. UTC
When a new file is created, it can inherit an acl from its parent
directory; this is similar to how default acls work in POSIX (draft)
ACLs.

As with POSIX ACLs, if a file inherits an acl from its parent directory,
the intersection between the create mode and the permissions granted by
the inherited acl determines the file masks and file permission bits,
and the umask is ignored.

Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
---
 fs/richacl_base.c       | 69 +++++++++++++++++++++++++++++++++++++++++++++++++
 fs/richacl_inode.c      | 65 ++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |  2 ++
 3 files changed, 136 insertions(+)

Comments

Bruce Fields Sept. 18, 2015, 5:58 p.m. UTC | #1
On Sat, Sep 05, 2015 at 12:27:09PM +0200, Andreas Gruenbacher wrote:
> When a new file is created, it can inherit an acl from its parent
> directory; this is similar to how default acls work in POSIX (draft)
> ACLs.
> 
> As with POSIX ACLs, if a file inherits an acl from its parent directory,
> the intersection between the create mode and the permissions granted by
> the inherited acl determines the file masks and file permission bits,
> and the umask is ignored.
> 
> Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
> ---
>  fs/richacl_base.c       | 69 +++++++++++++++++++++++++++++++++++++++++++++++++
>  fs/richacl_inode.c      | 65 ++++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/richacl.h |  2 ++
>  3 files changed, 136 insertions(+)
> 
> diff --git a/fs/richacl_base.c b/fs/richacl_base.c
> index 106e988..fda407d 100644
> --- a/fs/richacl_base.c
> +++ b/fs/richacl_base.c
> @@ -483,3 +483,72 @@ richacl_equiv_mode(const struct richacl *acl, mode_t *mode_p)
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(richacl_equiv_mode);
> +
> +/**
> + * richacl_inherit  -  compute the inherited acl of a new file
> + * @dir_acl:	acl of the containing directory
> + * @isdir:	inherit by a directory or non-directory?
> + *
> + * A directory can have acl entries which files and/or directories created
> + * inside the directory will inherit.  This function computes the acl for such
> + * a new file.  If there is no inheritable acl, it will return %NULL.
> + */
> +struct richacl *
> +richacl_inherit(const struct richacl *dir_acl, int isdir)
> +{
> +	const struct richace *dir_ace;
> +	struct richacl *acl = NULL;
> +	struct richace *ace;
> +	int count = 0;
> +
> +	if (isdir) {
> +		richacl_for_each_entry(dir_ace, dir_acl) {
> +			if (!richace_is_inheritable(dir_ace))
> +				continue;
> +			count++;
> +		}
> +		if (!count)
> +			return NULL;
> +		acl = richacl_alloc(count, GFP_KERNEL);
> +		if (!acl)
> +			return ERR_PTR(-ENOMEM);
> +		ace = acl->a_entries;
> +		richacl_for_each_entry(dir_ace, dir_acl) {
> +			if (!richace_is_inheritable(dir_ace))
> +				continue;
> +			richace_copy(ace, dir_ace);
> +			if (dir_ace->e_flags & RICHACE_NO_PROPAGATE_INHERIT_ACE)
> +				ace->e_flags &= ~RICHACE_INHERITANCE_FLAGS;
> +			else if ((dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE) &&
> +				 !(dir_ace->e_flags & RICHACE_DIRECTORY_INHERIT_ACE))

The FILE_INHERIT_ACE check there is redundant since we already know
dir_ace is inheritable.

(So, OK, it isn't wrong to check it again but let's not make this
condition any more complicated than necessary.)

> +				ace->e_flags |= RICHACE_INHERIT_ONLY_ACE;
> +			ace++;
> +		}
> +	} else {
> +		richacl_for_each_entry(dir_ace, dir_acl) {
> +			if (!(dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE))
> +				continue;
> +			count++;
> +		}
> +		if (!count)
> +			return NULL;
> +		acl = richacl_alloc(count, GFP_KERNEL);
> +		if (!acl)
> +			return ERR_PTR(-ENOMEM);
> +		ace = acl->a_entries;
> +		richacl_for_each_entry(dir_ace, dir_acl) {
> +			if (!(dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE))
> +				continue;
> +			richace_copy(ace, dir_ace);
> +			ace->e_flags &= ~RICHACE_INHERITANCE_FLAGS;
> +			/*
> +			 * RICHACE_DELETE_CHILD is meaningless for
> +			 * non-directories, so clear it.
> +			 */
> +			ace->e_mask &= ~RICHACE_DELETE_CHILD;
> +			ace++;
> +		}
> +	}
> +
> +	return acl;
> +}
> diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
> index dc2a69f..f3f1f84 100644
> --- a/fs/richacl_inode.c
> +++ b/fs/richacl_inode.c
> @@ -220,3 +220,68 @@ out:
>  	return denied ? -EACCES : 0;
>  }
>  EXPORT_SYMBOL_GPL(richacl_permission);
> +
> +/**
> + * richacl_inherit_inode  -  compute inherited acl and file mode
> + * @dir_acl:	acl of the containing directory
> + * @inode:	inode of the new file (create mode in i_mode)
> + *
> + * The file permission bits in inode->i_mode must be set to the create mode by
> + * the caller.
> + *
> + * If there is an inheritable acl, the maximum permissions that the acl grants
> + * will be computed and permissions not granted by the acl will be removed from
> + * inode->i_mode.  If there is no inheritable acl, the umask will be applied
> + * instead.
> + */
> +static struct richacl *
> +richacl_inherit_inode(const struct richacl *dir_acl, struct inode *inode)
> +{
> +	struct richacl *acl;
> +	mode_t mask;
> +
> +	acl = richacl_inherit(dir_acl, S_ISDIR(inode->i_mode));
> +	if (acl) {
> +		mask = inode->i_mode;
> +		if (richacl_equiv_mode(acl, &mask) == 0) {
> +			richacl_put(acl);
> +			acl = NULL;

Why is it correct to ignore entirely the inherited acl in this case?

Oh, I see, I'm forgetting that richacl_equiv_mode is setting the mask,
which will get applied at the end of this function.  In my defense,
maybe it's easy to overlook a side effect in an if condition.... But I
don't have a better idea.  OK.

So, nits aside:

	Reviewed-by: J. Bruce Fields <bfields@redhat.com>

--b.

> +		} else {
> +			richacl_compute_max_masks(acl);
> +			/*
> +			 * Ensure that the acl will not grant any permissions
> +			 * beyond the create mode.
> +			 */
> +			acl->a_flags |= RICHACL_MASKED;
> +			acl->a_owner_mask &=
> +				richacl_mode_to_mask(inode->i_mode >> 6);
> +			acl->a_group_mask &=
> +				richacl_mode_to_mask(inode->i_mode >> 3);
> +			acl->a_other_mask &=
> +				richacl_mode_to_mask(inode->i_mode);
> +			mask = ~S_IRWXUGO | richacl_masks_to_mode(acl);
> +		}
> +	} else
> +		mask = ~current_umask();
> +
> +	inode->i_mode &= mask;
> +	return acl;
> +}
> +
> +struct richacl *richacl_create(struct inode *inode, struct inode *dir)
> +{
> +	struct richacl *dir_acl, *acl = NULL;
> +
> +	if (S_ISLNK(inode->i_mode))
> +		return NULL;
> +	dir_acl = get_richacl(dir);
> +	if (dir_acl) {
> +		if (IS_ERR(dir_acl))
> +			return dir_acl;
> +		acl = richacl_inherit_inode(dir_acl, inode);
> +		richacl_put(dir_acl);
> +	} else
> +		inode->i_mode &= ~current_umask();
> +	return acl;
> +}
> +EXPORT_SYMBOL_GPL(richacl_create);
> diff --git a/include/linux/richacl.h b/include/linux/richacl.h
> index 6535ce5..9bf95c2 100644
> --- a/include/linux/richacl.h
> +++ b/include/linux/richacl.h
> @@ -305,8 +305,10 @@ extern unsigned int richacl_want_to_mask(unsigned int);
>  extern void richacl_compute_max_masks(struct richacl *);
>  extern struct richacl *richacl_chmod(struct richacl *, mode_t);
>  extern int richacl_equiv_mode(const struct richacl *, mode_t *);
> +extern struct richacl *richacl_inherit(const struct richacl *, int);
>  
>  /* richacl_inode.c */
>  extern int richacl_permission(struct inode *, const struct richacl *, int);
> +extern struct richacl *richacl_create(struct inode *, struct inode *);
>  
>  #endif /* __RICHACL_H */
> -- 
> 2.4.3
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Andreas Gruenbacher Sept. 21, 2015, 8:37 p.m. UTC | #2
2015-09-18 19:58 GMT+02:00 J. Bruce Fields <bfields@fieldses.org>:
> On Sat, Sep 05, 2015 at 12:27:09PM +0200, Andreas Gruenbacher wrote:
>> +                     if (dir_ace->e_flags & RICHACE_NO_PROPAGATE_INHERIT_ACE)
>> +                             ace->e_flags &= ~RICHACE_INHERITANCE_FLAGS;
>> +                     else if ((dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE) &&
>> +                              !(dir_ace->e_flags & RICHACE_DIRECTORY_INHERIT_ACE))
>
> The FILE_INHERIT_ACE check there is redundant since we already know
> dir_ace is inheritable.
>
> (So, OK, it isn't wrong to check it again but let's not make this
> condition any more complicated than necessary.)

Indeed, we can turn it into:

        if (dir_ace->e_flags & RICHACE_NO_PROPAGATE_INHERIT_ACE)
                ace->e_flags &= ~RICHACE_INHERITANCE_FLAGS;
        else if (!(dir_ace->e_flags & RICHACE_DIRECTORY_INHERIT_ACE))
                ace->e_flags |= RICHACE_INHERIT_ONLY_ACE;

>> +static struct richacl *
>> +richacl_inherit_inode(const struct richacl *dir_acl, struct inode *inode)
>> +{
>> +     struct richacl *acl;
>> +     mode_t mask;
>> +
>> +     acl = richacl_inherit(dir_acl, S_ISDIR(inode->i_mode));
>> +     if (acl) {
>> +             mask = inode->i_mode;
>> +             if (richacl_equiv_mode(acl, &mask) == 0) {
>> +                     richacl_put(acl);
>> +                     acl = NULL;
>
> Why is it correct to ignore entirely the inherited acl in this case?
>
> Oh, I see, I'm forgetting that richacl_equiv_mode is setting the mask,
> which will get applied at the end of this function.  In my defense,
> maybe it's easy to overlook a side effect in an if condition.... But I
> don't have a better idea.  OK.

Yes. I'll leave that as it is.

> So, nits aside:
>
>         Reviewed-by: J. Bruce Fields <bfields@redhat.com>

Thanks,
Andreas
--
To unsubscribe from this list: send the line "unsubscribe linux-cifs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Patch
diff mbox

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 106e988..fda407d 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -483,3 +483,72 @@  richacl_equiv_mode(const struct richacl *acl, mode_t *mode_p)
 	return 0;
 }
 EXPORT_SYMBOL_GPL(richacl_equiv_mode);
+
+/**
+ * richacl_inherit  -  compute the inherited acl of a new file
+ * @dir_acl:	acl of the containing directory
+ * @isdir:	inherit by a directory or non-directory?
+ *
+ * A directory can have acl entries which files and/or directories created
+ * inside the directory will inherit.  This function computes the acl for such
+ * a new file.  If there is no inheritable acl, it will return %NULL.
+ */
+struct richacl *
+richacl_inherit(const struct richacl *dir_acl, int isdir)
+{
+	const struct richace *dir_ace;
+	struct richacl *acl = NULL;
+	struct richace *ace;
+	int count = 0;
+
+	if (isdir) {
+		richacl_for_each_entry(dir_ace, dir_acl) {
+			if (!richace_is_inheritable(dir_ace))
+				continue;
+			count++;
+		}
+		if (!count)
+			return NULL;
+		acl = richacl_alloc(count, GFP_KERNEL);
+		if (!acl)
+			return ERR_PTR(-ENOMEM);
+		ace = acl->a_entries;
+		richacl_for_each_entry(dir_ace, dir_acl) {
+			if (!richace_is_inheritable(dir_ace))
+				continue;
+			richace_copy(ace, dir_ace);
+			if (dir_ace->e_flags & RICHACE_NO_PROPAGATE_INHERIT_ACE)
+				ace->e_flags &= ~RICHACE_INHERITANCE_FLAGS;
+			else if ((dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE) &&
+				 !(dir_ace->e_flags & RICHACE_DIRECTORY_INHERIT_ACE))
+				ace->e_flags |= RICHACE_INHERIT_ONLY_ACE;
+			ace++;
+		}
+	} else {
+		richacl_for_each_entry(dir_ace, dir_acl) {
+			if (!(dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE))
+				continue;
+			count++;
+		}
+		if (!count)
+			return NULL;
+		acl = richacl_alloc(count, GFP_KERNEL);
+		if (!acl)
+			return ERR_PTR(-ENOMEM);
+		ace = acl->a_entries;
+		richacl_for_each_entry(dir_ace, dir_acl) {
+			if (!(dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE))
+				continue;
+			richace_copy(ace, dir_ace);
+			ace->e_flags &= ~RICHACE_INHERITANCE_FLAGS;
+			/*
+			 * RICHACE_DELETE_CHILD is meaningless for
+			 * non-directories, so clear it.
+			 */
+			ace->e_mask &= ~RICHACE_DELETE_CHILD;
+			ace++;
+		}
+	}
+
+	return acl;
+}
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
index dc2a69f..f3f1f84 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -220,3 +220,68 @@  out:
 	return denied ? -EACCES : 0;
 }
 EXPORT_SYMBOL_GPL(richacl_permission);
+
+/**
+ * richacl_inherit_inode  -  compute inherited acl and file mode
+ * @dir_acl:	acl of the containing directory
+ * @inode:	inode of the new file (create mode in i_mode)
+ *
+ * The file permission bits in inode->i_mode must be set to the create mode by
+ * the caller.
+ *
+ * If there is an inheritable acl, the maximum permissions that the acl grants
+ * will be computed and permissions not granted by the acl will be removed from
+ * inode->i_mode.  If there is no inheritable acl, the umask will be applied
+ * instead.
+ */
+static struct richacl *
+richacl_inherit_inode(const struct richacl *dir_acl, struct inode *inode)
+{
+	struct richacl *acl;
+	mode_t mask;
+
+	acl = richacl_inherit(dir_acl, S_ISDIR(inode->i_mode));
+	if (acl) {
+		mask = inode->i_mode;
+		if (richacl_equiv_mode(acl, &mask) == 0) {
+			richacl_put(acl);
+			acl = NULL;
+		} else {
+			richacl_compute_max_masks(acl);
+			/*
+			 * Ensure that the acl will not grant any permissions
+			 * beyond the create mode.
+			 */
+			acl->a_flags |= RICHACL_MASKED;
+			acl->a_owner_mask &=
+				richacl_mode_to_mask(inode->i_mode >> 6);
+			acl->a_group_mask &=
+				richacl_mode_to_mask(inode->i_mode >> 3);
+			acl->a_other_mask &=
+				richacl_mode_to_mask(inode->i_mode);
+			mask = ~S_IRWXUGO | richacl_masks_to_mode(acl);
+		}
+	} else
+		mask = ~current_umask();
+
+	inode->i_mode &= mask;
+	return acl;
+}
+
+struct richacl *richacl_create(struct inode *inode, struct inode *dir)
+{
+	struct richacl *dir_acl, *acl = NULL;
+
+	if (S_ISLNK(inode->i_mode))
+		return NULL;
+	dir_acl = get_richacl(dir);
+	if (dir_acl) {
+		if (IS_ERR(dir_acl))
+			return dir_acl;
+		acl = richacl_inherit_inode(dir_acl, inode);
+		richacl_put(dir_acl);
+	} else
+		inode->i_mode &= ~current_umask();
+	return acl;
+}
+EXPORT_SYMBOL_GPL(richacl_create);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 6535ce5..9bf95c2 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -305,8 +305,10 @@  extern unsigned int richacl_want_to_mask(unsigned int);
 extern void richacl_compute_max_masks(struct richacl *);
 extern struct richacl *richacl_chmod(struct richacl *, mode_t);
 extern int richacl_equiv_mode(const struct richacl *, mode_t *);
+extern struct richacl *richacl_inherit(const struct richacl *, int);
 
 /* richacl_inode.c */
 extern int richacl_permission(struct inode *, const struct richacl *, int);
+extern struct richacl *richacl_create(struct inode *, struct inode *);
 
 #endif /* __RICHACL_H */