On Sat, Sep 05, 2015 at 12:27:15PM +0200, Andreas Gruenbacher wrote: > The file masks in richacls make chmod and creating new files more > efficient than having to apply file permission bits to the acl directly. > They also allow us to regain permissions from an acl even after a > restrictive chmod, because the permissions in the acl itself are not > being destroyed. In POSIX ACLs, the mask entry has a similar function. > > Protocols like nfsv4 do not understand file masks. For those protocols, > we need to compute nfs4 acls which represent the effective permissions > granted by a richacl: we need to "apply" the file masks to the acl. > > This is the first in a series of richacl transformation patches; it > implements basic richacl editing functions. The following patches > implement algorithms for transforming a richacl so that it can be > evaluated as a plain nfs4 acl, with identical permission check results. Reviewed-by: J. Bruce Fields <bfields@xxxxxxxxxx> Looks nicely done.--b. > > Signed-off-by: Andreas Gruenbacher <agruen@xxxxxxxxxx> > --- > fs/Makefile | 3 +- > fs/richacl_compat.c | 155 +++++++++++++++++++++++++++++++++++++++++ > include/linux/richacl_compat.h | 40 +++++++++++ > 3 files changed, 197 insertions(+), 1 deletion(-) > create mode 100644 fs/richacl_compat.c > create mode 100644 include/linux/richacl_compat.h > > diff --git a/fs/Makefile b/fs/Makefile > index baf385a..2d08c70 100644 > --- a/fs/Makefile > +++ b/fs/Makefile > @@ -48,7 +48,8 @@ obj-$(CONFIG_SYSCTL) += drop_caches.o > > obj-$(CONFIG_FHANDLE) += fhandle.o > obj-$(CONFIG_FS_RICHACL) += richacl.o > -richacl-y := richacl_base.o richacl_inode.o richacl_xattr.o > +richacl-y := richacl_base.o richacl_inode.o \ > + richacl_xattr.o richacl_compat.o > > obj-y += quota/ > > diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c > new file mode 100644 > index 0000000..341e429 > --- /dev/null > +++ b/fs/richacl_compat.c > @@ -0,0 +1,155 @@ > +/* > + * Copyright (C) 2006, 2010 Novell, Inc. > + * Copyright (C) 2015 Red Hat, Inc. > + * Written by Andreas Gruenbacher <agruen@xxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2, or (at your option) any > + * later version. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > +#include <linux/module.h> > +#include <linux/fs.h> > +#include <linux/slab.h> > +#include <linux/richacl_compat.h> > + > +/** > + * richacl_prepare - allocate richacl being constructed > + * > + * Allocate a richacl which can hold @count entries but which is initially > + * empty. > + */ > +struct richacl *richacl_prepare(struct richacl_alloc *alloc, unsigned int count) > +{ > + alloc->acl = richacl_alloc(count, GFP_KERNEL); > + if (!alloc->acl) > + return NULL; > + alloc->acl->a_count = 0; > + alloc->count = count; > + return alloc->acl; > +} > +EXPORT_SYMBOL_GPL(richacl_prepare); > + > +/** > + * richacl_delete_entry - delete an entry in an acl > + * @alloc: acl and number of allocated entries > + * @ace: an entry in @alloc->acl > + * > + * Updates @ace so that it points to the entry before the deleted entry > + * on return. (When deleting the first entry, @ace will point to the > + * (non-existent) entry before the first entry). This behavior is the > + * expected behavior when deleting entries while forward iterating over > + * an acl. > + */ > +void > +richacl_delete_entry(struct richacl_alloc *alloc, struct richace **ace) > +{ > + void *end = alloc->acl->a_entries + alloc->acl->a_count; > + > + memmove(*ace, *ace + 1, end - (void *)(*ace + 1)); > + (*ace)--; > + alloc->acl->a_count--; > +} > +EXPORT_SYMBOL_GPL(richacl_delete_entry); > + > +/** > + * richacl_insert_entry - insert an entry in an acl > + * @alloc: acl and number of allocated entries > + * @ace: entry before which the new entry shall be inserted > + * > + * Insert a new entry in @alloc->acl at position @ace and zero-initialize > + * it. This may require reallocating @alloc->acl. > + */ > +int > +richacl_insert_entry(struct richacl_alloc *alloc, struct richace **ace) > +{ > + struct richacl *acl = alloc->acl; > + unsigned int index = *ace - acl->a_entries; > + size_t tail_size = (acl->a_count - index) * sizeof(struct richace); > + > + if (alloc->count == acl->a_count) { > + size_t new_size = sizeof(struct richacl) + > + (acl->a_count + 1) * sizeof(struct richace); > + > + acl = krealloc(acl, new_size, GFP_KERNEL); > + if (!acl) > + return -1; > + *ace = acl->a_entries + index; > + alloc->acl = acl; > + alloc->count++; > + } > + > + memmove(*ace + 1, *ace, tail_size); > + memset(*ace, 0, sizeof(**ace)); > + acl->a_count++; > + return 0; > +} > +EXPORT_SYMBOL_GPL(richacl_insert_entry); > + > +/** > + * richacl_append_entry - append an entry to an acl > + * @alloc: acl and number of allocated entries > + * > + * This may require reallocating @alloc->acl. > + */ > +struct richace *richacl_append_entry(struct richacl_alloc *alloc) > +{ > + struct richacl *acl = alloc->acl; > + struct richace *ace = acl->a_entries + acl->a_count; > + > + if (alloc->count > alloc->acl->a_count) { > + acl->a_count++; > + return ace; > + } > + return richacl_insert_entry(alloc, &ace) ? NULL : ace; > +} > +EXPORT_SYMBOL_GPL(richacl_append_entry); > + > +/** > + * richace_change_mask - set the mask of @ace to @mask > + * @alloc: acl and number of allocated entries > + * @ace: entry to modify > + * @mask: new mask for @ace > + * > + * If @ace is inheritable, a inherit-only ace is inserted before @ace which > + * includes the inheritable permissions of @ace and the inheritance flags of > + * @ace are cleared before changing the mask. > + * > + * If @mask is 0, the original ace is turned into an inherit-only entry if > + * there are any inheritable permissions, and removed otherwise. > + * > + * The returned @ace points to the modified or inserted effective-only acl > + * entry if that entry exists, to the entry that has become inheritable-only, > + * or else to the previous entry in the acl. > + */ > +static int > +richace_change_mask(struct richacl_alloc *alloc, struct richace **ace, > + unsigned int mask) > +{ > + if (mask && (*ace)->e_mask == mask) > + (*ace)->e_flags &= ~RICHACE_INHERIT_ONLY_ACE; > + else if (mask & ~RICHACE_POSIX_ALWAYS_ALLOWED) { > + if (richace_is_inheritable(*ace)) { > + if (richacl_insert_entry(alloc, ace)) > + return -1; > + richace_copy(*ace, *ace + 1); > + (*ace)->e_flags |= RICHACE_INHERIT_ONLY_ACE; > + (*ace)++; > + (*ace)->e_flags &= ~RICHACE_INHERITANCE_FLAGS | > + RICHACE_INHERITED_ACE; > + } > + (*ace)->e_mask = mask; > + } else { > + if (richace_is_inheritable(*ace)) > + (*ace)->e_flags |= RICHACE_INHERIT_ONLY_ACE; > + else > + richacl_delete_entry(alloc, ace); > + } > + return 0; > +} > diff --git a/include/linux/richacl_compat.h b/include/linux/richacl_compat.h > new file mode 100644 > index 0000000..a9ff630 > --- /dev/null > +++ b/include/linux/richacl_compat.h > @@ -0,0 +1,40 @@ > +/* > + * Copyright (C) 2015 Red Hat, Inc. > + * Written by Andreas Gruenbacher <agruenba@xxxxxxxxxx> > + * > + * This program is free software; you can redistribute it and/or modify it > + * under the terms of the GNU General Public License as published by the > + * Free Software Foundation; either version 2, or (at your option) any > + * later version. > + * > + * This program is distributed in the hope that it will be useful, but > + * WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * General Public License for more details. > + */ > + > +#ifndef __RICHACL_COMPAT_H > +#define __RICHACL_COMPAT_H > + > +#include <linux/richacl.h> > + > +/** > + * struct richacl_alloc - remember how many entries are actually allocated > + * @acl: acl with a_count <= @count > + * @count: the actual number of entries allocated in @acl > + * > + * We pass around this structure while modifying an acl so that we do > + * not have to reallocate when we remove existing entries followed by > + * adding new entries. > + */ > +struct richacl_alloc { > + struct richacl *acl; > + unsigned int count; > +}; > + > +struct richacl *richacl_prepare(struct richacl_alloc *, unsigned int); > +struct richace *richacl_append_entry(struct richacl_alloc *); > +int richacl_insert_entry(struct richacl_alloc *, struct richace **); > +void richacl_delete_entry(struct richacl_alloc *, struct richace **); > + > +#endif /* __RICHACL_COMPAT_H */ > -- > 2.4.3 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-nfs" in > the body of a message to majordomo@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html -- To unsubscribe from this list: send the line "unsubscribe linux-api" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html