[PATCH v8 00/41] Richacls

classic Classic list List threaded Threaded
68 messages Options
1234
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 32/41] nfsd: Add support for the MAY_CREATE_{FILE,DIR} permissions

Andreas Gruenbacher-6
For local file systems, the vfs performs the necessary permission checks
for operations like creating files and directories.  NFSd duplicates
several of those checks.  The vfs checks have been extended to check for
additional permissions like MAY_CREATE_FILE and MY_CREATE_DIR; the nfsd
checks currently lack those extensions.

Ideally, all duplicate checks should be removed; for now, just fix the
duplicate checks instead though.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
Acked-by: J. Bruce Fields <[hidden email]>
---
 fs/nfsd/nfs4proc.c |  5 +++--
 fs/nfsd/nfsfh.c    |  8 ++++----
 fs/nfsd/vfs.c      | 28 ++++++++++++++++++++--------
 fs/nfsd/vfs.h      | 17 +++++++++--------
 4 files changed, 36 insertions(+), 22 deletions(-)

diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index a053e78..8d476ff 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -599,14 +599,15 @@ static __be32
 nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
      struct nfsd4_create *create)
 {
+ int access = create->cr_type == NF4DIR ?
+ NFSD_MAY_CREATE_DIR : NFSD_MAY_CREATE_FILE;
  struct svc_fh resfh;
  __be32 status;
  dev_t rdev;
 
  fh_init(&resfh, NFS4_FHSIZE);
 
- status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR,
-   NFSD_MAY_CREATE);
+ status = fh_verify(rqstp, &cstate->current_fh, S_IFDIR, access);
  if (status)
  return status;
 
diff --git a/fs/nfsd/nfsfh.c b/fs/nfsd/nfsfh.c
index 350041a..7159316 100644
--- a/fs/nfsd/nfsfh.c
+++ b/fs/nfsd/nfsfh.c
@@ -319,10 +319,10 @@ fh_verify(struct svc_rqst *rqstp, struct svc_fh *fhp, umode_t type, int access)
  /*
  * We still have to do all these permission checks, even when
  * fh_dentry is already set:
- * - fh_verify may be called multiple times with different
- *  "access" arguments (e.g. nfsd_proc_create calls
- *  fh_verify(...,NFSD_MAY_EXEC) first, then later (in
- *  nfsd_create) calls fh_verify(...,NFSD_MAY_CREATE).
+ * - fh_verify may be called multiple times with different
+ *  "access" arguments (e.g. nfsd_proc_create calls
+ *  fh_verify(...,NFSD_MAY_EXEC) first, then later (in
+ *  nfsd_create) calls fh_verify(...,NFSD_MAY_CREATE_FILE).
  * - in the NFSv4 case, the filehandle may have been filled
  *  in by fh_compose, and given a dentry, but further
  *  compound operations performed with that filehandle
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c
index 45c0497..fb35775 100644
--- a/fs/nfsd/vfs.c
+++ b/fs/nfsd/vfs.c
@@ -1128,6 +1128,8 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
  __be32 err;
  __be32 err2;
  int host_err;
+ int access = (type == S_IFDIR) ?
+ NFSD_MAY_CREATE_DIR : NFSD_MAY_CREATE_FILE;
 
  err = nfserr_perm;
  if (!flen)
@@ -1136,7 +1138,7 @@ nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
  if (isdotent(fname, flen))
  goto out;
 
- err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
+ err = fh_verify(rqstp, fhp, S_IFDIR, access);
  if (err)
  goto out;
 
@@ -1301,7 +1303,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
 
  /* If file doesn't exist, check for permissions to create one */
  if (d_really_is_negative(dchild)) {
- err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
+ err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE_FILE);
  if (err)
  goto out;
  }
@@ -1485,7 +1487,7 @@ nfsd_symlink(struct svc_rqst *rqstp, struct svc_fh *fhp,
  if (isdotent(fname, flen))
  goto out;
 
- err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE);
+ err = fh_verify(rqstp, fhp, S_IFDIR, NFSD_MAY_CREATE_FILE);
  if (err)
  goto out;
 
@@ -1532,7 +1534,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
  __be32 err;
  int host_err;
 
- err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_CREATE);
+ err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_CREATE_FILE);
  if (err)
  goto out;
  err = fh_verify(rqstp, tfhp, 0, NFSD_MAY_NOP);
@@ -1604,11 +1606,12 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
  struct inode *fdir, *tdir;
  __be32 err;
  int host_err;
+ int access;
 
  err = fh_verify(rqstp, ffhp, S_IFDIR, NFSD_MAY_REMOVE);
  if (err)
  goto out;
- err = fh_verify(rqstp, tfhp, S_IFDIR, NFSD_MAY_CREATE);
+ err = fh_verify(rqstp, tfhp, S_IFDIR, NFSD_MAY_NOP);
  if (err)
  goto out;
 
@@ -1647,6 +1650,13 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
  if (odentry == trap)
  goto out_dput_old;
 
+ host_err = 0;
+ access = S_ISDIR(d_inode(odentry)->i_mode) ?
+ NFSD_MAY_CREATE_DIR : NFSD_MAY_CREATE_FILE;
+ err = fh_verify(rqstp, tfhp, S_IFDIR, access);
+ if (err)
+ goto out_dput_old;
+
  ndentry = lookup_one_len(tname, tdentry, tlen);
  host_err = PTR_ERR(ndentry);
  if (IS_ERR(ndentry))
@@ -1672,7 +1682,8 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
  out_dput_old:
  dput(odentry);
  out_nfserr:
- err = nfserrno(host_err);
+ if (host_err)
+ err = nfserrno(host_err);
  /*
  * We cannot rely on fh_unlock on the two filehandles,
  * as that would do the wrong thing if the two directories
@@ -2005,8 +2016,9 @@ nfsd_permission(struct svc_rqst *rqstp, struct svc_export *exp,
     uid_eq(inode->i_uid, current_fsuid()))
  return 0;
 
- /* This assumes  NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC} */
- err = inode_permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC));
+ /* This assumes NFSD_MAY_{READ,WRITE,EXEC} == MAY_{READ,WRITE,EXEC}. */
+ err = inode_permission(inode, acc & (MAY_READ|MAY_WRITE|MAY_EXEC|
+     MAY_CREATE_DIR|MAY_CREATE_FILE));
 
  /* Allow read access to binaries even when mode 111 */
  if (err == -EACCES && S_ISREG(inode->i_mode) &&
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h
index fee2451..c849ef2 100644
--- a/fs/nfsd/vfs.h
+++ b/fs/nfsd/vfs.h
@@ -19,18 +19,19 @@
 #define NFSD_MAY_TRUNC 0x010
 #define NFSD_MAY_LOCK 0x020
 #define NFSD_MAY_MASK 0x03f
+#define NFSD_MAY_CREATE_FILE 0x103 /* == MAY_{EXEC|WRITE|CREATE_FILE} */
+#define NFSD_MAY_CREATE_DIR 0x203 /* == MAY_{EXEC|WRITE|CREATE_DIR} */
 
 /* extra hints to permission and open routines: */
-#define NFSD_MAY_OWNER_OVERRIDE 0x040
-#define NFSD_MAY_LOCAL_ACCESS 0x080 /* for device special files */
-#define NFSD_MAY_BYPASS_GSS_ON_ROOT 0x100
-#define NFSD_MAY_NOT_BREAK_LEASE 0x200
-#define NFSD_MAY_BYPASS_GSS 0x400
-#define NFSD_MAY_READ_IF_EXEC 0x800
+#define NFSD_MAY_OWNER_OVERRIDE 0x04000
+#define NFSD_MAY_LOCAL_ACCESS 0x08000 /* for device special files */
+#define NFSD_MAY_BYPASS_GSS_ON_ROOT 0x10000
+#define NFSD_MAY_NOT_BREAK_LEASE 0x20000
+#define NFSD_MAY_BYPASS_GSS 0x40000
+#define NFSD_MAY_READ_IF_EXEC 0x80000
 
-#define NFSD_MAY_64BIT_COOKIE 0x1000 /* 64 bit readdir cookies for >= NFSv3 */
+#define NFSD_MAY_64BIT_COOKIE 0x100000 /* 64 bit readdir cookies for >= NFSv3 */
 
-#define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE)
 #define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC)
 
 /*
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 33/41] richacl: Add support for unmapped identifiers

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Some remote file systems like nfs may return user or group identifiers
that cannot be mapped to local uids / gids.  Allow to represent such
unmapped identifiers in richacls.  (We still cannot represent unmapped
owners and owning groups, however.)

In the in-memory representation, the richacl is followed by a list of
NUL-terminated strings, with no padding.  Entries with an unmapped
identifier have the RICHACE_UNMAPPED_WHO flag set, and ace->e_id.offs
specifies the offset into this list.  Multiple entries can refer to the
same offset.

The xattr representation is similar, but ace->e_id is ignored, and the
list of unmapped identifier strings contains a string for each acl entry
whose RICHACE_UNMAPPED_WHO flag is set.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 fs/richacl_base.c       | 139 ++++++++++++++++++++++++++++++++++++++++++++----
 fs/richacl_compat.c     |  18 +++----
 fs/richacl_inode.c      |   4 +-
 fs/richacl_xattr.c      |  69 ++++++++++++++++++++----
 include/linux/richacl.h |  33 ++++++++++--
 5 files changed, 227 insertions(+), 36 deletions(-)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index e0357ae..b1b3f5d 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -23,22 +23,25 @@
 MODULE_LICENSE("GPL");
 
 /**
- * richacl_alloc  -  allocate a richacl
+ * __richacl_alloc  -  allocate a richacl
  * @count: number of entries
+ * @unmapped_size: size to reserve for unmapped identifiers
  */
 struct richacl *
-richacl_alloc(int count, gfp_t gfp)
+__richacl_alloc(unsigned int count, size_t unmapped_size, gfp_t gfp)
 {
- size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
+ size_t size = sizeof(struct richacl) + count * sizeof(struct richace) +
+      unmapped_size;
  struct richacl *acl = kzalloc(size, gfp);
 
  if (acl) {
  atomic_set(&acl->a_base.ba_refcount, 1);
  acl->a_count = count;
+ acl->a_unmapped_size = unmapped_size;
  }
  return acl;
 }
-EXPORT_SYMBOL_GPL(richacl_alloc);
+EXPORT_SYMBOL_GPL(__richacl_alloc);
 
 /**
  * richacl_clone  -  create a copy of a richacl
@@ -47,7 +50,8 @@ struct richacl *
 richacl_clone(const struct richacl *acl, gfp_t gfp)
 {
  int count = acl->a_count;
- size_t size = sizeof(struct richacl) + count * sizeof(struct richace);
+ size_t size = sizeof(struct richacl) + count * sizeof(struct richace) +
+      acl->a_unmapped_size;
  struct richacl *dup = kmalloc(size, gfp);
 
  if (dup) {
@@ -59,6 +63,9 @@ richacl_clone(const struct richacl *acl, gfp_t gfp)
 
 /**
  * richace_copy  -  copy an acl entry
+ *
+ * If @from has an unmapped who value (from->e_flags & RICHACE_UNMAPPED_WHO),
+ * it can only be copied within the same acl!
  */
 void
 richace_copy(struct richace *to, const struct richace *from)
@@ -66,6 +73,82 @@ richace_copy(struct richace *to, const struct richace *from)
  memcpy(to, from, sizeof(struct richace));
 }
 
+/**
+ * richacl_add_unmapped_identifier
+ * @pacl: Pointer to an acl
+ * @pace: acl entry within @acl
+ * @who: unmapped identifier
+ * @len: length of @who
+ * @gfp: memory allocation flags
+ *
+ * Add an unmapped identifier to an acl, possibly reallocating the acl.
+ */
+int richacl_add_unmapped_identifier(struct richacl **pacl,
+    struct richace **pace,
+    const char *who,
+    unsigned int len, gfp_t gfp)
+{
+ struct richacl *acl = *pacl;
+ size_t size = sizeof(struct richacl) +
+      acl->a_count * sizeof(struct richace) +
+      acl->a_unmapped_size + len + 1;
+ unsigned int index = *pace - acl->a_entries;
+
+ acl = krealloc(*pacl, size, gfp);
+ if (acl) {
+ char *unmapped = (char *)(acl->a_entries + acl->a_count);
+ struct richace *ace = acl->a_entries + index;
+
+ ace->e_flags |= RICHACE_UNMAPPED_WHO;
+ ace->e_flags &= ~RICHACE_SPECIAL_WHO;
+ ace->e_id.offs = acl->a_unmapped_size;
+ memcpy(unmapped + ace->e_id.offs, who, len);
+ unmapped[ace->e_id.offs + len] = 0;
+ acl->a_unmapped_size += len + 1;
+ *pace = ace;
+ *pacl = acl;
+ return 0;
+ }
+ return -1;
+}
+EXPORT_SYMBOL_GPL(richacl_add_unmapped_identifier);
+
+/**
+ * richace_unmapped_identifier  -  get unmapped identifier
+ * @acl: acl containing @ace
+ * @ace: acl entry
+ *
+ * Get the unmapped identifier of @ace as a NUL-terminated string, or NULL if
+ * @ace doesn't have an unmapped identifier.
+ */
+const char *richace_unmapped_identifier(const struct richace *ace,
+ const struct richacl *acl)
+{
+ const char *unmapped = (char *)(acl->a_entries + acl->a_count);
+
+ if (!(ace->e_flags & RICHACE_UNMAPPED_WHO))
+ return NULL;
+ return unmapped + ace->e_id.offs;
+}
+EXPORT_SYMBOL(richace_unmapped_identifier);
+
+/**
+ * richacl_has_unmapped_identifiers
+ *
+ * Check if an acl has unmapped identifiers.
+ */
+bool richacl_has_unmapped_identifiers(struct richacl *acl)
+{
+ struct richace *ace;
+
+ richacl_for_each_entry(ace, acl) {
+ if (ace->e_flags & RICHACE_UNMAPPED_WHO)
+ return true;
+ }
+ return false;
+}
+EXPORT_SYMBOL_GPL(richacl_has_unmapped_identifiers);
+
 /*
  * richacl_mask_to_mode  -  compute the file permission bits from mask
  * @mask: %RICHACE_* permission mask
@@ -213,7 +296,7 @@ static unsigned int richacl_allowed_to_who(struct richacl *acl,
  richacl_for_each_entry_reverse(ace, acl) {
  if (richace_is_inherit_only(ace))
  continue;
- if (richace_is_same_identifier(ace, who) ||
+ if (richace_is_same_identifier(acl, ace, who) ||
     richace_is_everyone(ace)) {
  if (richace_is_allow(ace))
  allowed |= ace->e_mask;
@@ -502,45 +585,72 @@ richacl_inherit(const struct richacl *dir_acl, int isdir)
  const struct richace *dir_ace;
  struct richacl *acl = NULL;
  struct richace *ace;
- int count = 0;
+ unsigned int count = 0, unmapped_size = 0, offset = 0;
+ const char *dir_unmapped;
+ char *unmapped;
 
  if (isdir) {
  richacl_for_each_entry(dir_ace, dir_acl) {
  if (!richace_is_inheritable(dir_ace))
  continue;
+
  count++;
+ dir_unmapped =
+ richace_unmapped_identifier(dir_ace, dir_acl);
+ if (dir_unmapped)
+ unmapped_size += strlen(dir_unmapped) + 1;
  }
  if (!count)
  return NULL;
- acl = richacl_alloc(count, GFP_KERNEL);
+ acl = __richacl_alloc(count, unmapped_size, GFP_KERNEL);
  if (!acl)
  return ERR_PTR(-ENOMEM);
  ace = acl->a_entries;
+ unmapped = (char *)(acl->a_entries + acl->a_count);
  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_DIRECTORY_INHERIT_ACE))
  ace->e_flags |= RICHACE_INHERIT_ONLY_ACE;
+
+ dir_unmapped =
+ richace_unmapped_identifier(dir_ace, dir_acl);
+ if (dir_unmapped) {
+ size_t sz = strlen(dir_unmapped) + 1;
+
+ ace->e_id.offs = offset;
+ memcpy(unmapped, dir_unmapped, sz);
+ unmapped += sz;
+ offset += sz;
+ }
  ace++;
  }
  } else {
  richacl_for_each_entry(dir_ace, dir_acl) {
  if (!(dir_ace->e_flags & RICHACE_FILE_INHERIT_ACE))
  continue;
+
  count++;
+ dir_unmapped =
+ richace_unmapped_identifier(dir_ace, dir_acl);
+ if (dir_unmapped)
+ unmapped_size += strlen(dir_unmapped) + 1;
  }
  if (!count)
  return NULL;
- acl = richacl_alloc(count, GFP_KERNEL);
+ acl = __richacl_alloc(count, unmapped_size, GFP_KERNEL);
  if (!acl)
  return ERR_PTR(-ENOMEM);
  ace = acl->a_entries;
+ unmapped = (char *)(acl->a_entries + acl->a_count);
  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;
  /*
@@ -548,6 +658,17 @@ richacl_inherit(const struct richacl *dir_acl, int isdir)
  * non-directories, so clear it.
  */
  ace->e_mask &= ~RICHACE_DELETE_CHILD;
+
+ dir_unmapped =
+ richace_unmapped_identifier(dir_ace, dir_acl);
+ if (dir_unmapped) {
+ size_t sz = strlen(dir_unmapped) + 1;
+
+ ace->e_id.offs = offset;
+ memcpy(unmapped, dir_unmapped, sz);
+ unmapped += sz;
+ offset += sz;
+ }
  ace++;
  }
  }
diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index df807c0..39805bc 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -71,11 +71,13 @@ 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);
+ size_t tail_size = (acl->a_count - index) * sizeof(struct richace) +
+   acl->a_unmapped_size;
 
  if (alloc->count == acl->a_count) {
  size_t new_size = sizeof(struct richacl) +
- (acl->a_count + 1) * sizeof(struct richace);
+ (acl->a_count + 1) * sizeof(struct richace) +
+ acl->a_unmapped_size;
 
  acl = krealloc(acl, new_size, GFP_KERNEL);
  if (!acl)
@@ -103,10 +105,6 @@ 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);
@@ -261,12 +259,12 @@ __richacl_propagate_everyone(struct richacl_alloc *alloc, struct richace *who,
  if (richace_is_inherit_only(ace))
  continue;
  if (richace_is_allow(ace)) {
- if (richace_is_same_identifier(ace, who)) {
+ if (richace_is_same_identifier(acl, ace, who)) {
  allow &= ~ace->e_mask;
  allow_last = ace;
  }
  } else if (richace_is_deny(ace)) {
- if (richace_is_same_identifier(ace, who))
+ if (richace_is_same_identifier(acl, ace, who))
  allow &= ~ace->e_mask;
  else if (allow & ace->e_mask)
  allow_last = NULL;
@@ -613,7 +611,7 @@ __richacl_isolate_who(struct richacl_alloc *alloc, struct richace *who,
  richacl_for_each_entry(ace, acl) {
  if (richace_is_inherit_only(ace))
  continue;
- if (richace_is_same_identifier(ace, who))
+ if (richace_is_same_identifier(acl, ace, who))
  deny &= ~ace->e_mask;
  }
  if (!deny)
@@ -629,7 +627,7 @@ __richacl_isolate_who(struct richacl_alloc *alloc, struct richace *who,
  if (richace_is_inherit_only(ace))
  continue;
  if (richace_is_deny(ace)) {
- if (richace_is_same_identifier(ace, who))
+ if (richace_is_same_identifier(acl, ace, who))
  return richace_change_mask(alloc, &ace,
    ace->e_mask | deny);
  } else if (richace_is_allow(ace) &&
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
index d451e83..5d35346 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -160,8 +160,10 @@ richacl_permission(struct inode *inode, const struct richacl *acl,
  } else if (richace_is_unix_group(ace)) {
  if (!in_group_p(ace->e_id.gid))
  continue;
- } else
+ } else if (richace_is_everyone(ace))
  goto entry_matches_everyone;
+ else
+ continue;
 
  /*
  * Apply the group file mask to entries other than owner@ and
diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c
index cd9979d..e9622f6 100644
--- a/fs/richacl_xattr.c
+++ b/fs/richacl_xattr.c
@@ -33,7 +33,8 @@ richacl_from_xattr(struct user_namespace *user_ns,
  const struct richace_xattr *xattr_ace = (void *)(xattr_acl + 1);
  struct richacl *acl;
  struct richace *ace;
- int count;
+ unsigned int count, offset;
+ char *unmapped;
 
  if (size < sizeof(*xattr_acl) ||
     xattr_acl->a_version != RICHACL_XATTR_VERSION ||
@@ -43,10 +44,11 @@ richacl_from_xattr(struct user_namespace *user_ns,
  count = le16_to_cpu(xattr_acl->a_count);
  if (count > RICHACL_XATTR_MAX_COUNT)
  return ERR_PTR(-EINVAL);
- if (size != count * sizeof(*xattr_ace))
+ if (size < count * sizeof(*xattr_ace))
  return ERR_PTR(-EINVAL);
+ size -= count * sizeof(*xattr_ace);
 
- acl = richacl_alloc(count, GFP_NOFS);
+ acl = __richacl_alloc(count, size, GFP_NOFS);
  if (!acl)
  return ERR_PTR(-ENOMEM);
 
@@ -61,6 +63,16 @@ richacl_from_xattr(struct user_namespace *user_ns,
  if (acl->a_other_mask & ~RICHACE_VALID_MASK)
  goto fail_einval;
 
+ unmapped = (char *)(acl->a_entries + count);
+ if (size) {
+ char *xattr_unmapped = (char *)(xattr_ace + count);
+
+ if (xattr_unmapped[size - 1] != 0)
+ goto fail_einval;
+ memcpy(unmapped, xattr_unmapped, size);
+ }
+ offset = 0;
+
  richacl_for_each_entry(ace, acl) {
  ace->e_type  = le16_to_cpu(xattr_ace->e_type);
  ace->e_flags = le16_to_cpu(xattr_ace->e_flags);
@@ -72,6 +84,15 @@ richacl_from_xattr(struct user_namespace *user_ns,
  ace->e_id.special = le32_to_cpu(xattr_ace->e_id);
  if (ace->e_id.special > RICHACE_EVERYONE_SPECIAL_ID)
  goto fail_einval;
+ } else if (ace->e_flags & RICHACE_UNMAPPED_WHO) {
+ size_t sz;
+
+ if (offset == size)
+ goto fail_einval;
+ ace->e_id.offs = offset;
+ sz = strlen(unmapped) + 1;
+ unmapped += sz;
+ offset += sz;
  } else if (ace->e_flags & RICHACE_IDENTIFIER_GROUP) {
  u32 id = le32_to_cpu(xattr_ace->e_id);
 
@@ -88,10 +109,12 @@ richacl_from_xattr(struct user_namespace *user_ns,
  if (ace->e_type > RICHACE_ACCESS_DENIED_ACE_TYPE ||
     (ace->e_mask & ~RICHACE_VALID_MASK))
  goto fail_einval;
-
  xattr_ace++;
  }
 
+ if (offset != size)
+ goto fail_einval;
+
  return acl;
 
 fail_einval:
@@ -107,8 +130,15 @@ size_t
 richacl_xattr_size(const struct richacl *acl)
 {
  size_t size = sizeof(struct richacl_xattr);
+ const struct richace *ace;
 
  size += sizeof(struct richace_xattr) * acl->a_count;
+ richacl_for_each_entry(ace, acl) {
+ const char *unmapped = richace_unmapped_identifier(ace, acl);
+
+ if (unmapped)
+ size += strlen(unmapped) + 1;
+ }
  return size;
 }
 EXPORT_SYMBOL_GPL(richacl_xattr_size);
@@ -127,6 +157,7 @@ richacl_to_xattr(struct user_namespace *user_ns,
  struct richace_xattr *xattr_ace;
  const struct richace *ace;
  size_t real_size;
+ char *xattr_unmapped;
 
  real_size = richacl_xattr_size(acl);
  if (!buffer)
@@ -143,18 +174,33 @@ richacl_to_xattr(struct user_namespace *user_ns,
  xattr_acl->a_other_mask = cpu_to_le32(acl->a_other_mask);
 
  xattr_ace = (void *)(xattr_acl + 1);
+ xattr_unmapped = (char *)(xattr_ace + acl->a_count);
  richacl_for_each_entry(ace, acl) {
+ const char *who;
+
  xattr_ace->e_type = cpu_to_le16(ace->e_type);
  xattr_ace->e_flags = cpu_to_le16(ace->e_flags);
  xattr_ace->e_mask = cpu_to_le32(ace->e_mask);
  if (ace->e_flags & RICHACE_SPECIAL_WHO)
  xattr_ace->e_id = cpu_to_le32(ace->e_id.special);
- else if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
- xattr_ace->e_id =
- cpu_to_le32(from_kgid(user_ns, ace->e_id.gid));
- else
- xattr_ace->e_id =
- cpu_to_le32(from_kuid(user_ns, ace->e_id.uid));
+ else {
+ who = richace_unmapped_identifier(ace, acl);
+ if (who) {
+ size_t sz = strlen(who) + 1;
+
+ xattr_ace->e_id = 0;
+ memcpy(xattr_unmapped, who, sz);
+ xattr_unmapped += sz;
+ } else if (ace->e_flags & RICHACE_IDENTIFIER_GROUP) {
+ u32 id = from_kgid(user_ns, ace->e_id.gid);
+
+ xattr_ace->e_id = cpu_to_le32(id);
+ } else {
+ u32 id = from_kuid(user_ns, ace->e_id.uid);
+
+ xattr_ace->e_id = cpu_to_le32(id);
+ }
+ }
  xattr_ace++;
  }
  return real_size;
@@ -184,7 +230,8 @@ static void richacl_fix_xattr_userns(
  return;
  count = size / sizeof(*xattr_ace);
  for (; count; count--, xattr_ace++) {
- if (xattr_ace->e_flags & cpu_to_le16(RICHACE_SPECIAL_WHO))
+ if (xattr_ace->e_flags & cpu_to_le16(RICHACE_SPECIAL_WHO |
+     RICHACE_UNMAPPED_WHO))
  continue;
  if (xattr_ace->e_flags &
     cpu_to_le16(RICHACE_IDENTIFIER_GROUP)) {
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index f48c04e..1822666 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -29,6 +29,7 @@ struct richace {
  kuid_t uid;
  kgid_t gid;
  unsigned int special;
+ unsigned short offs;  /* unmapped offset */
  } e_id;
 };
 
@@ -39,6 +40,7 @@ struct richacl {
  unsigned int a_other_mask;
  unsigned short a_count;
  unsigned short a_flags;
+ unsigned short a_unmapped_size;
  struct richace a_entries[0];
 };
 
@@ -77,6 +79,7 @@ struct richacl {
 #define RICHACE_INHERIT_ONLY_ACE 0x0008
 #define RICHACE_IDENTIFIER_GROUP 0x0040
 #define RICHACE_INHERITED_ACE 0x0080
+#define RICHACE_UNMAPPED_WHO 0x2000
 #define RICHACE_SPECIAL_WHO 0x4000
 
 #define RICHACE_VALID_FLAGS ( \
@@ -86,6 +89,7 @@ struct richacl {
  RICHACE_INHERIT_ONLY_ACE | \
  RICHACE_IDENTIFIER_GROUP | \
  RICHACE_INHERITED_ACE | \
+ RICHACE_UNMAPPED_WHO | \
  RICHACE_SPECIAL_WHO)
 
 #define RICHACE_INHERITANCE_FLAGS ( \
@@ -310,14 +314,28 @@ richace_is_deny(const struct richace *ace)
  * richace_is_same_identifier  -  are both identifiers the same?
  */
 static inline bool
-richace_is_same_identifier(const struct richace *a, const struct richace *b)
+richace_is_same_identifier(const struct richacl *acl,
+   const struct richace *ace1,
+   const struct richace *ace2)
 {
- return !((a->e_flags ^ b->e_flags) &
- (RICHACE_SPECIAL_WHO | RICHACE_IDENTIFIER_GROUP)) &&
-       !memcmp(&a->e_id, &b->e_id, sizeof(a->e_id));
+ const char *unmapped = (char *)(acl->a_entries + acl->a_count);
+
+ return !((ace1->e_flags ^ ace2->e_flags) &
+ (RICHACE_SPECIAL_WHO |
+  RICHACE_IDENTIFIER_GROUP |
+  RICHACE_UNMAPPED_WHO)) &&
+       ((ace1->e_flags & RICHACE_UNMAPPED_WHO) ?
+ !strcmp(unmapped + ace1->e_id.offs,
+ unmapped + ace2->e_id.offs) :
+ !memcmp(&ace1->e_id, &ace2->e_id, sizeof(ace1->e_id)));
+}
+
+extern struct richacl *__richacl_alloc(unsigned int, size_t, gfp_t);
+static inline struct richacl *richacl_alloc(unsigned int count, gfp_t gfp)
+{
+ return __richacl_alloc(count, 0, gfp);
 }
 
-extern struct richacl *richacl_alloc(int, gfp_t);
 extern struct richacl *richacl_clone(const struct richacl *, gfp_t);
 extern void richace_copy(struct richace *, const struct richace *);
 extern int richacl_masks_to_mode(const struct richacl *);
@@ -327,6 +345,11 @@ 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);
+extern int richacl_add_unmapped_identifier(struct richacl **, struct richace **,
+   const char *, unsigned int, gfp_t);
+extern const char *richace_unmapped_identifier(const struct richace *,
+       const struct richacl *);
+extern bool richacl_has_unmapped_identifiers(struct richacl *);
 
 /* richacl_inode.c */
 extern int richacl_permission(struct inode *, const struct richacl *, int);
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 36/41] sunrpc: Add xdr_init_encode_pages

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Initialize xdr_stream and xdr_buf from a pages array, for encoding into
the pages.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 include/linux/sunrpc/xdr.h |  2 ++
 net/sunrpc/xdr.c           | 25 +++++++++++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index 70c6b92..2c99cff 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -214,6 +214,8 @@ typedef void (*kxdreproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);
 typedef int (*kxdrdproc_t)(void *rqstp, struct xdr_stream *xdr, void *obj);
 
 extern void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p);
+extern void xdr_init_encode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
+  struct page **pages, unsigned int len);
 extern __be32 *xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes);
 extern void xdr_commit_encode(struct xdr_stream *xdr);
 extern void xdr_truncate_encode(struct xdr_stream *xdr, size_t len);
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 63c1c36..f97b96b 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -483,6 +483,31 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
 EXPORT_SYMBOL_GPL(xdr_init_encode);
 
 /**
+ * xdr_init_encode_pages - Initialize struct xdr_stream for encoding into pages
+ * @xdr: pointer to xdr_stream struct
+ * @buf: pointer to XDR buffer in which to encode data
+ * @pages: pages array in which to encode
+ * @len: maximum length of @buf
+ *
+ * Initialize @xdr and @buf for encoding into the @pages array.  If a
+ * page in @pages is NULL, it will be allocated on demand.
+ */
+void xdr_init_encode_pages(struct xdr_stream *xdr, struct xdr_buf *buf,
+   struct page **pages, unsigned int len)
+{
+ memset(buf, 0, sizeof(*buf));
+ buf->pages = pages;
+ buf->page_len = len;
+ buf->buflen = len;
+
+ memset(xdr, 0, sizeof(*xdr));
+ xdr->buf = buf;
+ xdr->iov = buf->head;
+ xdr->page_ptr = pages - 1;
+}
+EXPORT_SYMBOL_GPL(xdr_init_encode_pages);
+
+/**
  * xdr_commit_encode - Ensure all data is written to buffer
  * @xdr: pointer to xdr_stream
  *
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 38/41] nfs: Remove unused xdr page offsets in getacl/setacl arguments

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
The arguments passed around for getacl and setacl xdr encoding, struct
nfs_setaclargs and struct nfs_getaclargs, both contain an array of
pages, an offset into the first page, and the length of the page data.
The offset is unused as it is always zero; remove it.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 fs/nfs/nfs4proc.c       | 5 ++---
 fs/nfs/nfs4xdr.c        | 4 ++--
 include/linux/nfs_xdr.h | 2 --
 3 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index f93b9cd..b3a6558 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -4519,7 +4519,7 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server)
 #define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)
 
 static int buf_to_pages_noslab(const void *buf, size_t buflen,
- struct page **pages, unsigned int *pgbase)
+ struct page **pages)
 {
  struct page *newpage, **spages;
  int rc = 0;
@@ -4663,7 +4663,6 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
  goto out_free;
 
  args.acl_len = npages * PAGE_SIZE;
- args.acl_pgbase = 0;
 
  dprintk("%s  buf %p buflen %zu npages %d args.acl_len %zu\n",
  __func__, buf, buflen, npages, args.acl_len);
@@ -4755,7 +4754,7 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
  return -EOPNOTSUPP;
  if (npages > ARRAY_SIZE(pages))
  return -ERANGE;
- i = buf_to_pages_noslab(buf, buflen, arg.acl_pages, &arg.acl_pgbase);
+ i = buf_to_pages_noslab(buf, buflen, arg.acl_pages);
  if (i < 0)
  return i;
  nfs4_inode_return_delegation(inode);
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 6f6d921..eefed15 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1659,7 +1659,7 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compoun
  *p = cpu_to_be32(FATTR4_WORD0_ACL);
  p = reserve_space(xdr, 4);
  *p = cpu_to_be32(arg->acl_len);
- xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len);
+ xdr_write_pages(xdr, arg->acl_pages, 0, arg->acl_len);
 }
 
 static void
@@ -2491,7 +2491,7 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
  encode_getattr_two(xdr, FATTR4_WORD0_ACL, 0, &hdr);
 
  xdr_inline_pages(&req->rq_rcv_buf, replen << 2,
- args->acl_pages, args->acl_pgbase, args->acl_len);
+ args->acl_pages, 0, args->acl_len);
 
  encode_nops(&hdr);
 }
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 52faf7e..090ade4 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -685,7 +685,6 @@ struct nfs_setaclargs {
  struct nfs4_sequence_args seq_args;
  struct nfs_fh * fh;
  size_t acl_len;
- unsigned int acl_pgbase;
  struct page ** acl_pages;
 };
 
@@ -697,7 +696,6 @@ struct nfs_getaclargs {
  struct nfs4_sequence_args seq_args;
  struct nfs_fh * fh;
  size_t acl_len;
- unsigned int acl_pgbase;
  struct page ** acl_pages;
 };
 
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 40/41] nfs: Add support for the v4.1 dacl attribute

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
The dacl attribute includes Automatic Inheritance flags not supported by
the acl attribute.  it is only supported in NFS version 4.1 and higher.
On systems where NFS version 4.0 is still the default, an additional
mount option is needed:

    mount -t nfs4 -o vers=4.1 [...]

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 fs/nfs/nfs4proc.c       |  2 +-
 fs/nfs/nfs4xdr.c        | 55 ++++++++++++++++++++++++++++++++++++++++++-------
 include/linux/nfs_xdr.h |  2 +-
 3 files changed, 50 insertions(+), 9 deletions(-)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 3888c70..3cd2402 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -4545,7 +4545,7 @@ static struct richacl *__nfs4_get_acl_uncached(struct inode *inode)
  struct nfs_server *server = NFS_SERVER(inode);
  struct page *pages[DIV_ROUND_UP(NFS4ACL_SIZE_MAX, PAGE_SIZE)] = {};
  struct nfs_getaclargs args = {
- .fh = NFS_FH(inode),
+ .inode = inode,
  .acl_pages = pages,
  .acl_len = ARRAY_SIZE(pages) * PAGE_SIZE,
  };
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index f2507d7..d855c6b 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -1661,9 +1661,16 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compoun
  encode_nfs4_stateid(xdr, &zero_stateid);
 
  /* Encode attribute bitmap. */
- p = reserve_space(xdr, 2*4);
- *p++ = cpu_to_be32(1);
- *p = cpu_to_be32(FATTR4_WORD0_ACL);
+ if (arg->server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
+ p = reserve_space(xdr, 3*4);
+ *p++ = cpu_to_be32(2);
+ *p++ = 0;
+ *p = cpu_to_be32(FATTR4_WORD1_DACL);
+ } else {
+ p = reserve_space(xdr, 2*4);
+ *p++ = cpu_to_be32(1);
+ *p = cpu_to_be32(FATTR4_WORD0_ACL);
+ }
 
  attrlen_offset = xdr->buf->len;
  xdr_reserve_space(xdr, 4);  /* to be backfilled later */
@@ -2498,9 +2505,12 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
 
  encode_compound_hdr(xdr, req, &hdr);
  encode_sequence(xdr, &args->seq_args, &hdr);
- encode_putfh(xdr, args->fh, &hdr);
+ encode_putfh(xdr, NFS_FH(args->inode), &hdr);
  replen = hdr.replen + op_decode_hdr_maxsz + 1;
- encode_getattr_two(xdr, FATTR4_WORD0_ACL, FATTR4_WORD1_MODE, &hdr);
+ if (NFS_SERVER(args->inode)->attr_bitmask[1] & FATTR4_WORD1_DACL)
+ encode_getattr_two(xdr, 0, FATTR4_WORD1_MODE | FATTR4_WORD1_DACL, &hdr);
+ else
+ encode_getattr_two(xdr, FATTR4_WORD0_ACL, FATTR4_WORD1_MODE, &hdr);
 
  xdr_inline_pages(&req->rq_rcv_buf, replen << 2,
  args->acl_pages, 0, args->acl_len);
@@ -5408,14 +5418,28 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
 
  if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U)))
  return -EIO;
- if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
+
+ if (bitmap[0] & FATTR4_WORD0_ACL) {
+ struct richace *ace;
+
+ if (bitmap[1] & FATTR4_WORD1_DACL)
+ return -EIO;
+
  acl = decode_acl_entries(xdr, res->server);
  status = PTR_ERR(acl);
  if (IS_ERR(acl))
  goto out;
+
+ status = -EIO;
+ richacl_for_each_entry(ace, acl) {
+ if (ace->e_flags & RICHACE_INHERITED_ACE)
+ goto out;
+ }
  bitmap[0] &= ~FATTR4_WORD0_ACL;
- } else
+ } else if (!(bitmap[1] & FATTR4_WORD1_DACL)) {
  status = -EOPNOTSUPP;
+ goto out;
+ }
 
  status = -EIO;
  if (unlikely(bitmap[0]))
@@ -5424,6 +5448,23 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
  status = decode_attr_mode(xdr, bitmap, &res->mode);
  if (status < 0)
  goto out;
+ if (bitmap[1] & FATTR4_WORD1_DACL) {
+ unsigned int flags;
+ __be32 *p;
+
+ p = xdr_inline_decode(xdr, 4);
+ status = -EIO;
+ if (unlikely(!p))
+ goto out;
+ flags = be32_to_cpup(p);
+
+ acl = decode_acl_entries(xdr, res->server);
+ status = PTR_ERR(acl);
+ if (IS_ERR(acl))
+ goto out;
+ acl->a_flags = flags;
+ bitmap[1] &= ~FATTR4_WORD1_DACL;
+ }
  status = 0;
 
 out:
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 337c341..9c2a078 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -695,7 +695,7 @@ struct nfs_setaclres {
 
 struct nfs_getaclargs {
  struct nfs4_sequence_args seq_args;
- struct nfs_fh * fh;
+ struct inode * inode;
  size_t acl_len;
  struct page ** acl_pages;
 };
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 41/41] richacl: uapi header split

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 include/linux/richacl.h            | 119 +++++--------------------------------
 include/linux/richacl_xattr.h      |  17 +-----
 include/uapi/linux/Kbuild          |   2 +
 include/uapi/linux/richacl.h       | 111 ++++++++++++++++++++++++++++++++++
 include/uapi/linux/richacl_xattr.h |  43 ++++++++++++++
 5 files changed, 173 insertions(+), 119 deletions(-)
 create mode 100644 include/uapi/linux/richacl.h
 create mode 100644 include/uapi/linux/richacl_xattr.h

diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 1822666..8875941 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -17,9 +17,7 @@
 #ifndef __RICHACL_H
 #define __RICHACL_H
 
-#define RICHACE_OWNER_SPECIAL_ID 0
-#define RICHACE_GROUP_SPECIAL_ID 1
-#define RICHACE_EVERYONE_SPECIAL_ID 2
+#include <uapi/linux/richacl.h>
 
 struct richace {
  unsigned short e_type;
@@ -44,43 +42,12 @@ struct richacl {
  struct richace a_entries[0];
 };
 
-#define richacl_for_each_entry(_ace, _acl) \
- for (_ace = (_acl)->a_entries; \
-     _ace != (_acl)->a_entries + (_acl)->a_count; \
-     _ace++)
-
-#define richacl_for_each_entry_reverse(_ace, _acl) \
- for (_ace = (_acl)->a_entries + (_acl)->a_count - 1; \
-     _ace != (_acl)->a_entries - 1; \
-     _ace--)
-
-/* a_flags values */
-#define RICHACL_AUTO_INHERIT 0x01
-#define RICHACL_PROTECTED 0x02
-#define RICHACL_DEFAULTED 0x04
-#define RICHACL_WRITE_THROUGH 0x40
-#define RICHACL_MASKED 0x80
-
 #define RICHACL_VALID_FLAGS ( \
- RICHACL_AUTO_INHERIT | \
- RICHACL_PROTECTED | \
- RICHACL_DEFAULTED | \
- RICHACL_WRITE_THROUGH | \
- RICHACL_MASKED)
-
-/* e_type values */
-#define RICHACE_ACCESS_ALLOWED_ACE_TYPE 0x0000
-#define RICHACE_ACCESS_DENIED_ACE_TYPE 0x0001
-
-/* e_flags bitflags */
-#define RICHACE_FILE_INHERIT_ACE 0x0001
-#define RICHACE_DIRECTORY_INHERIT_ACE 0x0002
-#define RICHACE_NO_PROPAGATE_INHERIT_ACE 0x0004
-#define RICHACE_INHERIT_ONLY_ACE 0x0008
-#define RICHACE_IDENTIFIER_GROUP 0x0040
-#define RICHACE_INHERITED_ACE 0x0080
-#define RICHACE_UNMAPPED_WHO 0x2000
-#define RICHACE_SPECIAL_WHO 0x4000
+ RICHACL_AUTO_INHERIT | \
+ RICHACL_PROTECTED | \
+ RICHACL_DEFAULTED | \
+ RICHACL_WRITE_THROUGH | \
+ RICHACL_MASKED)
 
 #define RICHACE_VALID_FLAGS ( \
  RICHACE_FILE_INHERIT_ACE | \
@@ -99,27 +66,6 @@ struct richacl {
  RICHACE_INHERIT_ONLY_ACE | \
  RICHACE_INHERITED_ACE )
 
-/* e_mask bitflags */
-#define RICHACE_READ_DATA 0x00000001
-#define RICHACE_LIST_DIRECTORY 0x00000001
-#define RICHACE_WRITE_DATA 0x00000002
-#define RICHACE_ADD_FILE 0x00000002
-#define RICHACE_APPEND_DATA 0x00000004
-#define RICHACE_ADD_SUBDIRECTORY 0x00000004
-#define RICHACE_READ_NAMED_ATTRS 0x00000008
-#define RICHACE_WRITE_NAMED_ATTRS 0x00000010
-#define RICHACE_EXECUTE 0x00000020
-#define RICHACE_DELETE_CHILD 0x00000040
-#define RICHACE_READ_ATTRIBUTES 0x00000080
-#define RICHACE_WRITE_ATTRIBUTES 0x00000100
-#define RICHACE_WRITE_RETENTION 0x00000200
-#define RICHACE_WRITE_RETENTION_HOLD 0x00000400
-#define RICHACE_DELETE 0x00010000
-#define RICHACE_READ_ACL 0x00020000
-#define RICHACE_WRITE_ACL 0x00040000
-#define RICHACE_WRITE_OWNER 0x00080000
-#define RICHACE_SYNCHRONIZE 0x00100000
-
 /* Valid RICHACE_* flags for directories and non-directories */
 #define RICHACE_VALID_MASK ( \
  RICHACE_READ_DATA | RICHACE_LIST_DIRECTORY | \
@@ -139,49 +85,16 @@ struct richacl {
  RICHACE_WRITE_OWNER | \
  RICHACE_SYNCHRONIZE)
 
-/*
- * The POSIX permissions are supersets of the following NFSv4 permissions:
- *
- *  - MAY_READ maps to READ_DATA or LIST_DIRECTORY, depending on the type
- *    of the file system object.
- *
- *  - MAY_WRITE maps to WRITE_DATA or RICHACE_APPEND_DATA for files, and to
- *    ADD_FILE, RICHACE_ADD_SUBDIRECTORY, or RICHACE_DELETE_CHILD for directories.
- *
- *  - MAY_EXECUTE maps to RICHACE_EXECUTE.
- *
- *  (Some of these NFSv4 permissions have the same bit values.)
- */
-#define RICHACE_POSIX_MODE_READ ( \
- RICHACE_READ_DATA | \
- RICHACE_LIST_DIRECTORY)
-#define RICHACE_POSIX_MODE_WRITE ( \
- RICHACE_WRITE_DATA | \
- RICHACE_ADD_FILE | \
- RICHACE_APPEND_DATA | \
- RICHACE_ADD_SUBDIRECTORY | \
- RICHACE_DELETE_CHILD)
-#define RICHACE_POSIX_MODE_EXEC RICHACE_EXECUTE
-#define RICHACE_POSIX_MODE_ALL ( \
- RICHACE_POSIX_MODE_READ | \
- RICHACE_POSIX_MODE_WRITE | \
- RICHACE_POSIX_MODE_EXEC)
-/*
- * These permissions are always allowed
- * no matter what the acl says.
- */
-#define RICHACE_POSIX_ALWAYS_ALLOWED ( \
- RICHACE_SYNCHRONIZE | \
- RICHACE_READ_ATTRIBUTES | \
- RICHACE_READ_ACL)
-/*
- * The owner is implicitly granted
- * these permissions under POSIX.
- */
-#define RICHACE_POSIX_OWNER_ALLOWED ( \
- RICHACE_WRITE_ATTRIBUTES | \
- RICHACE_WRITE_OWNER | \
- RICHACE_WRITE_ACL)
+#define richacl_for_each_entry(_ace, _acl) \
+ for (_ace = (_acl)->a_entries; \
+     _ace != (_acl)->a_entries + (_acl)->a_count; \
+     _ace++)
+
+#define richacl_for_each_entry_reverse(_ace, _acl) \
+ for (_ace = (_acl)->a_entries + (_acl)->a_count - 1; \
+     _ace != (_acl)->a_entries - 1; \
+     _ace--)
+
 /**
  * richacl_get  -  grab another reference to a richacl handle
  */
diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h
index f84cc21..eff36a3 100644
--- a/include/linux/richacl_xattr.h
+++ b/include/linux/richacl_xattr.h
@@ -17,24 +17,9 @@
 #ifndef __RICHACL_XATTR_H
 #define __RICHACL_XATTR_H
 
+#include <uapi/linux/richacl_xattr.h>
 #include <linux/richacl.h>
 
-struct richace_xattr {
- __le16 e_type;
- __le16 e_flags;
- __le32 e_mask;
- __le32 e_id;
-};
-
-struct richacl_xattr {
- unsigned char a_version;
- unsigned char a_flags;
- __le16 a_count;
- __le32 a_owner_mask;
- __le32 a_group_mask;
- __le32 a_other_mask;
-};
-
 #define RICHACL_XATTR_VERSION 0
 #define RICHACL_XATTR_MAX_COUNT \
  ((XATTR_SIZE_MAX - sizeof(struct richacl_xattr)) / \
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index f7b2db4..18ad070 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -348,6 +348,8 @@ header-y += reboot.h
 header-y += reiserfs_fs.h
 header-y += reiserfs_xattr.h
 header-y += resource.h
+header-y += richacl.h
+header-y += richacl_xattr.h
 header-y += rfkill.h
 header-y += romfs_fs.h
 header-y += rose.h
diff --git a/include/uapi/linux/richacl.h b/include/uapi/linux/richacl.h
new file mode 100644
index 0000000..6887f88
--- /dev/null
+++ b/include/uapi/linux/richacl.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2006, 2010  Novell, Inc.
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Written by Andreas Gruenbacher <[hidden email]>
+ *
+ * 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 __UAPI_RICHACL_H
+#define __UAPI_RICHACL_H
+
+/* a_flags values */
+#define RICHACL_AUTO_INHERIT 0x01
+#define RICHACL_PROTECTED 0x02
+#define RICHACL_DEFAULTED 0x04
+#define RICHACL_WRITE_THROUGH 0x40
+#define RICHACL_MASKED 0x80
+
+/* e_type values */
+#define RICHACE_ACCESS_ALLOWED_ACE_TYPE 0x0000
+#define RICHACE_ACCESS_DENIED_ACE_TYPE 0x0001
+
+/* e_flags bitflags */
+#define RICHACE_FILE_INHERIT_ACE 0x0001
+#define RICHACE_DIRECTORY_INHERIT_ACE 0x0002
+#define RICHACE_NO_PROPAGATE_INHERIT_ACE 0x0004
+#define RICHACE_INHERIT_ONLY_ACE 0x0008
+#define RICHACE_IDENTIFIER_GROUP 0x0040
+#define RICHACE_INHERITED_ACE 0x0080
+#define RICHACE_UNMAPPED_WHO 0x2000
+#define RICHACE_SPECIAL_WHO 0x4000
+
+/* e_mask bitflags */
+#define RICHACE_READ_DATA 0x00000001
+#define RICHACE_LIST_DIRECTORY 0x00000001
+#define RICHACE_WRITE_DATA 0x00000002
+#define RICHACE_ADD_FILE 0x00000002
+#define RICHACE_APPEND_DATA 0x00000004
+#define RICHACE_ADD_SUBDIRECTORY 0x00000004
+#define RICHACE_READ_NAMED_ATTRS 0x00000008
+#define RICHACE_WRITE_NAMED_ATTRS 0x00000010
+#define RICHACE_EXECUTE 0x00000020
+#define RICHACE_DELETE_CHILD 0x00000040
+#define RICHACE_READ_ATTRIBUTES 0x00000080
+#define RICHACE_WRITE_ATTRIBUTES 0x00000100
+#define RICHACE_WRITE_RETENTION 0x00000200
+#define RICHACE_WRITE_RETENTION_HOLD 0x00000400
+#define RICHACE_DELETE 0x00010000
+#define RICHACE_READ_ACL 0x00020000
+#define RICHACE_WRITE_ACL 0x00040000
+#define RICHACE_WRITE_OWNER 0x00080000
+#define RICHACE_SYNCHRONIZE 0x00100000
+
+/* e_id values */
+#define RICHACE_OWNER_SPECIAL_ID 0
+#define RICHACE_GROUP_SPECIAL_ID 1
+#define RICHACE_EVERYONE_SPECIAL_ID 2
+
+/*
+ * The POSIX permissions are supersets of the following richacl permissions:
+ *
+ *  - MAY_READ maps to READ_DATA or LIST_DIRECTORY, depending on the type
+ *    of the file system object.
+ *
+ *  - MAY_WRITE maps to WRITE_DATA or RICHACE_APPEND_DATA for files, and to
+ *    ADD_FILE, RICHACE_ADD_SUBDIRECTORY, or RICHACE_DELETE_CHILD for directories.
+ *
+ *  - MAY_EXECUTE maps to RICHACE_EXECUTE.
+ *
+ *  (Some of these richacl permissions have the same bit values.)
+ */
+#define RICHACE_POSIX_MODE_READ ( \
+ RICHACE_READ_DATA | \
+ RICHACE_LIST_DIRECTORY)
+#define RICHACE_POSIX_MODE_WRITE ( \
+ RICHACE_WRITE_DATA | \
+ RICHACE_ADD_FILE | \
+ RICHACE_APPEND_DATA | \
+ RICHACE_ADD_SUBDIRECTORY | \
+ RICHACE_DELETE_CHILD)
+#define RICHACE_POSIX_MODE_EXEC RICHACE_EXECUTE
+#define RICHACE_POSIX_MODE_ALL ( \
+ RICHACE_POSIX_MODE_READ | \
+ RICHACE_POSIX_MODE_WRITE | \
+ RICHACE_POSIX_MODE_EXEC)
+
+/*
+ * These permissions are always allowed no matter what the acl says.
+ */
+#define RICHACE_POSIX_ALWAYS_ALLOWED ( \
+ RICHACE_SYNCHRONIZE | \
+ RICHACE_READ_ATTRIBUTES | \
+ RICHACE_READ_ACL)
+
+/*
+ * The owner is implicitly granted these permissions under POSIX.
+ */
+#define RICHACE_POSIX_OWNER_ALLOWED ( \
+ RICHACE_WRITE_ATTRIBUTES | \
+ RICHACE_WRITE_OWNER | \
+ RICHACE_WRITE_ACL)
+
+#endif /* __UAPI_RICHACL_H */
diff --git a/include/uapi/linux/richacl_xattr.h b/include/uapi/linux/richacl_xattr.h
new file mode 100644
index 0000000..6f96bc0
--- /dev/null
+++ b/include/uapi/linux/richacl_xattr.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2006, 2010  Novell, Inc.
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Written by Andreas Gruenbacher <[hidden email]>
+ *
+ * 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 __UAPI_RICHACL_XATTR_H
+#define __UAPI_RICHACL_XATTR_H
+
+#include <linux/types.h>
+#include <linux/xattr.h>
+
+struct richace_xattr {
+ __le16 e_type;
+ __le16 e_flags;
+ __le32 e_mask;
+ __le32 e_id;
+};
+
+struct richacl_xattr {
+ unsigned char a_version;
+ unsigned char a_flags;
+ __le16 a_count;
+ __le32 a_owner_mask;
+ __le32 a_group_mask;
+ __le32 a_other_mask;
+};
+
+#define RICHACL_XATTR_MAX_COUNT \
+ ((XATTR_SIZE_MAX - sizeof(struct richacl_xattr)) / \
+ sizeof(struct richace_xattr))
+
+#endif  /* __UAPI_RICHACL_XATTR_H */
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 39/41] nfs: Add richacl support

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Add support for the "system.richacl" xattr in nfs.  The existing
"system.nfs4_acl" xattr on nfs doesn't map user and group names to uids
and gids; the "system.richacl" xattr does, and only keeps the
on-the-wire names when there is no mapping.  This allows to copy
permissions across different file systems.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 fs/nfs/inode.c            |   3 -
 fs/nfs/nfs4proc.c         | 698 +++++++++++++++++++++++++++++++++-------------
 fs/nfs/nfs4xdr.c          | 179 ++++++++++--
 fs/nfs/super.c            |   4 +-
 include/linux/nfs_fs.h    |   1 -
 include/linux/nfs_fs_sb.h |   2 +
 include/linux/nfs_xdr.h   |   9 +-
 7 files changed, 673 insertions(+), 223 deletions(-)

diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c
index 326d9e1..843d15d 100644
--- a/fs/nfs/inode.c
+++ b/fs/nfs/inode.c
@@ -1852,9 +1852,6 @@ struct inode *nfs_alloc_inode(struct super_block *sb)
  return NULL;
  nfsi->flags = 0UL;
  nfsi->cache_validity = 0UL;
-#if IS_ENABLED(CONFIG_NFS_V4)
- nfsi->nfs4_acl = NULL;
-#endif /* CONFIG_NFS_V4 */
  return &nfsi->vfs_inode;
 }
 EXPORT_SYMBOL_GPL(nfs_alloc_inode);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index b3a6558..3888c70 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -55,6 +55,9 @@
 #include <linux/xattr.h>
 #include <linux/utsname.h>
 #include <linux/freezer.h>
+#include <linux/richacl.h>
+#include <linux/richacl_xattr.h>
+#include <linux/nfs4acl.h>
 
 #include "nfs4_fs.h"
 #include "delegation.h"
@@ -2971,15 +2974,18 @@ static int _nfs4_server_capabilities(struct nfs_server *server, struct nfs_fh *f
  res.attr_bitmask[2] &= FATTR4_WORD2_NFS42_MASK;
  }
  memcpy(server->attr_bitmask, res.attr_bitmask, sizeof(server->attr_bitmask));
- server->caps &= ~(NFS_CAP_ACLS|NFS_CAP_HARDLINKS|
- NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
+ server->caps &= ~(NFS_CAP_ALLOW_ACLS|NFS_CAP_DENY_ACLS|
+ NFS_CAP_HARDLINKS|NFS_CAP_SYMLINKS|NFS_CAP_FILEID|
  NFS_CAP_MODE|NFS_CAP_NLINK|NFS_CAP_OWNER|
  NFS_CAP_OWNER_GROUP|NFS_CAP_ATIME|
  NFS_CAP_CTIME|NFS_CAP_MTIME|
  NFS_CAP_SECURITY_LABEL);
- if (res.attr_bitmask[0] & FATTR4_WORD0_ACL &&
- res.acl_bitmask & ACL4_SUPPORT_ALLOW_ACL)
- server->caps |= NFS_CAP_ACLS;
+ if (res.attr_bitmask[0] & FATTR4_WORD0_ACL) {
+ if (res.acl_bitmask & ACL4_SUPPORT_ALLOW_ACL)
+ server->caps |= NFS_CAP_ALLOW_ACLS;
+ if (res.acl_bitmask & ACL4_SUPPORT_DENY_ACL)
+ server->caps |= NFS_CAP_DENY_ACLS;
+ }
  if (res.has_links != 0)
  server->caps |= NFS_CAP_HARDLINKS;
  if (res.has_symlinks != 0)
@@ -4507,45 +4513,11 @@ static int nfs4_proc_renew(struct nfs_client *clp, struct rpc_cred *cred)
  return 0;
 }
 
-static inline int nfs4_server_supports_acls(struct nfs_server *server)
-{
- return server->caps & NFS_CAP_ACLS;
-}
-
-/* Assuming that XATTR_SIZE_MAX is a multiple of PAGE_SIZE, and that
- * it's OK to put sizeof(void) * (XATTR_SIZE_MAX/PAGE_SIZE) bytes on
- * the stack.
+/* A arbitrary limit; we allocate at most DIV_ROUND_UP(NFS4ACL_SIZE_MAX,
+ * PAGE_SIZE) pages and put an array of DIV_ROUND_UP(NFS4ACL_SIZE_MAX,
+ * PAGE_SIZE) pages on the stack when encoding or decoding acls.
  */
-#define NFS4ACL_MAXPAGES DIV_ROUND_UP(XATTR_SIZE_MAX, PAGE_SIZE)
-
-static int buf_to_pages_noslab(const void *buf, size_t buflen,
- struct page **pages)
-{
- struct page *newpage, **spages;
- int rc = 0;
- size_t len;
- spages = pages;
-
- do {
- len = min_t(size_t, PAGE_SIZE, buflen);
- newpage = alloc_page(GFP_KERNEL);
-
- if (newpage == NULL)
- goto unwind;
- memcpy(page_address(newpage), buf, len);
-                buf += len;
-                buflen -= len;
- *pages++ = newpage;
- rc++;
- } while (buflen != 0);
-
- return rc;
-
-unwind:
- for(; rc > 0; rc--)
- __free_page(spages[rc-1]);
- return -ENOMEM;
-}
+#define NFS4ACL_SIZE_MAX 65536
 
 struct nfs4_cached_acl {
  int cached;
@@ -4553,66 +4525,9 @@ struct nfs4_cached_acl {
  char data[0];
 };
 
-static void nfs4_set_cached_acl(struct inode *inode, struct nfs4_cached_acl *acl)
-{
- struct nfs_inode *nfsi = NFS_I(inode);
-
- spin_lock(&inode->i_lock);
- kfree(nfsi->nfs4_acl);
- nfsi->nfs4_acl = acl;
- spin_unlock(&inode->i_lock);
-}
-
 static void nfs4_zap_acl_attr(struct inode *inode)
 {
- nfs4_set_cached_acl(inode, NULL);
-}
-
-static inline ssize_t nfs4_read_cached_acl(struct inode *inode, char *buf, size_t buflen)
-{
- struct nfs_inode *nfsi = NFS_I(inode);
- struct nfs4_cached_acl *acl;
- int ret = -ENOENT;
-
- spin_lock(&inode->i_lock);
- acl = nfsi->nfs4_acl;
- if (acl == NULL)
- goto out;
- if (buf == NULL) /* user is just asking for length */
- goto out_len;
- if (acl->cached == 0)
- goto out;
- ret = -ERANGE; /* see getxattr(2) man page */
- if (acl->len > buflen)
- goto out;
- memcpy(buf, acl->data, acl->len);
-out_len:
- ret = acl->len;
-out:
- spin_unlock(&inode->i_lock);
- return ret;
-}
-
-static void nfs4_write_cached_acl(struct inode *inode, struct page **pages, size_t pgbase, size_t acl_len)
-{
- struct nfs4_cached_acl *acl;
- size_t buflen = sizeof(*acl) + acl_len;
-
- if (buflen <= PAGE_SIZE) {
- acl = kmalloc(buflen, GFP_KERNEL);
- if (acl == NULL)
- goto out;
- acl->cached = 1;
- _copy_from_pages(acl->data, pages, pgbase, acl_len);
- } else {
- acl = kmalloc(sizeof(*acl), GFP_KERNEL);
- if (acl == NULL)
- goto out;
- acl->cached = 0;
- }
- acl->len = acl_len;
-out:
- nfs4_set_cached_acl(inode, acl);
+ forget_cached_richacl(inode);
 }
 
 /*
@@ -4625,121 +4540,269 @@ out:
  * length. The next getxattr call will then produce another round trip to
  * the server, this time with the input buf of the required size.
  */
-static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
+static struct richacl *__nfs4_get_acl_uncached(struct inode *inode)
 {
- struct page *pages[NFS4ACL_MAXPAGES] = {NULL, };
+ struct nfs_server *server = NFS_SERVER(inode);
+ struct page *pages[DIV_ROUND_UP(NFS4ACL_SIZE_MAX, PAGE_SIZE)] = {};
  struct nfs_getaclargs args = {
  .fh = NFS_FH(inode),
  .acl_pages = pages,
- .acl_len = buflen,
+ .acl_len = ARRAY_SIZE(pages) * PAGE_SIZE,
  };
  struct nfs_getaclres res = {
- .acl_len = buflen,
+ .server = server,
  };
  struct rpc_message msg = {
  .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_GETACL],
  .rpc_argp = &args,
  .rpc_resp = &res,
  };
- unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
- int ret = -ENOMEM, i;
+ int err, i;
 
- /* As long as we're doing a round trip to the server anyway,
- * let's be prepared for a page of acl data. */
- if (npages == 0)
- npages = 1;
- if (npages > ARRAY_SIZE(pages))
- return -ERANGE;
-
- for (i = 0; i < npages; i++) {
- pages[i] = alloc_page(GFP_KERNEL);
- if (!pages[i])
+ if (ARRAY_SIZE(pages) > 1) {
+ /* for decoding across pages */
+ res.acl_scratch = alloc_page(GFP_KERNEL);
+ err = -ENOMEM;
+ if (!res.acl_scratch)
  goto out_free;
  }
 
- /* for decoding across pages */
- res.acl_scratch = alloc_page(GFP_KERNEL);
- if (!res.acl_scratch)
- goto out_free;
-
- args.acl_len = npages * PAGE_SIZE;
-
- dprintk("%s  buf %p buflen %zu npages %d args.acl_len %zu\n",
- __func__, buf, buflen, npages, args.acl_len);
- ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
+ dprintk("%s  args.acl_len %zu\n",
+ __func__, args.acl_len);
+ err = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
      &msg, &args.seq_args, &res.seq_res, 0);
- if (ret)
+ if (err)
  goto out_free;
 
- /* Handle the case where the passed-in buffer is too short */
- if (res.acl_flags & NFS4_ACL_TRUNC) {
- /* Did the user only issue a request for the acl length? */
- if (buf == NULL)
- goto out_ok;
- ret = -ERANGE;
- goto out_free;
- }
- nfs4_write_cached_acl(inode, pages, res.acl_data_offset, res.acl_len);
- if (buf) {
- if (res.acl_len > buflen) {
- ret = -ERANGE;
- goto out_free;
- }
- _copy_from_pages(buf, pages, res.acl_data_offset, res.acl_len);
- }
-out_ok:
- ret = res.acl_len;
+ richacl_compute_max_masks(res.acl);
+ /* FIXME: Set inode->i_mode from res->mode?  */
+ set_cached_richacl(inode, res.acl);
+ err = 0;
+
 out_free:
- for (i = 0; i < npages; i++)
- if (pages[i])
- __free_page(pages[i]);
+ if (err) {
+ richacl_put(res.acl);
+ res.acl = ERR_PTR(err);
+ }
+ for (i = 0; i < ARRAY_SIZE(pages) && pages[i]; i++)
+ __free_page(pages[i]);
  if (res.acl_scratch)
  __free_page(res.acl_scratch);
- return ret;
+ return res.acl;
 }
 
-static ssize_t nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
+static struct richacl *nfs4_get_acl_uncached(struct inode *inode)
 {
  struct nfs4_exception exception = { };
- ssize_t ret;
+ struct richacl *acl;
  do {
- ret = __nfs4_get_acl_uncached(inode, buf, buflen);
- trace_nfs4_get_acl(inode, ret);
- if (ret >= 0)
+ acl = __nfs4_get_acl_uncached(inode);
+ trace_nfs4_get_acl(inode, IS_ERR(acl) ? PTR_ERR(acl) : 0);
+ if (!IS_ERR(acl))
  break;
- ret = nfs4_handle_exception(NFS_SERVER(inode), ret, &exception);
+ acl = ERR_PTR(nfs4_handle_exception(NFS_SERVER(inode),
+      PTR_ERR(acl), &exception));
  } while (exception.retry);
- return ret;
+ return acl;
 }
 
-static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
+static struct richacl *nfs4_proc_get_acl(struct inode *inode)
 {
  struct nfs_server *server = NFS_SERVER(inode);
+ struct richacl *acl;
  int ret;
 
- if (!nfs4_server_supports_acls(server))
- return -EOPNOTSUPP;
+ if (!(server->caps & (NFS_CAP_ALLOW_ACLS | NFS_CAP_DENY_ACLS)))
+ return ERR_PTR(-EOPNOTSUPP);
  ret = nfs_revalidate_inode(server, inode);
  if (ret < 0)
- return ret;
+ return ERR_PTR(ret);
  if (NFS_I(inode)->cache_validity & NFS_INO_INVALID_ACL)
  nfs_zap_acl_cache(inode);
- ret = nfs4_read_cached_acl(inode, buf, buflen);
- if (ret != -ENOENT)
- /* -ENOENT is returned if there is no ACL or if there is an ACL
- * but no cached acl data, just the acl length */
- return ret;
- return nfs4_get_acl_uncached(inode, buf, buflen);
+ acl = get_cached_richacl(inode);
+ if (acl != ACL_NOT_CACHED)
+ return acl;
+ return nfs4_get_acl_uncached(inode);
+}
+
+static int
+richacl_supported(struct nfs_server *server, struct richacl *acl)
+{
+ struct richace *ace;
+
+ if (!(server->caps & (NFS_CAP_ALLOW_ACLS | NFS_CAP_DENY_ACLS)))
+ return -EOPNOTSUPP;
+
+ richacl_for_each_entry(ace, acl) {
+ if (richace_is_allow(ace)) {
+ if (!(server->caps & NFS_CAP_ALLOW_ACLS))
+ return -EINVAL;
+ } else if (richace_is_deny(ace)) {
+ if (!(server->caps & NFS_CAP_DENY_ACLS))
+ return -EINVAL;
+ } else
+ return -EINVAL;
+ }
+ return 0;
 }
 
-static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
+static int
+nfs4_encode_user(struct xdr_stream *xdr, const struct nfs_server *server,
+ kuid_t uid)
+{
+ char name[IDMAP_NAMESZ];
+ int len;
+ __be32 *p;
+
+ len = nfs_map_uid_to_name(server, uid, name, IDMAP_NAMESZ);
+ if (len < 0) {
+ dprintk("nfs: couldn't resolve uid %d to string\n",
+ from_kuid(&init_user_ns, uid));
+ return -ENOENT;
+ }
+ p = xdr_reserve_space(xdr, 4 + len);
+ if (!p)
+ return -EIO;
+ p = xdr_encode_opaque(p, name, len);
+ return 0;
+}
+
+static int
+nfs4_encode_group(struct xdr_stream *xdr, const struct nfs_server *server,
+  kgid_t gid)
+{
+ char name[IDMAP_NAMESZ];
+ int len;
+ __be32 *p;
+
+ len = nfs_map_gid_to_group(server, gid, name, IDMAP_NAMESZ);
+ if (len < 0) {
+ dprintk("nfs: couldn't resolve gid %d to string\n",
+ from_kgid(&init_user_ns, gid));
+ return -ENOENT;
+ }
+ p = xdr_reserve_space(xdr, 4 + len);
+ if (!p)
+ return -EIO;
+ p = xdr_encode_opaque(p, name, len);
+ return 0;
+}
+
+static unsigned int
+nfs4_ace_mask(int minorversion)
+{
+ return minorversion == 0 ? NFS40_ACE_MASK_ALL : NFS4_ACE_MASK_ALL;
+}
+
+static int
+nfs4_encode_ace_who(struct xdr_stream *xdr, const struct nfs_server *server,
+    struct richace *ace, struct richacl *acl)
+{
+ const char *who;
+ __be32 *p;
+
+ if (ace->e_flags & RICHACE_SPECIAL_WHO) {
+ unsigned int special_id = ace->e_id.special;
+ const char *who;
+ unsigned int len;
+
+ if (!nfs4acl_special_id_to_who(special_id, &who, &len)) {
+ WARN_ON_ONCE(1);
+ return -EIO;
+ }
+ p = xdr_reserve_space(xdr, 4 + len);
+ if (!p)
+ return -EIO;
+ xdr_encode_opaque(p, who, len);
+ return 0;
+ } else {
+ who = richace_unmapped_identifier(ace, acl);
+ if (who) {
+ unsigned int len = strlen(who);
+
+ p = xdr_reserve_space(xdr, 4 + len);
+ if (!p)
+ return -EIO;
+ xdr_encode_opaque(p, who, len);
+ return 0;
+ } else if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
+ return nfs4_encode_group(xdr, server, ace->e_id.gid);
+ else
+ return nfs4_encode_user(xdr, server, ace->e_id.uid);
+ }
+}
+
+static int
+nfs4_encode_acl(struct page **pages, unsigned int len, struct richacl *acl,
+ const struct nfs_server *server)
+{
+ int minorversion = server->nfs_client->cl_minorversion;
+ unsigned int ace_mask = nfs4_ace_mask(minorversion);
+ struct xdr_stream xdr;
+ struct xdr_buf buf;
+ __be32 *p;
+ struct richace *ace;
+
+ /* Reject acls not understood by the server */
+ if (server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
+ BUILD_BUG_ON(NFS4_ACE_MASK_ALL != RICHACE_VALID_MASK);
+ } else {
+ if (acl->a_flags)
+ return -EINVAL;
+ richacl_for_each_entry(ace, acl) {
+ if (ace->e_flags & RICHACE_INHERITED_ACE)
+ return -EINVAL;
+ }
+ }
+ richacl_for_each_entry(ace, acl) {
+ if (ace->e_mask & ~ace_mask)
+ return -EINVAL;
+ }
+
+ xdr_init_encode_pages(&xdr, &buf, pages, len);
+
+ if (server->attr_bitmask[1] & FATTR4_WORD1_DACL) {
+ p = xdr_reserve_space(&xdr, 4);
+ if (!p)
+ goto fail;
+ *p = cpu_to_be32(acl ? acl->a_flags : 0);
+ }
+
+ p = xdr_reserve_space(&xdr, 4);
+ if (!p)
+ goto fail;
+ if (!acl) {
+ *p++ = cpu_to_be32(0);
+ return buf.len;
+ }
+ *p++ = cpu_to_be32(acl->a_count);
+
+ richacl_for_each_entry(ace, acl) {
+ p = xdr_reserve_space(&xdr, 4*3);
+ if (!p)
+ goto fail;
+ *p++ = cpu_to_be32(ace->e_type);
+ *p++ = cpu_to_be32(ace->e_flags &
+ ~(RICHACE_SPECIAL_WHO | RICHACE_UNMAPPED_WHO));
+ *p++ = cpu_to_be32(ace->e_mask & NFS4_ACE_MASK_ALL);
+ if (nfs4_encode_ace_who(&xdr, server, ace, acl) != 0)
+ goto fail;
+ }
+
+ return buf.len;
+
+fail:
+ return -ENOMEM;
+}
+
+static int __nfs4_proc_set_acl(struct inode *inode, struct richacl *acl)
 {
  struct nfs_server *server = NFS_SERVER(inode);
- struct page *pages[NFS4ACL_MAXPAGES];
+ struct page *pages[DIV_ROUND_UP(NFS4ACL_SIZE_MAX, PAGE_SIZE) + 1 /* scratch */] = {};
  struct nfs_setaclargs arg = {
+ .server = server,
  .fh = NFS_FH(inode),
  .acl_pages = pages,
- .acl_len = buflen,
  };
  struct nfs_setaclres res;
  struct rpc_message msg = {
@@ -4747,16 +4810,20 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
  .rpc_argp = &arg,
  .rpc_resp = &res,
  };
- unsigned int npages = DIV_ROUND_UP(buflen, PAGE_SIZE);
  int ret, i;
 
- if (!nfs4_server_supports_acls(server))
- return -EOPNOTSUPP;
- if (npages > ARRAY_SIZE(pages))
- return -ERANGE;
- i = buf_to_pages_noslab(buf, buflen, arg.acl_pages);
- if (i < 0)
- return i;
+ ret = richacl_supported(server, acl);
+ if (ret)
+ return ret;
+
+ ret = nfs4_encode_acl(pages, NFS4ACL_SIZE_MAX, acl, server);
+ if (ret < 0) {
+ for (i = 0; i < ARRAY_SIZE(pages) && pages[i]; i++)
+ put_page(pages[i]);
+ return ret;
+ }
+ arg.acl_len = ret;
+
  nfs4_inode_return_delegation(inode);
  ret = nfs4_call_sync(server->client, server, &msg, &arg.seq_args, &res.seq_res, 1);
 
@@ -4764,8 +4831,8 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
  * Free each page after tx, so the only ref left is
  * held by the network stack
  */
- for (; i > 0; i--)
- put_page(pages[i-1]);
+ for (i = 0; i < ARRAY_SIZE(pages) && pages[i]; i++)
+ put_page(pages[i]);
 
  /*
  * Acl update can result in inode attribute update.
@@ -4779,12 +4846,12 @@ static int __nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t bufl
  return ret;
 }
 
-static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen)
+static int nfs4_proc_set_acl(struct inode *inode, struct richacl *acl)
 {
  struct nfs4_exception exception = { };
  int err;
  do {
- err = __nfs4_proc_set_acl(inode, buf, buflen);
+ err = __nfs4_proc_set_acl(inode, acl);
  trace_nfs4_set_acl(inode, err);
  err = nfs4_handle_exception(NFS_SERVER(inode), err,
  &exception);
@@ -6246,34 +6313,283 @@ nfs4_release_lockowner(struct nfs_server *server, struct nfs4_lock_state *lsp)
  rpc_call_async(server->client, &msg, 0, &nfs4_release_lockowner_ops, data);
 }
 
+static int nfs4_xattr_set_richacl(struct dentry *dentry, const char *key,
+  const void *buf, size_t buflen,
+  int flags, int handler_flags)
+{
+ struct inode *inode = d_inode(dentry);
+ struct richacl *acl;
+ int error;
+
+ if (strcmp(key, "") != 0)
+ return -EINVAL;
+
+ if (buf) {
+ acl = richacl_from_xattr(&init_user_ns, buf, buflen);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ error = richacl_apply_masks(&acl, inode->i_uid);
+ } else {
+ /*
+ * "Remove the acl"; only permissions granted by the mode
+ * remain.  We are using the cached mode here which could be
+ * outdated; should we do a GETATTR first to narrow down the
+ * race window?
+ */
+ acl = richacl_from_mode(inode->i_mode);
+ error = 0;
+ }
+
+ if (!error)
+ error = nfs4_proc_set_acl(inode, acl);
+ richacl_put(acl);
+ return error;
+}
+
+static int nfs4_xattr_get_richacl(struct dentry *dentry, const char *key,
+  void *buf, size_t buflen, int handler_flags)
+{
+ struct inode *inode = d_inode(dentry);
+ struct richacl *acl;
+ int error;
+ mode_t mode = inode->i_mode & S_IFMT;
+
+ if (strcmp(key, "") != 0)
+ return -EINVAL;
+
+ acl = nfs4_proc_get_acl(inode);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (acl == NULL)
+ return -ENODATA;
+ error = -ENODATA;
+ if (richacl_equiv_mode(acl, &mode) == 0 &&
+    ((mode ^ inode->i_mode) & S_IRWXUGO) == 0)
+ goto out;
+ error = richacl_to_xattr(&init_user_ns, acl, buf, buflen);
+out:
+ richacl_put(acl);
+ return error;
+}
+
+static size_t nfs4_xattr_list_richacl(struct dentry *dentry, char *list,
+      size_t list_len, const char *name,
+      size_t name_len, int handler_flags)
+{
+ struct nfs_server *server = NFS_SERVER(d_inode(dentry));
+ size_t len = sizeof(XATTR_NAME_RICHACL);
+
+ if (!(server->caps & (NFS_CAP_ALLOW_ACLS | NFS_CAP_DENY_ACLS)))
+ return 0;
+
+ if (list && len <= list_len)
+ memcpy(list, XATTR_NAME_RICHACL, len);
+ return len;
+}
+
 #define XATTR_NAME_NFSV4_ACL "system.nfs4_acl"
 
+static int richacl_to_nfs4_acl(struct nfs_server *server,
+       const struct richacl *acl,
+       void *buf, size_t buflen)
+{
+ const struct richace *ace;
+ __be32 *p = buf;
+ size_t size = 0;
+
+ size += sizeof(*p);
+ if (buflen >= size)
+ *p++ = cpu_to_be32(acl->a_count);
+
+ richacl_for_each_entry(ace, acl) {
+ char who_buf[IDMAP_NAMESZ];
+ const char *who = who_buf;
+ int who_len;
+
+ size += 3 * sizeof(*p);
+ if (buflen >= size) {
+ *p++ = cpu_to_be32(ace->e_type);
+ *p++ = cpu_to_be32(ace->e_flags &
+   ~(RICHACE_INHERITED_ACE |
+     RICHACE_UNMAPPED_WHO |
+     RICHACE_SPECIAL_WHO));
+ *p++ = cpu_to_be32(ace->e_mask);
+ }
+
+ if (richace_is_unix_user(ace)) {
+ who_len = nfs_map_uid_to_name(server, ace->e_id.uid,
+      who_buf, sizeof(who_buf));
+ if (who_len < 0)
+ return -EIO;
+ } else if (richace_is_unix_group(ace)) {
+ who_len = nfs_map_gid_to_group(server, ace->e_id.gid,
+       who_buf, sizeof(who_buf));
+ if (who_len < 0)
+ return -EIO;
+ } else if (ace->e_flags & RICHACE_SPECIAL_WHO) {
+ if (!nfs4acl_special_id_to_who(ace->e_id.special,
+       &who, &who_len))
+ return -EIO;
+ } else {
+ who = richace_unmapped_identifier(ace, acl);
+ if (who)
+ who_len = strlen(who);
+ else
+ return -EIO;
+ }
+
+ size += sizeof(*p) + ALIGN(who_len, sizeof(*p));
+ if (buflen >= size) {
+ unsigned int padding = -who_len & (sizeof(*p) - 1);
+
+ *p++ = cpu_to_be32(who_len);
+ memcpy(p, who, who_len);
+ memset((char *)p + who_len, 0, padding);
+ p += DIV_ROUND_UP(who_len, sizeof(*p));
+ }
+ }
+ if (buflen && buflen < size)
+ return -ERANGE;
+ return size;
+}
+
+static struct richacl *richacl_from_nfs4_acl(struct nfs_server *server,
+     const void *buf, size_t buflen)
+{
+ struct richacl *acl = NULL;
+ struct richace *ace;
+ const __be32 *p = buf;
+ int count, err;
+
+ if (buflen < sizeof(*p))
+ return ERR_PTR(-EINVAL);
+ count = be32_to_cpu(*p++);
+ if (count > RICHACL_XATTR_MAX_COUNT)
+ return ERR_PTR(-EINVAL);
+ buflen -= sizeof(*p);
+ acl = richacl_alloc(count, GFP_NOFS);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+ richacl_for_each_entry(ace, acl) {
+ u32 who_len, size;
+ int special_id;
+ char *who;
+
+ err = -EINVAL;
+ if (buflen < 4 * sizeof(*p))
+ goto out;
+ ace->e_type = be32_to_cpu(*p++);
+ ace->e_flags = be32_to_cpu(*p++);
+ if (ace->e_flags & (RICHACE_SPECIAL_WHO | RICHACE_UNMAPPED_WHO))
+ goto out;
+ ace->e_mask = be32_to_cpu(*p++);
+ who_len = be32_to_cpu(*p++);
+ buflen -= 4 * sizeof(*p);
+ size = ALIGN(who_len, 4);
+ if (buflen < size || size == 0)
+ goto out;
+ who = (char *)p;
+ special_id = nfs4acl_who_to_special_id(who, who_len);
+ if (special_id >= 0) {
+ ace->e_flags |= RICHACE_SPECIAL_WHO;
+ ace->e_id.special = special_id;
+ } else {
+ bool unmappable;
+
+ if (ace->e_flags & RICHACE_IDENTIFIER_GROUP) {
+ err = nfs_map_group_to_gid(server, who, who_len,
+   &ace->e_id.gid);
+ if (err) {
+ dprintk("%s: nfs_map_group_to_gid "
+ "failed!\n", __func__);
+ goto out;
+ }
+ /* FIXME: nfsidmap doesn't distinguish between
+  group nobody and unmappable groups! */
+ unmappable = gid_eq(ace->e_id.gid,
+ make_kgid(&init_user_ns, 99));
+ } else {
+ err = nfs_map_name_to_uid(server, who, who_len,
+  &ace->e_id.uid);
+ if (err) {
+ dprintk("%s: nfs_map_name_to_gid "
+ "failed!\n", __func__);
+ goto out;
+ }
+ /* FIXME: nfsidmap doesn't distinguish between
+  user nobody and unmappable users! */
+ unmappable = uid_eq(ace->e_id.uid,
+ make_kuid(&init_user_ns, 99));
+ }
+ if (unmappable) {
+ err = -ENOMEM;
+ if (richacl_add_unmapped_identifier(&acl, &ace,
+ who, who_len, GFP_NOFS))
+ goto out;
+ }
+ }
+ p += size / sizeof(*p);
+ buflen -= size;
+ }
+ err = -EINVAL;
+ if (buflen != 0)
+ goto out;
+ err = 0;
+
+out:
+ if (err) {
+ richacl_put(acl);
+ acl = ERR_PTR(err);
+ }
+ return acl;
+}
+
 static int nfs4_xattr_set_nfs4_acl(struct dentry *dentry, const char *key,
    const void *buf, size_t buflen,
    int flags, int type)
 {
- if (strcmp(key, "") != 0)
+ struct inode *inode = d_inode(dentry);
+ struct richacl *acl;
+ int error;
+
+ if (!buf || strcmp(key, "") != 0)
  return -EINVAL;
 
- return nfs4_proc_set_acl(d_inode(dentry), buf, buflen);
+ acl = richacl_from_nfs4_acl(NFS_SERVER(inode), (void *)buf, buflen);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ error = nfs4_proc_set_acl(inode, acl);
+ richacl_put(acl);
+ return error;
 }
 
 static int nfs4_xattr_get_nfs4_acl(struct dentry *dentry, const char *key,
    void *buf, size_t buflen, int type)
 {
+ struct inode *inode = d_inode(dentry);
+ struct richacl *acl;
+ int error;
+
  if (strcmp(key, "") != 0)
  return -EINVAL;
-
- return nfs4_proc_get_acl(d_inode(dentry), buf, buflen);
+ acl = nfs4_proc_get_acl(inode);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (acl == NULL)
+ return -ENODATA;
+ error = richacl_to_nfs4_acl(NFS_SERVER(inode), acl, buf, buflen);
+ richacl_put(acl);
+ return error;
 }
 
 static size_t nfs4_xattr_list_nfs4_acl(struct dentry *dentry, char *list,
        size_t list_len, const char *name,
        size_t name_len, int type)
 {
+ struct nfs_server *server = NFS_SERVER(d_inode(dentry));
  size_t len = sizeof(XATTR_NAME_NFSV4_ACL);
 
- if (!nfs4_server_supports_acls(NFS_SERVER(d_inode(dentry))))
+ if (!(server->caps & (NFS_CAP_ALLOW_ACLS | NFS_CAP_DENY_ACLS)))
  return 0;
 
  if (list && len <= list_len)
@@ -8826,6 +9142,13 @@ const struct nfs_rpc_ops nfs_v4_clientops = {
  .clone_server = nfs_clone_server,
 };
 
+static const struct xattr_handler nfs4_xattr_richacl_handler = {
+ .prefix = XATTR_NAME_RICHACL,
+ .list = nfs4_xattr_list_richacl,
+ .get = nfs4_xattr_get_richacl,
+ .set = nfs4_xattr_set_richacl,
+};
+
 static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
  .prefix = XATTR_NAME_NFSV4_ACL,
  .list = nfs4_xattr_list_nfs4_acl,
@@ -8834,6 +9157,7 @@ static const struct xattr_handler nfs4_xattr_nfs4_acl_handler = {
 };
 
 const struct xattr_handler *nfs4_xattr_handlers[] = {
+ &nfs4_xattr_richacl_handler,
  &nfs4_xattr_nfs4_acl_handler,
 #ifdef CONFIG_NFS_V4_SECURITY_LABEL
  &nfs4_xattr_nfs4_label_handler,
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index eefed15..f2507d7 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -52,6 +52,10 @@
 #include <linux/nfs.h>
 #include <linux/nfs4.h>
 #include <linux/nfs_fs.h>
+#include <linux/nfs_idmap.h>
+#include <linux/richacl.h>
+#include <linux/richacl_xattr.h>  /* for RICHACL_XATTR_MAX_COUNT */
+#include <linux/nfs4acl.h>
 
 #include "nfs4_fs.h"
 #include "internal.h"
@@ -1650,16 +1654,24 @@ encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
 static void
 encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compound_hdr *hdr)
 {
- __be32 *p;
+ int attrlen_offset;
+ __be32 attrlen, *p;
 
  encode_op_hdr(xdr, OP_SETATTR, decode_setacl_maxsz, hdr);
  encode_nfs4_stateid(xdr, &zero_stateid);
+
+ /* Encode attribute bitmap. */
  p = reserve_space(xdr, 2*4);
  *p++ = cpu_to_be32(1);
  *p = cpu_to_be32(FATTR4_WORD0_ACL);
- p = reserve_space(xdr, 4);
- *p = cpu_to_be32(arg->acl_len);
+
+ attrlen_offset = xdr->buf->len;
+ xdr_reserve_space(xdr, 4);  /* to be backfilled later */
+
  xdr_write_pages(xdr, arg->acl_pages, 0, arg->acl_len);
+
+ attrlen = htonl(xdr->buf->len - attrlen_offset - 4);
+ write_bytes_to_xdr_buf(xdr->buf, attrlen_offset, &attrlen, 4);
 }
 
 static void
@@ -2488,7 +2500,7 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
  encode_sequence(xdr, &args->seq_args, &hdr);
  encode_putfh(xdr, args->fh, &hdr);
  replen = hdr.replen + op_decode_hdr_maxsz + 1;
- encode_getattr_two(xdr, FATTR4_WORD0_ACL, 0, &hdr);
+ encode_getattr_two(xdr, FATTR4_WORD0_ACL, FATTR4_WORD1_MODE, &hdr);
 
  xdr_inline_pages(&req->rq_rcv_buf, replen << 2,
  args->acl_pages, 0, args->acl_len);
@@ -5260,24 +5272,135 @@ decode_restorefh(struct xdr_stream *xdr)
  return decode_op_hdr(xdr, OP_RESTOREFH);
 }
 
+static int
+nfs4_decode_ace_who(struct richace *ace,
+    const char **unmapped, unsigned int *unmapped_len,
+    const struct nfs_server *server,
+    struct xdr_stream *xdr)
+{
+ char *who;
+ u32 len;
+ int special_id;
+ __be32 *p;
+ int error;
+
+ p = xdr_inline_decode(xdr, 4);
+ if (!p)
+ return -ENOMEM;  /* acl truncated */
+ len = be32_to_cpup(p++);
+ if (len >= XDR_MAX_NETOBJ) {
+ dprintk("%s: name too long (%u)!\n",
+ __func__, len);
+ return -EIO;
+ }
+ who = (char *)xdr_inline_decode(xdr, len);
+ if (!who)
+ return -ENOMEM;  /* acl truncated */
+
+ special_id = nfs4acl_who_to_special_id(who, len);
+ if (special_id >= 0) {
+ ace->e_flags |= RICHACE_SPECIAL_WHO;
+ ace->e_flags &= ~RICHACE_IDENTIFIER_GROUP;
+ ace->e_id.special = special_id;
+ return 0;
+ }
+ if (ace->e_flags & RICHACE_IDENTIFIER_GROUP) {
+ error = nfs_map_group_to_gid(server, who, len, &ace->e_id.gid);
+ if (error) {
+ dprintk("%s: nfs_map_group_to_gid failed!\n",
+ __func__);
+ return error;
+ }
+ /* FIXME: nfsidmap doesn't distinguish between group nobody and
+  unmappable groups! */
+ if (gid_eq(ace->e_id.gid, make_kgid(&init_user_ns, 99))) {
+ *unmapped = who;
+ *unmapped_len = len;
+ }
+ } else {
+ error = nfs_map_name_to_uid(server, who, len, &ace->e_id.uid);
+ if (error) {
+ dprintk("%s: nfs_map_name_to_uid failed!\n",
+ __func__);
+ return error;
+ }
+ /* FIXME: nfsidmap doesn't distinguish between user nobody and
+  unmappable users! */
+ if (uid_eq(ace->e_id.uid, make_kuid(&init_user_ns, 99))) {
+ *unmapped = who;
+ *unmapped_len = len;
+ }
+ }
+ return 0;
+}
+
+static struct richacl *
+decode_acl_entries(struct xdr_stream *xdr, const struct nfs_server *server)
+{
+ struct richacl *acl;
+ struct richace *ace;
+ uint32_t count;
+ __be32 *p;
+ int status;
+
+ p = xdr_inline_decode(xdr, 4);
+ if (unlikely(!p))
+ return ERR_PTR(-ENOMEM);  /* acl truncated */
+ count = be32_to_cpup(p);
+ if (count > RICHACL_XATTR_MAX_COUNT)
+ return ERR_PTR(-EIO);
+ acl = richacl_alloc(count, GFP_NOFS);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+ richacl_for_each_entry(ace, acl) {
+ const char *unmapped = NULL;
+ unsigned int unmapped_len;
+
+ p = xdr_inline_decode(xdr, 4*3);
+ status = -ENOMEM;
+ if (unlikely(!p))
+ goto out;  /* acl truncated */
+ ace->e_type = be32_to_cpup(p++);
+ ace->e_flags = be32_to_cpup(p++);
+ status = -EIO;
+ if (ace->e_flags &
+    (RICHACE_SPECIAL_WHO | RICHACE_UNMAPPED_WHO))
+ goto out;
+ ace->e_mask = be32_to_cpup(p++);
+ status = nfs4_decode_ace_who(ace, &unmapped,
+     &unmapped_len, server,
+     xdr);
+ if (status)
+ goto out;
+ if (unmapped) {
+ status = -ENOMEM;
+ if (richacl_add_unmapped_identifier(&acl, &ace,
+ unmapped, unmapped_len,
+ GFP_NOFS))
+ goto out;
+ }
+ }
+ status = 0;
+
+out:
+ if (status) {
+ richacl_put(acl);
+ acl = ERR_PTR(status);
+ }
+ return acl;
+}
+
 static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
  struct nfs_getaclres *res)
 {
  unsigned int savep;
  uint32_t attrlen,
  bitmap[3] = {0};
+ struct richacl *acl = NULL;
  int status;
- unsigned int pg_offset;
 
- res->acl_len = 0;
  if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
  goto out;
-
- xdr_enter_page(xdr, xdr->buf->page_len);
-
- /* Calculate the offset of the page data */
- pg_offset = xdr->buf->head[0].iov_len;
-
  if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
  goto out;
  if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
@@ -5286,24 +5409,28 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
  if (unlikely(bitmap[0] & (FATTR4_WORD0_ACL - 1U)))
  return -EIO;
  if (likely(bitmap[0] & FATTR4_WORD0_ACL)) {
-
- /* The bitmap (xdr len + bitmaps) and the attr xdr len words
- * are stored with the acl data to handle the problem of
- * variable length bitmaps.*/
- res->acl_data_offset = xdr_stream_pos(xdr) - pg_offset;
- res->acl_len = attrlen;
-
- /* Check for receive buffer overflow */
- if (res->acl_len > (xdr->nwords << 2) ||
-    res->acl_len + res->acl_data_offset > xdr->buf->page_len) {
- res->acl_flags |= NFS4_ACL_TRUNC;
- dprintk("NFS: acl reply: attrlen %u > page_len %u\n",
- attrlen, xdr->nwords << 2);
- }
+ acl = decode_acl_entries(xdr, res->server);
+ status = PTR_ERR(acl);
+ if (IS_ERR(acl))
+ goto out;
+ bitmap[0] &= ~FATTR4_WORD0_ACL;
  } else
  status = -EOPNOTSUPP;
 
+ status = -EIO;
+ if (unlikely(bitmap[0]))
+ goto out;
+
+ status = decode_attr_mode(xdr, bitmap, &res->mode);
+ if (status < 0)
+ goto out;
+ status = 0;
+
 out:
+ if (status == 0)
+ res->acl = acl;
+ else
+ richacl_put(acl);
  return status;
 }
 
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 383a027..8ced33d 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -2319,7 +2319,7 @@ void nfs_fill_super(struct super_block *sb, struct nfs_mount_info *mount_info)
  /* The VFS shouldn't apply the umask to mode bits. We will do
  * so ourselves when necessary.
  */
- sb->s_flags |= MS_POSIXACL;
+ sb->s_flags |= MS_RICHACL;
  sb->s_time_gran = 1;
  }
 
@@ -2346,7 +2346,7 @@ void nfs_clone_super(struct super_block *sb, struct nfs_mount_info *mount_info)
  /* The VFS shouldn't apply the umask to mode bits. We will do
  * so ourselves when necessary.
  */
- sb->s_flags |= MS_POSIXACL;
+ sb->s_flags |= MS_RICHACL;
  }
 
  nfs_initialise_sb(sb);
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h
index c0e9614..b84e194 100644
--- a/include/linux/nfs_fs.h
+++ b/include/linux/nfs_fs.h
@@ -176,7 +176,6 @@ struct nfs_inode {
  wait_queue_head_t waitqueue;
 
 #if IS_ENABLED(CONFIG_NFS_V4)
- struct nfs4_cached_acl *nfs4_acl;
         /* NFSv4 state */
  struct list_head open_states;
  struct nfs_delegation __rcu *delegation;
diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h
index 570a7df..6c41668 100644
--- a/include/linux/nfs_fs_sb.h
+++ b/include/linux/nfs_fs_sb.h
@@ -243,5 +243,7 @@ struct nfs_server {
 #define NFS_CAP_ALLOCATE (1U << 20)
 #define NFS_CAP_DEALLOCATE (1U << 21)
 #define NFS_CAP_LAYOUTSTATS (1U << 22)
+#define NFS_CAP_ALLOW_ACLS (1U << 23)
+#define NFS_CAP_DENY_ACLS (1U << 24)
 
 #endif
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 090ade4..337c341 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -683,9 +683,10 @@ struct nfs_setattrargs {
 
 struct nfs_setaclargs {
  struct nfs4_sequence_args seq_args;
+ const struct nfs_server * server;
  struct nfs_fh * fh;
- size_t acl_len;
  struct page ** acl_pages;
+ size_t acl_len;
 };
 
 struct nfs_setaclres {
@@ -703,9 +704,9 @@ struct nfs_getaclargs {
 #define NFS4_ACL_TRUNC 0x0001 /* ACL was truncated */
 struct nfs_getaclres {
  struct nfs4_sequence_res seq_res;
- size_t acl_len;
- size_t acl_data_offset;
- int acl_flags;
+ const struct nfs_server * server;
+ struct richacl * acl;
+ umode_t mode;
  struct page * acl_scratch;
 };
 
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 37/41] nfs: Fix GETATTR bitmap verification

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
When decoding GETATTR replies, the client checks the attribute bitmap
for which attributes the server has sent.  It misses bits at the word
boundaries, though; fix that.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 fs/nfs/nfs4xdr.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 788adf3..6f6d921 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -4375,6 +4375,11 @@ static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat)
  goto xdr_error;
  if ((status = decode_attr_files_total(xdr, bitmap, &fsstat->tfiles)) != 0)
  goto xdr_error;
+
+ status = -EIO;
+ if (unlikely(bitmap[0]))
+ goto xdr_error;
+
  if ((status = decode_attr_space_avail(xdr, bitmap, &fsstat->abytes)) != 0)
  goto xdr_error;
  if ((status = decode_attr_space_free(xdr, bitmap, &fsstat->fbytes)) != 0)
@@ -4574,6 +4579,10 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
  goto xdr_error;
  fattr->valid |= status;
 
+ status = -EIO;
+ if (unlikely(bitmap[0]))
+ goto xdr_error;
+
  status = decode_attr_mode(xdr, bitmap, &fmode);
  if (status < 0)
  goto xdr_error;
@@ -4627,6 +4636,10 @@ static int decode_getfattr_attrs(struct xdr_stream *xdr, uint32_t *bitmap,
  goto xdr_error;
  fattr->valid |= status;
 
+ status = -EIO;
+ if (unlikely(bitmap[1]))
+ goto xdr_error;
+
  status = decode_attr_mdsthreshold(xdr, bitmap, fattr->mdsthreshold);
  if (status < 0)
  goto xdr_error;
@@ -4789,12 +4802,22 @@ static int decode_fsinfo(struct xdr_stream *xdr, struct nfs_fsinfo *fsinfo)
  if ((status = decode_attr_maxwrite(xdr, bitmap, &fsinfo->wtmax)) != 0)
  goto xdr_error;
  fsinfo->wtpref = fsinfo->wtmax;
+
+ status = -EIO;
+ if (unlikely(bitmap[0]))
+ goto xdr_error;
+
  status = decode_attr_time_delta(xdr, bitmap, &fsinfo->time_delta);
  if (status != 0)
  goto xdr_error;
  status = decode_attr_pnfstype(xdr, bitmap, &fsinfo->layouttype);
  if (status != 0)
  goto xdr_error;
+
+ status = -EIO;
+ if (unlikely(bitmap[1]))
+ goto xdr_error;
+
  status = decode_attr_layout_blksize(xdr, bitmap, &fsinfo->blksize);
  if (status)
  goto xdr_error;
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 35/41] sunrpc: Allow to demand-allocate pages to encode into

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
When encoding large, variable-length objects such as acls into xdr_bufs,
it is easier to allocate buffer pages on demand rather than precomputing
the required buffer size.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 net/sunrpc/xdr.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 4439ac4..63c1c36 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -537,6 +537,15 @@ static __be32 *xdr_get_next_encode_buffer(struct xdr_stream *xdr,
  */
  xdr->scratch.iov_base = xdr->p;
  xdr->scratch.iov_len = frag1bytes;
+
+ if (!*xdr->page_ptr) {
+ struct page *page = alloc_page(GFP_NOFS);
+
+ if (!page)
+ return NULL;
+ *xdr->page_ptr = page;
+ }
+
  p = page_address(*xdr->page_ptr);
  /*
  * Note this is where the next encode will start after we've
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 34/41] ext4: Don't allow unmapped identifiers in richacls

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Don't allow acls which contain unmapped identifiers: they are meaningful
for remote file systems only.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 fs/ext4/richacl.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/fs/ext4/richacl.c b/fs/ext4/richacl.c
index 6758def..ca6e0cb 100644
--- a/fs/ext4/richacl.c
+++ b/fs/ext4/richacl.c
@@ -68,8 +68,13 @@ ext4_set_richacl(handle_t *handle, struct inode *inode, struct richacl *acl)
  int retval;
 
  if (acl) {
- mode_t mode = inode->i_mode;
+ mode_t mode;
 
+ /* Don't allow acls with unmapped identifiers. */
+ if (richacl_has_unmapped_identifiers(acl))
+ return -EINVAL;
+
+ mode = inode->i_mode;
  if (richacl_equiv_mode(acl, &mode) == 0) {
  inode->i_mode = mode;
  ext4_mark_inode_dirty(handle, inode);
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 30/41] nfsd: Add richacl support

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
On file systems with richacls enabled, get and set richacls directly
instead of converting from / to posix acls.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
Acked-by: J. Bruce Fields <[hidden email]>
---
 fs/nfsd/acl.h      |   3 +-
 fs/nfsd/nfs4acl.c  | 124 ++++++++++++++++++++++++++++++++++++++---------------
 fs/nfsd/nfs4proc.c |   2 +-
 fs/nfsd/nfs4xdr.c  |  34 +++++++++++----
 4 files changed, 117 insertions(+), 46 deletions(-)

diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h
index 1c5deb5..d73c664 100644
--- a/fs/nfsd/acl.h
+++ b/fs/nfsd/acl.h
@@ -53,8 +53,7 @@ __be32 nfsd4_decode_ace_who(struct richace *ace, struct svc_rqst *rqstp,
 __be32 nfsd4_encode_ace_who(struct xdr_stream *xdr, struct svc_rqst *rqstp,
     struct richace *ace);
 
-int nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry,
-  struct richacl **acl);
+struct richacl *nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry);
 __be32 nfsd4_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
      struct richacl *acl);
 
diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
index 6d3bb72..f017a76 100644
--- a/fs/nfsd/nfs4acl.c
+++ b/fs/nfsd/nfs4acl.c
@@ -40,6 +40,8 @@
 #include <linux/nfs_fs.h>
 #include <linux/richacl_compat.h>
 #include <linux/nfs4acl.h>
+#include <linux/xattr.h>
+#include <linux/richacl_xattr.h>
 
 #include "nfsfh.h"
 #include "nfsd.h"
@@ -129,32 +131,28 @@ static short ace2type(struct richace *);
 static void _posix_to_richacl_one(struct posix_acl *, struct richacl_alloc *,
  unsigned int);
 
-int
-nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry,
-      struct richacl **acl)
+static struct richacl *
+nfsd4_get_posix_acl(struct svc_rqst *rqstp, struct dentry *dentry)
 {
  struct inode *inode = d_inode(dentry);
- int error = 0;
  struct posix_acl *pacl = NULL, *dpacl = NULL;
  struct richacl_alloc alloc;
  unsigned int flags = 0;
  int count;
 
  pacl = get_acl(inode, ACL_TYPE_ACCESS);
- if (!pacl)
- pacl = posix_acl_from_mode(inode->i_mode, GFP_KERNEL);
-
- if (IS_ERR(pacl))
- return PTR_ERR(pacl);
+ if (IS_ERR_OR_NULL(pacl))
+ return (void *)pacl;
 
- /* allocate for worst case: one (deny, allow) pair each: */
+ /* Allocate for worst case: one (deny, allow) pair each.  The resulting
+   acl will be released shortly and won't be cached. */
  count = 2 * pacl->a_count;
 
  if (S_ISDIR(inode->i_mode)) {
  flags = FLAG_DIRECTORY;
  dpacl = get_acl(inode, ACL_TYPE_DEFAULT);
  if (IS_ERR(dpacl)) {
- error = PTR_ERR(dpacl);
+ alloc.acl = (void *)dpacl;
  goto rel_pacl;
  }
 
@@ -163,7 +161,7 @@ nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry,
  }
 
  if (!richacl_prepare(&alloc, count)) {
- error = -ENOMEM;
+ alloc.acl = ERR_PTR(-ENOMEM);
  goto out;
  }
 
@@ -172,13 +170,37 @@ nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry,
  if (dpacl)
  _posix_to_richacl_one(dpacl, &alloc, flags | FLAG_DEFAULT_ACL);
 
- *acl = alloc.acl;
-
 out:
  posix_acl_release(dpacl);
 rel_pacl:
  posix_acl_release(pacl);
- return error;
+ return alloc.acl;
+}
+
+struct richacl *
+nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry)
+{
+ struct inode *inode = d_inode(dentry);
+ struct richacl *acl;
+ int error;
+
+ if (IS_RICHACL(inode))
+ acl = get_richacl(inode);
+ else
+ acl = nfsd4_get_posix_acl(rqstp, dentry);
+ if (IS_ERR(acl))
+ return acl;
+ else if (acl == NULL) {
+ acl = richacl_from_mode(inode->i_mode);
+ if (acl == NULL)
+ acl = ERR_PTR(-ENOMEM);
+ }
+ error = richacl_apply_masks(&acl, inode->i_uid);
+ if (error) {
+ richacl_put(acl);
+ acl = ERR_PTR(error);
+ }
+ return acl;
 }
 
 struct posix_acl_summary {
@@ -744,56 +766,88 @@ out_estate:
  return ret;
 }
 
-__be32
-nfsd4_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, struct richacl *acl)
+static int
+nfsd4_set_posix_acl(struct svc_rqst *rqstp, struct dentry *dentry,
+    struct richacl *acl)
 {
- __be32 error;
  int host_error;
- struct dentry *dentry;
- struct inode *inode;
+ struct inode *inode = d_inode(dentry);
  struct posix_acl *pacl = NULL, *dpacl = NULL;
  unsigned int flags = 0;
 
- /* Get inode */
- error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
- if (error)
- return error;
-
- dentry = fhp->fh_dentry;
- inode = d_inode(dentry);
-
  if (!inode->i_op->set_acl || !IS_POSIXACL(inode))
- return nfserr_attrnotsupp;
+ return -EOPNOTSUPP;
 
  if (S_ISDIR(inode->i_mode))
  flags = FLAG_DIRECTORY;
 
  host_error = nfs4_richacl_to_posix(acl, &pacl, &dpacl, flags);
  if (host_error == -EINVAL)
- return nfserr_attrnotsupp;
+ return -EOPNOTSUPP;
  if (host_error < 0)
- goto out_nfserr;
+ return host_error;
 
  host_error = inode->i_op->set_acl(inode, pacl, ACL_TYPE_ACCESS);
  if (host_error < 0)
  goto out_release;
 
- if (S_ISDIR(inode->i_mode)) {
+ if (S_ISDIR(inode->i_mode))
  host_error = inode->i_op->set_acl(inode, dpacl,
   ACL_TYPE_DEFAULT);
- }
 
 out_release:
  posix_acl_release(pacl);
  posix_acl_release(dpacl);
-out_nfserr:
+ return host_error;
+}
+
+static int
+nfsd4_set_richacl(struct svc_rqst *rqstp, struct dentry *dentry,
+  struct richacl *acl)
+{
+ int host_error;
+ struct inode *inode = d_inode(dentry);
+ size_t size = richacl_xattr_size(acl);
+ char *buffer;
+
+ if (!inode->i_op->setxattr || !IS_RICHACL(inode))
+ return -EOPNOTSUPP;
+
+ richacl_compute_max_masks(acl);
+
+ buffer = kmalloc(size, GFP_KERNEL);
+ if (!buffer)
+ return -ENOMEM;
+ richacl_to_xattr(&init_user_ns, acl, buffer, size);
+ host_error = inode->i_op->setxattr(dentry, XATTR_NAME_RICHACL,
+   buffer, size, 0);
+ kfree(buffer);
+ return host_error;
+}
+
+__be32
+nfsd4_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, struct richacl *acl)
+{
+ struct dentry *dentry;
+ int host_error;
+ __be32 error;
+
+ error = fh_verify(rqstp, fhp, 0, NFSD_MAY_SATTR);
+ if (error)
+ return error;
+ dentry = fhp->fh_dentry;
+
+ if (IS_RICHACL(d_inode(dentry)))
+ host_error = nfsd4_set_richacl(rqstp, dentry, acl);
+ else
+ host_error = nfsd4_set_posix_acl(rqstp, dentry, acl);
+
  if (host_error == -EOPNOTSUPP)
  return nfserr_attrnotsupp;
  else
  return nfserrno(host_error);
 }
 
-
 static short
 ace2type(struct richace *ace)
 {
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
index 2430235..1bcfda2 100644
--- a/fs/nfsd/nfs4proc.c
+++ b/fs/nfsd/nfs4proc.c
@@ -110,7 +110,7 @@ check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
  * in current environment or not.
  */
  if (bmval[0] & FATTR4_WORD0_ACL) {
- if (!IS_POSIXACL(d_inode(dentry)))
+ if (!IS_ACL(d_inode(dentry)))
  return nfserr_attrnotsupp;
  }
 
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
index 8603f40..682a7d8 100644
--- a/fs/nfsd/nfs4xdr.c
+++ b/fs/nfsd/nfs4xdr.c
@@ -340,11 +340,24 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
 
  richacl_for_each_entry(ace, *acl) {
  READ_BUF(16); len += 16;
- ace->e_type = be32_to_cpup(p++);
- ace->e_flags = be32_to_cpup(p++);
- ace->e_mask = be32_to_cpup(p++);
- if (ace->e_flags & RICHACE_SPECIAL_WHO)
+
+ dummy32 = be32_to_cpup(p++);
+ if (dummy32 > RICHACE_ACCESS_DENIED_ACE_TYPE)
+ return nfserr_inval;
+ ace->e_type = dummy32;
+
+ dummy32 = be32_to_cpup(p++);
+ if (dummy32 & (~RICHACE_VALID_FLAGS |
+       RICHACE_INHERITED_ACE |
+       RICHACE_SPECIAL_WHO))
  return nfserr_inval;
+ ace->e_flags = dummy32;
+
+ dummy32 = be32_to_cpup(p++);
+ if (dummy32 & ~NFS4_ACE_MASK_ALL)
+ return nfserr_inval;
+ ace->e_mask = dummy32;
+
  dummy32 = be32_to_cpup(p++);
  READ_BUF(dummy32);
  len += XDR_QUADLEN(dummy32) << 2;
@@ -2330,7 +2343,11 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
  fhp = tempfh;
  }
  if (bmval0 & FATTR4_WORD0_ACL) {
- err = nfsd4_get_acl(rqstp, dentry, &acl);
+ acl = nfsd4_get_acl(rqstp, dentry);
+ if (IS_ERR(acl)) {
+ err = PTR_ERR(acl);
+ acl = NULL;
+ }
  if (err == -EOPNOTSUPP)
  bmval0 &= ~FATTR4_WORD0_ACL;
  else if (err == -EINVAL) {
@@ -2370,7 +2387,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
  u32 word1 = nfsd_suppattrs1(minorversion);
  u32 word2 = nfsd_suppattrs2(minorversion);
 
- if (!IS_POSIXACL(dentry->d_inode))
+ if (!IS_ACL(d_inode(dentry)))
  word0 &= ~FATTR4_WORD0_ACL;
  if (!contextsupport)
  word2 &= ~FATTR4_WORD2_SECURITY_LABEL;
@@ -2505,7 +2522,8 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
  if (!p)
  goto out_resource;
  *p++ = cpu_to_be32(ace->e_type);
- *p++ = cpu_to_be32(ace->e_flags & ~RICHACE_SPECIAL_WHO);
+ *p++ = cpu_to_be32(ace->e_flags &
+ ~(RICHACE_SPECIAL_WHO | RICHACE_INHERITED_ACE));
  *p++ = cpu_to_be32(ace->e_mask & NFS4_ACE_MASK_ALL);
  status = nfsd4_encode_ace_who(xdr, rqstp, ace);
  if (status)
@@ -2517,7 +2535,7 @@ out_acl:
  p = xdr_reserve_space(xdr, 4);
  if (!p)
  goto out_resource;
- *p++ = cpu_to_be32(IS_POSIXACL(dentry->d_inode) ?
+ *p++ = cpu_to_be32(IS_ACL(d_inode(dentry)) ?
  ACL4_SUPPORT_ALLOW_ACL|ACL4_SUPPORT_DENY_ACL : 0);
  }
  if (bmval0 & FATTR4_WORD0_CANSETTIME) {
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 26/41] richacl: Apply the file masks to a richacl

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Put all the pieces of the acl transformation puzzle together for
computing a richacl which has the file masks "applied" so that the
standard nfsv4 access check algorithm can be used on the richacl.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 fs/richacl_compat.c     | 101 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |   3 ++
 2 files changed, 104 insertions(+)

diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index 7e25343..c99274f 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -726,3 +726,104 @@ richacl_isolate_group_class(struct richacl_alloc *alloc, unsigned int deny)
  }
  return 0;
 }
+
+/**
+ * __richacl_apply_masks  -  apply the file masks to all aces
+ * @alloc: acl and number of allocated entries
+ *
+ * Apply the owner mask to owner@ aces, the other mask to
+ * everyone@ aces, and the group mask to all other aces.
+ *
+ * The previous transformations have brought the acl into a
+ * form in which applying the masks will not lead to the
+ * accidental loss of permissions anymore.
+ */
+static int
+__richacl_apply_masks(struct richacl_alloc *alloc, kuid_t owner)
+{
+ struct richace *ace;
+
+ richacl_for_each_entry(ace, alloc->acl) {
+ unsigned int mask;
+
+ if (richace_is_inherit_only(ace) || !richace_is_allow(ace))
+ continue;
+ if (richace_is_owner(ace) ||
+    (richace_is_unix_user(ace) && uid_eq(owner, ace->e_id.uid)))
+ mask = alloc->acl->a_owner_mask;
+ else if (richace_is_everyone(ace))
+ mask = alloc->acl->a_other_mask;
+ else
+ mask = alloc->acl->a_group_mask;
+ if (richace_change_mask(alloc, &ace, ace->e_mask & mask))
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * richacl_apply_masks  -  apply the masks to the acl
+ *
+ * Transform @acl so that the standard NFSv4 permission check algorithm (which
+ * is not aware of file masks) will compute the same access decisions as the
+ * richacl permission check algorithm (which looks at the acl and the file
+ * masks).
+ *
+ * This algorithm is split into several steps:
+ *
+ *   - Move everyone@ aces to the end of the acl.  This simplifies the other
+ *     transformations, and allows the everyone@ allow ace at the end of the
+ *     acl to eventually allow permissions to the other class only.
+ *
+ *   - Propagate everyone@ permissions up the acl.  This transformation makes
+ *     sure that the owner and group class aces won't lose any permissions when
+ *     we apply the other mask to the everyone@ allow ace at the end of the acl.
+ *
+ *   - Apply the file masks to all aces.
+ *
+ *   - Make sure everyone is granted the other mask permissions.  This step can
+ *     elevate elevate permissions for the owner and group classes, which is
+ *     corrected later.
+ *
+ *   - Make sure that the group class is not granted any permissions from
+ *     everyone@.
+ *
+ *   - Make sure the owner is granted the owner mask permissions.
+ *
+ *   - Make sure the owner is not granted any permissions beyond the owner
+ *     mask from group class aces or from everyone@.
+ *
+ * NOTE: Depending on the acl and file masks, this algorithm can increase the
+ * number of aces by almost a factor of three in the worst case. This may make
+ * the acl too large for some purposes.
+ */
+int
+richacl_apply_masks(struct richacl **acl, kuid_t owner)
+{
+ if ((*acl)->a_flags & RICHACL_MASKED) {
+ struct richacl_alloc alloc = {
+ .acl = richacl_clone(*acl, GFP_KERNEL),
+ .count = (*acl)->a_count,
+ };
+ unsigned int added = 0;
+
+ if (!alloc.acl)
+ return -ENOMEM;
+ if (richacl_move_everyone_aces_down(&alloc) ||
+    richacl_propagate_everyone(&alloc) ||
+    __richacl_apply_masks(&alloc, owner) ||
+    richacl_set_other_permissions(&alloc, &added) ||
+    richacl_isolate_group_class(&alloc, added) ||
+    richacl_set_owner_permissions(&alloc) ||
+    richacl_isolate_owner_class(&alloc)) {
+ richacl_put(alloc.acl);
+ return -ENOMEM;
+ }
+
+ alloc.acl->a_flags &= ~(RICHACL_WRITE_THROUGH | RICHACL_MASKED);
+ richacl_put(*acl);
+ *acl = alloc.acl;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(richacl_apply_masks);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 832b06c..a945f3c 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -332,4 +332,7 @@ extern struct richacl *richacl_inherit(const struct richacl *, int);
 extern int richacl_permission(struct inode *, const struct richacl *, int);
 extern struct richacl *richacl_create(struct inode *, struct inode *);
 
+/* richacl_compat.c */
+extern int richacl_apply_masks(struct richacl **, kuid_t);
+
 #endif /* __RICHACL_H */
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 24/41] richacl: Set the other permissions to the other mask

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Change the acl so that everyone@ is granted the permissions set in the
other mask.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 fs/richacl_compat.c | 41 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index 16deeb4..21ee31e 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -462,3 +462,44 @@ richacl_set_owner_permissions(struct richacl_alloc *alloc)
  }
  return 0;
 }
+
+/**
+ * richacl_set_other_permissions  -  set the other permissions to the other mask
+ * @alloc: acl and number of allocated entries
+ * @added: permissions added for everyone@
+ *
+ * Change the acl so that everyone@ is granted the permissions set in the other
+ * mask.  This leaves at most one efective everyone@ allow entry at the end of
+ * the acl.  If everyone@ end up being granted additional permissions, these
+ * permissions are returned in @added.
+ */
+static int
+richacl_set_other_permissions(struct richacl_alloc *alloc, unsigned int *added)
+{
+ struct richacl *acl = alloc->acl;
+ unsigned int x = RICHACE_POSIX_ALWAYS_ALLOWED;
+ unsigned int other_mask = acl->a_other_mask & ~x;
+ struct richace *ace;
+
+ if (!(other_mask &&
+      (acl->a_flags & RICHACL_WRITE_THROUGH)))
+ return 0;
+
+ *added = other_mask;
+ ace = acl->a_entries + acl->a_count - 1;
+ if (acl->a_count == 0 ||
+    !richace_is_everyone(ace) ||
+    richace_is_inherit_only(ace)) {
+ ace++;
+ if (richacl_insert_entry(alloc, &ace))
+ return -1;
+ ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = RICHACE_SPECIAL_WHO;
+ ace->e_mask = other_mask;
+ ace->e_id.special = RICHACE_EVERYONE_SPECIAL_ID;
+ } else {
+ *added &= ~ace->e_mask;
+ richace_change_mask(alloc, &ace, other_mask);
+ }
+ return 0;
+}
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 23/41] richacl: Set the owner permissions to the owner mask

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
In the write-through case, change the acl so that owner@ is granted the
permissions set in the owner mask (to match what the permission check
algorithm grants the owner).

Signed-off-by: Andreas Gruenbacher <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 fs/richacl_compat.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/fs/richacl_compat.c b/fs/richacl_compat.c
index 7d007d7..16deeb4 100644
--- a/fs/richacl_compat.c
+++ b/fs/richacl_compat.c
@@ -416,3 +416,49 @@ richacl_propagate_everyone(struct richacl_alloc *alloc)
  }
  return 0;
 }
+
+/**
+ * richacl_set_owner_permissions  -  set the owner permissions to the owner mask
+ *
+ * In the write-through case, change the acl so that owner@ is granted the
+ * permissions set in the owner mask (to match what the permission check
+ * algorithm grants the owner).  This leaves at most one efective owner@ allow
+ * entry at the beginning of the acl.
+ */
+static int
+richacl_set_owner_permissions(struct richacl_alloc *alloc)
+{
+ unsigned int x = RICHACE_POSIX_ALWAYS_ALLOWED;
+ unsigned int owner_mask = alloc->acl->a_owner_mask & ~x;
+ unsigned int denied = 0;
+ struct richace *ace;
+
+ if (!((alloc->acl->a_flags & RICHACL_WRITE_THROUGH)))
+ return 0;
+
+ richacl_for_each_entry(ace, alloc->acl) {
+ if (richace_is_owner(ace)) {
+ if (richace_is_allow(ace) && !(owner_mask & denied)) {
+ richace_change_mask(alloc, &ace, owner_mask);
+ owner_mask = 0;
+ } else
+ richace_change_mask(alloc, &ace, 0);
+ } else {
+ if (richace_is_deny(ace))
+ denied |= ace->e_mask;
+ }
+ }
+
+ if (owner_mask & (denied |
+  ~alloc->acl->a_other_mask |
+  ~alloc->acl->a_group_mask)) {
+ ace = alloc->acl->a_entries;
+ if (richacl_insert_entry(alloc, &ace))
+ return -1;
+ ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
+ ace->e_flags = RICHACE_SPECIAL_WHO;
+ ace->e_mask = owner_mask;
+ ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
+ }
+ return 0;
+}
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 20/41] richacl: acl editing helper functions

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
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.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 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 35e640d..32b391b 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -49,7 +49,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 <[hidden email]>
+ *
+ * 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 <[hidden email]>
+ *
+ * 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-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 17/41] vfs: Add richacl permission checking

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Hook the richacl permission checking function into the vfs.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 fs/namei.c     | 51 +++++++++++++++++++++++++++++++++++++++++++++++++--
 fs/posix_acl.c |  6 +++---
 2 files changed, 52 insertions(+), 5 deletions(-)

diff --git a/fs/namei.c b/fs/namei.c
index 6865cdf..3cc13f0 100644
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -35,6 +35,7 @@
 #include <linux/fs_struct.h>
 #include <linux/posix_acl.h>
 #include <linux/hash.h>
+#include <linux/richacl.h>
 #include <asm/uaccess.h>
 
 #include "internal.h"
@@ -255,7 +256,40 @@ void putname(struct filename *name)
  __putname(name);
 }
 
-static int check_acl(struct inode *inode, int mask)
+static int check_richacl(struct inode *inode, int mask)
+{
+#ifdef CONFIG_FS_RICHACL
+ struct richacl *acl;
+
+ if (mask & MAY_NOT_BLOCK) {
+ acl = get_cached_richacl_rcu(inode);
+ if (!acl)
+ goto no_acl;
+ /* no ->get_richacl() calls in RCU mode... */
+ if (acl == ACL_NOT_CACHED)
+ return -ECHILD;
+ return richacl_permission(inode, acl, mask & ~MAY_NOT_BLOCK);
+ }
+
+ acl = get_richacl(inode);
+ if (IS_ERR(acl))
+ return PTR_ERR(acl);
+ if (acl) {
+ int error = richacl_permission(inode, acl, mask);
+ richacl_put(acl);
+ return error;
+ }
+no_acl:
+#endif
+ if (mask & (MAY_DELETE_SELF | MAY_TAKE_OWNERSHIP |
+    MAY_CHMOD | MAY_SET_TIMES)) {
+ /* File permission bits cannot grant this. */
+ return -EACCES;
+ }
+ return -EAGAIN;
+}
+
+static int check_posix_acl(struct inode *inode, int mask)
 {
 #ifdef CONFIG_FS_POSIX_ACL
  struct posix_acl *acl;
@@ -290,11 +324,24 @@ static int acl_permission_check(struct inode *inode, int mask)
 {
  unsigned int mode = inode->i_mode;
 
+ /*
+ * With POSIX ACLs, the (mode & S_IRWXU) bits exactly match the owner
+ * permissions, and we can skip checking posix acls for the owner.
+ * With richacls, the owner may be granted fewer permissions than the
+ * mode bits seem to suggest (for example, append but not write), and
+ * we always need to check the richacl.
+ */
+
+ if (IS_RICHACL(inode)) {
+ int error = check_richacl(inode, mask);
+ if (error != -EAGAIN)
+ return error;
+ }
  if (likely(uid_eq(current_fsuid(), inode->i_uid)))
  mode >>= 6;
  else {
  if (IS_POSIXACL(inode) && (mode & S_IRWXG)) {
- int error = check_acl(inode, mask);
+ int error = check_posix_acl(inode, mask);
  if (error != -EAGAIN)
  return error;
  }
diff --git a/fs/posix_acl.c b/fs/posix_acl.c
index 1d766a5..3459bd5 100644
--- a/fs/posix_acl.c
+++ b/fs/posix_acl.c
@@ -100,13 +100,13 @@ struct posix_acl *get_acl(struct inode *inode, int type)
 {
  struct posix_acl *acl;
 
+ if (!IS_POSIXACL(inode))
+ return NULL;
+
  acl = get_cached_acl(inode, type);
  if (acl != ACL_NOT_CACHED)
  return acl;
 
- if (!IS_POSIXACL(inode))
- return NULL;
-
  /*
  * A filesystem can force a ACL callback by just never filling the
  * ACL cache. But normally you'd fill the cache either at inode
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 16/41] richacl: xattr mapping functions

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Map between "system.richacl" xattrs and the in-kernel representation.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
---
 fs/Makefile                   |   2 +-
 fs/richacl_xattr.c            | 220 ++++++++++++++++++++++++++++++++++++++++++
 fs/xattr.c                    |  34 +++++--
 include/linux/richacl_xattr.h |  62 ++++++++++++
 include/uapi/linux/xattr.h    |   2 +
 5 files changed, 313 insertions(+), 7 deletions(-)
 create mode 100644 fs/richacl_xattr.c
 create mode 100644 include/linux/richacl_xattr.h

diff --git a/fs/Makefile b/fs/Makefile
index ec665fd..35e640d 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -49,7 +49,7 @@ 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-y := richacl_base.o richacl_inode.o richacl_xattr.o
 
 obj-y += quota/
 
diff --git a/fs/richacl_xattr.c b/fs/richacl_xattr.c
new file mode 100644
index 0000000..cd9979d
--- /dev/null
+++ b/fs/richacl_xattr.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2006, 2010  Novell, Inc.
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Written by Andreas Gruenbacher <[hidden email]>
+ *
+ * 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/sched.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/richacl_xattr.h>
+
+MODULE_LICENSE("GPL");
+
+/**
+ * richacl_from_xattr  -  convert a richacl xattr into the in-memory representation
+ */
+struct richacl *
+richacl_from_xattr(struct user_namespace *user_ns,
+   const void *value, size_t size)
+{
+ const struct richacl_xattr *xattr_acl = value;
+ const struct richace_xattr *xattr_ace = (void *)(xattr_acl + 1);
+ struct richacl *acl;
+ struct richace *ace;
+ int count;
+
+ if (size < sizeof(*xattr_acl) ||
+    xattr_acl->a_version != RICHACL_XATTR_VERSION ||
+    (xattr_acl->a_flags & ~RICHACL_VALID_FLAGS))
+ return ERR_PTR(-EINVAL);
+ size -= sizeof(*xattr_acl);
+ count = le16_to_cpu(xattr_acl->a_count);
+ if (count > RICHACL_XATTR_MAX_COUNT)
+ return ERR_PTR(-EINVAL);
+ if (size != count * sizeof(*xattr_ace))
+ return ERR_PTR(-EINVAL);
+
+ acl = richacl_alloc(count, GFP_NOFS);
+ if (!acl)
+ return ERR_PTR(-ENOMEM);
+
+ acl->a_flags = xattr_acl->a_flags;
+ acl->a_owner_mask = le32_to_cpu(xattr_acl->a_owner_mask);
+ if (acl->a_owner_mask & ~RICHACE_VALID_MASK)
+ goto fail_einval;
+ acl->a_group_mask = le32_to_cpu(xattr_acl->a_group_mask);
+ if (acl->a_group_mask & ~RICHACE_VALID_MASK)
+ goto fail_einval;
+ acl->a_other_mask = le32_to_cpu(xattr_acl->a_other_mask);
+ if (acl->a_other_mask & ~RICHACE_VALID_MASK)
+ goto fail_einval;
+
+ richacl_for_each_entry(ace, acl) {
+ ace->e_type  = le16_to_cpu(xattr_ace->e_type);
+ ace->e_flags = le16_to_cpu(xattr_ace->e_flags);
+ ace->e_mask  = le32_to_cpu(xattr_ace->e_mask);
+
+ if (ace->e_flags & ~RICHACE_VALID_FLAGS)
+ goto fail_einval;
+ if (ace->e_flags & RICHACE_SPECIAL_WHO) {
+ ace->e_id.special = le32_to_cpu(xattr_ace->e_id);
+ if (ace->e_id.special > RICHACE_EVERYONE_SPECIAL_ID)
+ goto fail_einval;
+ } else if (ace->e_flags & RICHACE_IDENTIFIER_GROUP) {
+ u32 id = le32_to_cpu(xattr_ace->e_id);
+
+ ace->e_id.gid = make_kgid(user_ns, id);
+ if (!gid_valid(ace->e_id.gid))
+ goto fail_einval;
+ } else {
+ u32 id = le32_to_cpu(xattr_ace->e_id);
+
+ ace->e_id.uid = make_kuid(user_ns, id);
+ if (!uid_valid(ace->e_id.uid))
+ goto fail_einval;
+ }
+ if (ace->e_type > RICHACE_ACCESS_DENIED_ACE_TYPE ||
+    (ace->e_mask & ~RICHACE_VALID_MASK))
+ goto fail_einval;
+
+ xattr_ace++;
+ }
+
+ return acl;
+
+fail_einval:
+ richacl_put(acl);
+ return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL_GPL(richacl_from_xattr);
+
+/**
+ * richacl_xattr_size  -  compute the size of the xattr representation of @acl
+ */
+size_t
+richacl_xattr_size(const struct richacl *acl)
+{
+ size_t size = sizeof(struct richacl_xattr);
+
+ size += sizeof(struct richace_xattr) * acl->a_count;
+ return size;
+}
+EXPORT_SYMBOL_GPL(richacl_xattr_size);
+
+/**
+ * richacl_to_xattr  -  convert @acl into its xattr representation
+ * @acl: the richacl to convert
+ * @buffer: buffer for the result
+ * @size: size of @buffer
+ */
+int
+richacl_to_xattr(struct user_namespace *user_ns,
+ const struct richacl *acl, void *buffer, size_t size)
+{
+ struct richacl_xattr *xattr_acl = buffer;
+ struct richace_xattr *xattr_ace;
+ const struct richace *ace;
+ size_t real_size;
+
+ real_size = richacl_xattr_size(acl);
+ if (!buffer)
+ return real_size;
+ if (real_size > size)
+ return -ERANGE;
+
+ xattr_acl->a_version = RICHACL_XATTR_VERSION;
+ xattr_acl->a_flags = acl->a_flags;
+ xattr_acl->a_count = cpu_to_le16(acl->a_count);
+
+ xattr_acl->a_owner_mask = cpu_to_le32(acl->a_owner_mask);
+ xattr_acl->a_group_mask = cpu_to_le32(acl->a_group_mask);
+ xattr_acl->a_other_mask = cpu_to_le32(acl->a_other_mask);
+
+ xattr_ace = (void *)(xattr_acl + 1);
+ richacl_for_each_entry(ace, acl) {
+ xattr_ace->e_type = cpu_to_le16(ace->e_type);
+ xattr_ace->e_flags = cpu_to_le16(ace->e_flags);
+ xattr_ace->e_mask = cpu_to_le32(ace->e_mask);
+ if (ace->e_flags & RICHACE_SPECIAL_WHO)
+ xattr_ace->e_id = cpu_to_le32(ace->e_id.special);
+ else if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
+ xattr_ace->e_id =
+ cpu_to_le32(from_kgid(user_ns, ace->e_id.gid));
+ else
+ xattr_ace->e_id =
+ cpu_to_le32(from_kuid(user_ns, ace->e_id.uid));
+ xattr_ace++;
+ }
+ return real_size;
+}
+EXPORT_SYMBOL_GPL(richacl_to_xattr);
+
+/*
+ * Fix up the uids and gids in richacl extended attributes in place.
+ */
+static void richacl_fix_xattr_userns(
+ struct user_namespace *to, struct user_namespace *from,
+ void *value, size_t size)
+{
+ struct richacl_xattr *xattr_acl = value;
+ struct richace_xattr *xattr_ace =
+ (struct richace_xattr *)(xattr_acl + 1);
+ unsigned int count;
+
+ if (!value)
+ return;
+ if (size < sizeof(*xattr_acl))
+ return;
+ if (xattr_acl->a_version != cpu_to_le32(RICHACL_XATTR_VERSION))
+ return;
+ size -= sizeof(*xattr_acl);
+ if (size % sizeof(*xattr_ace))
+ return;
+ count = size / sizeof(*xattr_ace);
+ for (; count; count--, xattr_ace++) {
+ if (xattr_ace->e_flags & cpu_to_le16(RICHACE_SPECIAL_WHO))
+ continue;
+ if (xattr_ace->e_flags &
+    cpu_to_le16(RICHACE_IDENTIFIER_GROUP)) {
+ u32 id = le32_to_cpu(xattr_ace->e_id);
+ kgid_t gid = make_kgid(from, id);
+
+ xattr_ace->e_id = cpu_to_le32(from_kgid(to, gid));
+ } else {
+ u32 id = le32_to_cpu(xattr_ace->e_id);
+ kuid_t uid = make_kuid(from, id);
+
+ xattr_ace->e_id = cpu_to_le32(from_kuid(to, uid));
+ }
+ }
+}
+
+void richacl_fix_xattr_from_user(void *value, size_t size)
+{
+ struct user_namespace *user_ns = current_user_ns();
+
+ if (user_ns == &init_user_ns)
+ return;
+ richacl_fix_xattr_userns(&init_user_ns, user_ns, value, size);
+}
+
+void richacl_fix_xattr_to_user(void *value, size_t size)
+{
+ struct user_namespace *user_ns = current_user_ns();
+
+ if (user_ns == &init_user_ns)
+ return;
+ richacl_fix_xattr_userns(user_ns, &init_user_ns, value, size);
+}
diff --git a/fs/xattr.c b/fs/xattr.c
index 072fee1..f2313c6 100644
--- a/fs/xattr.c
+++ b/fs/xattr.c
@@ -21,6 +21,7 @@
 #include <linux/audit.h>
 #include <linux/vmalloc.h>
 #include <linux/posix_acl_xattr.h>
+#include <linux/richacl_xattr.h>
 
 #include <asm/uaccess.h>
 
@@ -314,6 +315,18 @@ out:
 }
 EXPORT_SYMBOL_GPL(vfs_removexattr);
 
+static void
+fix_xattr_from_user(const char *kname, void *kvalue, size_t size)
+{
+ if (strncmp(kname, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
+ return;
+ kname += XATTR_SYSTEM_PREFIX_LEN;
+ if (!strcmp(kname, XATTR_POSIX_ACL_ACCESS) ||
+    !strcmp(kname, XATTR_POSIX_ACL_DEFAULT))
+ posix_acl_fix_xattr_from_user(kvalue, size);
+ else if (!strcmp(kname, XATTR_RICHACL))
+ richacl_fix_xattr_from_user(kvalue, size);
+}
 
 /*
  * Extended attribute SET operations
@@ -350,9 +363,7 @@ setxattr(struct dentry *d, const char __user *name, const void __user *value,
  error = -EFAULT;
  goto out;
  }
- if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
-    (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
- posix_acl_fix_xattr_from_user(kvalue, size);
+ fix_xattr_from_user(kname, kvalue, size);
  }
 
  error = vfs_setxattr(d, kname, kvalue, size, flags);
@@ -419,6 +430,19 @@ SYSCALL_DEFINE5(fsetxattr, int, fd, const char __user *, name,
  return error;
 }
 
+static void
+fix_xattr_to_user(const char *kname, void *kvalue, size_t size)
+{
+ if (strncmp(kname, XATTR_SYSTEM_PREFIX, XATTR_SYSTEM_PREFIX_LEN))
+ return;
+ kname += XATTR_SYSTEM_PREFIX_LEN;
+ if (!strcmp(kname, XATTR_POSIX_ACL_ACCESS) ||
+    !strcmp(kname, XATTR_POSIX_ACL_DEFAULT))
+ posix_acl_fix_xattr_to_user(kvalue, size);
+ else if (!strcmp(kname, XATTR_RICHACL))
+ richacl_fix_xattr_to_user(kvalue, size);
+}
+
 /*
  * Extended attribute GET operations
  */
@@ -451,9 +475,7 @@ getxattr(struct dentry *d, const char __user *name, void __user *value,
 
  error = vfs_getxattr(d, kname, kvalue, size);
  if (error > 0) {
- if ((strcmp(kname, XATTR_NAME_POSIX_ACL_ACCESS) == 0) ||
-    (strcmp(kname, XATTR_NAME_POSIX_ACL_DEFAULT) == 0))
- posix_acl_fix_xattr_to_user(kvalue, size);
+ fix_xattr_to_user(kname, kvalue, size);
  if (size && copy_to_user(value, kvalue, error))
  error = -EFAULT;
  } else if (error == -ERANGE && size >= XATTR_SIZE_MAX) {
diff --git a/include/linux/richacl_xattr.h b/include/linux/richacl_xattr.h
new file mode 100644
index 0000000..f84cc21
--- /dev/null
+++ b/include/linux/richacl_xattr.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2006, 2010  Novell, Inc.
+ * Copyright (C) 2015  Red Hat, Inc.
+ * Written by Andreas Gruenbacher <[hidden email]>
+ *
+ * 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_XATTR_H
+#define __RICHACL_XATTR_H
+
+#include <linux/richacl.h>
+
+struct richace_xattr {
+ __le16 e_type;
+ __le16 e_flags;
+ __le32 e_mask;
+ __le32 e_id;
+};
+
+struct richacl_xattr {
+ unsigned char a_version;
+ unsigned char a_flags;
+ __le16 a_count;
+ __le32 a_owner_mask;
+ __le32 a_group_mask;
+ __le32 a_other_mask;
+};
+
+#define RICHACL_XATTR_VERSION 0
+#define RICHACL_XATTR_MAX_COUNT \
+ ((XATTR_SIZE_MAX - sizeof(struct richacl_xattr)) / \
+ sizeof(struct richace_xattr))
+
+extern struct richacl *richacl_from_xattr(struct user_namespace *, const void *,
+  size_t);
+extern size_t richacl_xattr_size(const struct richacl *);
+extern int richacl_to_xattr(struct user_namespace *, const struct richacl *,
+    void *, size_t);
+
+#ifdef CONFIG_FS_RICHACL
+extern void richacl_fix_xattr_from_user(void *, size_t);
+extern void richacl_fix_xattr_to_user(void *, size_t);
+#else
+static inline void richacl_fix_xattr_from_user(void *value, size_t size)
+{
+}
+
+static inline void richacl_fix_xattr_to_user(void *value, size_t size)
+{
+}
+#endif
+
+#endif /* __RICHACL_XATTR_H */
diff --git a/include/uapi/linux/xattr.h b/include/uapi/linux/xattr.h
index 1590c49..1996903 100644
--- a/include/uapi/linux/xattr.h
+++ b/include/uapi/linux/xattr.h
@@ -73,5 +73,7 @@
 #define XATTR_POSIX_ACL_DEFAULT  "posix_acl_default"
 #define XATTR_NAME_POSIX_ACL_DEFAULT XATTR_SYSTEM_PREFIX XATTR_POSIX_ACL_DEFAULT
 
+#define XATTR_RICHACL "richacl"
+#define XATTR_NAME_RICHACL XATTR_SYSTEM_PREFIX XATTR_RICHACL
 
 #endif /* _UAPI_LINUX_XATTR_H */
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 15/41] richacl: Automatic Inheritance

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Automatic Inheritance (AI) allows changes to the acl of a directory to
propagate down to children.

This is mostly implemented in user space: when a process changes the
permissions of a directory and Automatic Inheritance is enabled for that
directory, the process must propagate those changes to all children,
recursively.

The kernel enables this by keeping track of which permissions have been
inherited at create time.  In addition, it makes sure that permission
propagation is turned off when the permissions are set explicitly (for
example, upon create or chmod).

Automatic Inheritance works as follows:

 - When the RICHACL_AUTO_INHERIT flag in the acl of a file or directory
   is not set, the file or directory is not affected by AI.

 - When the RICHACL_AUTO_INHERIT flag in the acl of a directory is set
   and a file or subdirectory is created in that directory, the
   inherited acl will have the RICHACL_AUTO_INHERIT flag set, and all
   inherited aces will have the RICHACE_INHERITED_ACE flag set.  This
   allows user space to distinguish between aces which have been
   inherited and aces which have been explicitly added.

 - When the RICHACL_PROTECTED acl flag in the acl of a file or directory
   is set, AI will not modify the acl.  This does not affect propagation
   of permissions from the file to its children (if the file is a
   directory).

Linux does not have a way of creating files or directories without setting the
file permission bits, so all files created inside a directory with
RICHACL_AUTO_INHERIT set will have the RICHACL_PROTECTED flag set.  This
effectively disables Automatic Inheritance.

Protocols which support creating files without specifying permissions can
explicitly clear the RICHACL_PROTECTED flag after creating a file and reset the
file masks to "undo" applying the create mode; see richacl_compute_max_masks().
They should set the RICHACL_DEFAULTED flag.  (A mechanism that would allow to
indicate to the kernel to ignore the create mode in the first place when there
are inherited permissions would be nice to have.)

Signed-off-by: Andreas Gruenbacher <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 fs/richacl_base.c       | 10 +++++++++-
 fs/richacl_inode.c      |  7 +++++++
 include/linux/richacl.h | 23 ++++++++++++++++++++++-
 3 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index c1490c7..e0357ae 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -363,7 +363,8 @@ richacl_chmod(struct richacl *acl, mode_t mode)
  if (acl->a_owner_mask == owner_mask &&
     acl->a_group_mask == group_mask &&
     acl->a_other_mask == other_mask &&
-    (acl->a_flags & (RICHACL_WRITE_THROUGH | RICHACL_MASKED)))
+    (acl->a_flags & (RICHACL_WRITE_THROUGH | RICHACL_MASKED)) &&
+    (!richacl_is_auto_inherit(acl) || richacl_is_protected(acl)))
  return acl;
 
  clone = richacl_clone(acl, GFP_KERNEL);
@@ -375,6 +376,8 @@ richacl_chmod(struct richacl *acl, mode_t mode)
  clone->a_owner_mask = owner_mask;
  clone->a_group_mask = group_mask;
  clone->a_other_mask = other_mask;
+ if (richacl_is_auto_inherit(clone))
+ clone->a_flags |= RICHACL_PROTECTED;
 
  return clone;
 }
@@ -548,6 +551,11 @@ richacl_inherit(const struct richacl *dir_acl, int isdir)
  ace++;
  }
  }
+ if (richacl_is_auto_inherit(dir_acl)) {
+ acl->a_flags = RICHACL_AUTO_INHERIT;
+ richacl_for_each_entry(ace, acl)
+ ace->e_flags |= RICHACE_INHERITED_ACE;
+ }
 
  return acl;
 }
diff --git a/fs/richacl_inode.c b/fs/richacl_inode.c
index 737f054..d451e83 100644
--- a/fs/richacl_inode.c
+++ b/fs/richacl_inode.c
@@ -248,6 +248,13 @@ richacl_inherit_inode(const struct richacl *dir_acl, struct inode *inode)
  richacl_put(acl);
  acl = NULL;
  } else {
+ /*
+ * We need to set RICHACL_PROTECTED because we are
+ * doing an implicit chmod
+ */
+ if (richacl_is_auto_inherit(acl))
+ acl->a_flags |= RICHACL_PROTECTED;
+
  richacl_compute_max_masks(acl);
  /*
  * Ensure that the acl will not grant any permissions
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 9bf95c2..832b06c 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -53,10 +53,16 @@ struct richacl {
      _ace--)
 
 /* a_flags values */
+#define RICHACL_AUTO_INHERIT 0x01
+#define RICHACL_PROTECTED 0x02
+#define RICHACL_DEFAULTED 0x04
 #define RICHACL_WRITE_THROUGH 0x40
 #define RICHACL_MASKED 0x80
 
 #define RICHACL_VALID_FLAGS ( \
+ RICHACL_AUTO_INHERIT | \
+ RICHACL_PROTECTED | \
+ RICHACL_DEFAULTED | \
  RICHACL_WRITE_THROUGH | \
  RICHACL_MASKED)
 
@@ -70,6 +76,7 @@ struct richacl {
 #define RICHACE_NO_PROPAGATE_INHERIT_ACE 0x0004
 #define RICHACE_INHERIT_ONLY_ACE 0x0008
 #define RICHACE_IDENTIFIER_GROUP 0x0040
+#define RICHACE_INHERITED_ACE 0x0080
 #define RICHACE_SPECIAL_WHO 0x4000
 
 #define RICHACE_VALID_FLAGS ( \
@@ -78,13 +85,15 @@ struct richacl {
  RICHACE_NO_PROPAGATE_INHERIT_ACE | \
  RICHACE_INHERIT_ONLY_ACE | \
  RICHACE_IDENTIFIER_GROUP | \
+ RICHACE_INHERITED_ACE | \
  RICHACE_SPECIAL_WHO)
 
 #define RICHACE_INHERITANCE_FLAGS ( \
  RICHACE_FILE_INHERIT_ACE | \
  RICHACE_DIRECTORY_INHERIT_ACE | \
  RICHACE_NO_PROPAGATE_INHERIT_ACE | \
- RICHACE_INHERIT_ONLY_ACE )
+ RICHACE_INHERIT_ONLY_ACE | \
+ RICHACE_INHERITED_ACE )
 
 /* e_mask bitflags */
 #define RICHACE_READ_DATA 0x00000001
@@ -195,6 +204,18 @@ extern void set_cached_richacl(struct inode *, struct richacl *);
 extern void forget_cached_richacl(struct inode *);
 extern struct richacl *get_richacl(struct inode *);
 
+static inline int
+richacl_is_auto_inherit(const struct richacl *acl)
+{
+ return acl->a_flags & RICHACL_AUTO_INHERIT;
+}
+
+static inline int
+richacl_is_protected(const struct richacl *acl)
+{
+ return acl->a_flags & RICHACL_PROTECTED;
+}
+
 /**
  * richace_is_owner  -  check if @ace is an OWNER@ entry
  */
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 08/41] richacl: Compute maximum file masks from an acl

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
Compute upper bound owner, group, and other file masks with as few
permissions as possible without denying any permissions that the NFSv4
acl in a richacl grants.

This algorithm is used when a file inherits an acl at create time and
when an acl is set via a mechanism that does not provide file masks
(such as setting an acl via nfsd).  When user-space sets an acl via
setxattr, the extended attribute already includes the file masks.

Setting an acl also sets the file mode permission bits: they are
determined by the file masks; see richacl_masks_to_mode().

Signed-off-by: Andreas Gruenbacher <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 fs/richacl_base.c       | 157 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |   1 +
 2 files changed, 158 insertions(+)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 063dbe4..fc544f7 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -182,3 +182,160 @@ richacl_want_to_mask(unsigned int want)
  return mask;
 }
 EXPORT_SYMBOL_GPL(richacl_want_to_mask);
+
+/*
+ * Note: functions like richacl_allowed_to_who(), richacl_group_class_allowed(),
+ * and richacl_compute_max_masks() iterate through the entire acl in reverse
+ * order as an optimization.
+ *
+ * In the standard algorithm, aces are considered in forward order.  When a
+ * process matches an ace, the permissions in the ace are either allowed or
+ * denied depending on the ace type.  Once a permission has been allowed or
+ * denied, it is no longer considered in further aces.
+ *
+ * By iterating through the acl in reverse order, we can compute the same
+ * result without having to keep track of which permissions have been allowed
+ * and denied already.
+ */
+
+/**
+ * richacl_allowed_to_who  -  permissions allowed to a specific who value
+ *
+ * Compute the maximum mask values allowed to a specific who value, taking
+ * everyone@ aces into account.
+ */
+static unsigned int richacl_allowed_to_who(struct richacl *acl,
+   struct richace *who)
+{
+ struct richace *ace;
+ unsigned int allowed = 0;
+
+ richacl_for_each_entry_reverse(ace, acl) {
+ if (richace_is_inherit_only(ace))
+ continue;
+ if (richace_is_same_identifier(ace, who) ||
+    richace_is_everyone(ace)) {
+ if (richace_is_allow(ace))
+ allowed |= ace->e_mask;
+ else if (richace_is_deny(ace))
+ allowed &= ~ace->e_mask;
+ }
+ }
+ return allowed;
+}
+
+/**
+ * richacl_group_class_allowed  -  maximum permissions of the group class
+ *
+ * Compute the maximum mask values allowed to a process in the group class
+ * (i.e., a process which is not the owner but is in the owning group or
+ * matches a user or group acl entry).  This includes permissions granted or
+ * denied by everyone@ aces.
+ *
+ * See richacl_compute_max_masks().
+ */
+static unsigned int richacl_group_class_allowed(struct richacl *acl)
+{
+ struct richace *ace;
+ unsigned int everyone_allowed = 0, group_class_allowed = 0;
+ int had_group_ace = 0;
+
+ richacl_for_each_entry_reverse(ace, acl) {
+ if (richace_is_inherit_only(ace) ||
+    richace_is_owner(ace))
+ continue;
+
+ if (richace_is_everyone(ace)) {
+ if (richace_is_allow(ace))
+ everyone_allowed |= ace->e_mask;
+ else if (richace_is_deny(ace))
+ everyone_allowed &= ~ace->e_mask;
+ } else {
+ group_class_allowed |=
+ richacl_allowed_to_who(acl, ace);
+
+ if (richace_is_group(ace))
+ had_group_ace = 1;
+ }
+ }
+ /*
+ * If the acl doesn't contain any group@ aces, richacl_allowed_to_who()
+ * wasn't called for the owning group.  We could make that call now, but
+ * we already know the result (everyone_allowed).
+ */
+ if (!had_group_ace)
+ group_class_allowed |= everyone_allowed;
+ return group_class_allowed;
+}
+
+/**
+ * richacl_compute_max_masks  -  compute upper bound masks
+ *
+ * Computes upper bound owner, group, and other masks so that none of the
+ * permissions allowed by the acl are disabled.
+ *
+ * We don't make assumptions about who the owner is so that the owner can
+ * change with no effect on the file masks or file mode permission bits; this
+ * means that we must assume that all entries can match the owner.
+ */
+void richacl_compute_max_masks(struct richacl *acl)
+{
+ unsigned int gmask = ~0;
+ struct richace *ace;
+
+ /*
+ * @gmask contains all permissions which the group class is ever
+ * allowed.  We use it to avoid adding permissions to the group mask
+ * from everyone@ allow aces which the group class is always denied
+ * through other aces.  For example, the following acl would otherwise
+ * result in a group mask of rw:
+ *
+ * group@:w::deny
+ * everyone@:rw::allow
+ *
+ * Avoid computing @gmask for acls which do not include any group class
+ * deny aces: in such acls, the group class is never denied any
+ * permissions from everyone@ allow aces, and the group class cannot
+ * have fewer permissions than the other class.
+ */
+
+restart:
+ acl->a_owner_mask = 0;
+ acl->a_group_mask = 0;
+ acl->a_other_mask = 0;
+
+ richacl_for_each_entry_reverse(ace, acl) {
+ if (richace_is_inherit_only(ace))
+ continue;
+
+ if (richace_is_owner(ace)) {
+ if (richace_is_allow(ace))
+ acl->a_owner_mask |= ace->e_mask;
+ else if (richace_is_deny(ace))
+ acl->a_owner_mask &= ~ace->e_mask;
+ } else if (richace_is_everyone(ace)) {
+ if (richace_is_allow(ace)) {
+ acl->a_owner_mask |= ace->e_mask;
+ acl->a_group_mask |= ace->e_mask & gmask;
+ acl->a_other_mask |= ace->e_mask;
+ } else if (richace_is_deny(ace)) {
+ acl->a_owner_mask &= ~ace->e_mask;
+ acl->a_group_mask &= ~ace->e_mask;
+ acl->a_other_mask &= ~ace->e_mask;
+ }
+ } else {
+ if (richace_is_allow(ace)) {
+ acl->a_owner_mask |= ace->e_mask & gmask;
+ acl->a_group_mask |= ace->e_mask & gmask;
+ } else if (richace_is_deny(ace) && gmask == ~0) {
+ gmask = richacl_group_class_allowed(acl);
+ if (likely(gmask != ~0))
+ /* should always be true */
+ goto restart;
+ }
+ }
+ }
+
+ acl->a_flags &= ~(RICHACL_WRITE_THROUGH | RICHACL_MASKED);
+}
+EXPORT_SYMBOL_GPL(richacl_compute_max_masks);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index 9c8f298..e81144a 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -297,5 +297,6 @@ extern void richace_copy(struct richace *, const struct richace *);
 extern int richacl_masks_to_mode(const struct richacl *);
 extern unsigned int richacl_mode_to_mask(mode_t);
 extern unsigned int richacl_want_to_mask(unsigned int);
+extern void richacl_compute_max_masks(struct richacl *);
 
 #endif /* __RICHACL_H */
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
Reply | Threaded
Open this post in threaded view
|

[PATCH v8 07/41] richacl: Permission mapping functions

Andreas Gruenbacher-6
In reply to this post by Andreas Gruenbacher-6
We need to map from POSIX permissions to NFSv4 permissions when a
chmod() is done, from NFSv4 permissions to POSIX permissions when an acl
is set (which implicitly sets the file permission bits), and from the
MAY_READ/MAY_WRITE/MAY_EXEC/MAY_APPEND flags to NFSv4 permissions when
doing an access check in a richacl.

Signed-off-by: Andreas Gruenbacher <[hidden email]>
Reviewed-by: J. Bruce Fields <[hidden email]>
---
 fs/richacl_base.c       | 117 ++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/richacl.h |  47 ++++++++++++++++++-
 2 files changed, 163 insertions(+), 1 deletion(-)

diff --git a/fs/richacl_base.c b/fs/richacl_base.c
index 6d9a073..063dbe4 100644
--- a/fs/richacl_base.c
+++ b/fs/richacl_base.c
@@ -65,3 +65,120 @@ richace_copy(struct richace *to, const struct richace *from)
 {
  memcpy(to, from, sizeof(struct richace));
 }
+
+/*
+ * richacl_mask_to_mode  -  compute the file permission bits from mask
+ * @mask: %RICHACE_* permission mask
+ *
+ * Compute the file permission bits corresponding to a particular set of
+ * richacl permissions.
+ *
+ * See richacl_masks_to_mode().
+ */
+static int
+richacl_mask_to_mode(unsigned int mask)
+{
+ int mode = 0;
+
+ if (mask & RICHACE_POSIX_MODE_READ)
+ mode |= S_IROTH;
+ if (mask & RICHACE_POSIX_MODE_WRITE)
+ mode |= S_IWOTH;
+ if (mask & RICHACE_POSIX_MODE_EXEC)
+ mode |= S_IXOTH;
+
+ return mode;
+}
+
+/**
+ * richacl_masks_to_mode  -  compute file permission bits from file masks
+ *
+ * When setting a richacl, we set the file permission bits to indicate maximum
+ * permissions: for example, we set the Write permission when a mask contains
+ * RICHACE_APPEND_DATA even if it does not also contain RICHACE_WRITE_DATA.
+ *
+ * Permissions which are not in RICHACE_POSIX_MODE_READ,
+ * RICHACE_POSIX_MODE_WRITE, or RICHACE_POSIX_MODE_EXEC cannot be represented
+ * in the file permission bits.  Such permissions can still be effective, but
+ * not for new files or after a chmod(); they must be explicitly enabled in the
+ * richacl.
+ */
+int
+richacl_masks_to_mode(const struct richacl *acl)
+{
+ return richacl_mask_to_mode(acl->a_owner_mask) << 6 |
+       richacl_mask_to_mode(acl->a_group_mask) << 3 |
+       richacl_mask_to_mode(acl->a_other_mask);
+}
+EXPORT_SYMBOL_GPL(richacl_masks_to_mode);
+
+/**
+ * richacl_mode_to_mask  - compute a file mask from the lowest three mode bits
+ *
+ * When the file permission bits of a file are set with chmod(), this specifies
+ * the maximum permissions that processes will get.  All permissions beyond
+ * that will be removed from the file masks, and become ineffective.
+ */
+unsigned int
+richacl_mode_to_mask(mode_t mode)
+{
+ unsigned int mask = 0;
+
+ if (mode & S_IROTH)
+ mask |= RICHACE_POSIX_MODE_READ;
+ if (mode & S_IWOTH)
+ mask |= RICHACE_POSIX_MODE_WRITE;
+ if (mode & S_IXOTH)
+ mask |= RICHACE_POSIX_MODE_EXEC;
+
+ return mask;
+}
+
+/**
+ * richacl_want_to_mask  - convert the iop->permission want argument to a mask
+ * @want: @want argument of the permission inode operation
+ *
+ * When checking for append, @want is (MAY_WRITE | MAY_APPEND).
+ *
+ * Richacls use the iop->may_create and iop->may_delete hooks which are used
+ * for checking if creating and deleting files is allowed.  These hooks do not
+ * use richacl_want_to_mask(), so we do not have to deal with mapping MAY_WRITE
+ * to RICHACE_ADD_FILE, RICHACE_ADD_SUBDIRECTORY, and RICHACE_DELETE_CHILD
+ * here.
+ */
+unsigned int
+richacl_want_to_mask(unsigned int want)
+{
+ unsigned int mask = 0;
+
+ if (want & MAY_READ)
+ mask |= RICHACE_READ_DATA;
+ if (want & MAY_DELETE_SELF)
+ mask |= RICHACE_DELETE;
+ if (want & MAY_TAKE_OWNERSHIP)
+ mask |= RICHACE_WRITE_OWNER;
+ if (want & MAY_CHMOD)
+ mask |= RICHACE_WRITE_ACL;
+ if (want & MAY_SET_TIMES)
+ mask |= RICHACE_WRITE_ATTRIBUTES;
+ if (want & MAY_EXEC)
+ mask |= RICHACE_EXECUTE;
+ /*
+ * differentiate MAY_WRITE from these request
+ */
+ if (want & (MAY_APPEND |
+    MAY_CREATE_FILE | MAY_CREATE_DIR |
+    MAY_DELETE_CHILD)) {
+ if (want & MAY_APPEND)
+ mask |= RICHACE_APPEND_DATA;
+ if (want & MAY_CREATE_FILE)
+ mask |= RICHACE_ADD_FILE;
+ if (want & MAY_CREATE_DIR)
+ mask |= RICHACE_ADD_SUBDIRECTORY;
+ if (want & MAY_DELETE_CHILD)
+ mask |= RICHACE_DELETE_CHILD;
+ } else if (want & MAY_WRITE)
+ mask |= RICHACE_WRITE_DATA;
+ return mask;
+}
+EXPORT_SYMBOL_GPL(richacl_want_to_mask);
diff --git a/include/linux/richacl.h b/include/linux/richacl.h
index bfa94bb..9c8f298 100644
--- a/include/linux/richacl.h
+++ b/include/linux/richacl.h
@@ -126,6 +126,49 @@ struct richacl {
  RICHACE_WRITE_OWNER | \
  RICHACE_SYNCHRONIZE)
 
+/*
+ * The POSIX permissions are supersets of the following NFSv4 permissions:
+ *
+ *  - MAY_READ maps to READ_DATA or LIST_DIRECTORY, depending on the type
+ *    of the file system object.
+ *
+ *  - MAY_WRITE maps to WRITE_DATA or RICHACE_APPEND_DATA for files, and to
+ *    ADD_FILE, RICHACE_ADD_SUBDIRECTORY, or RICHACE_DELETE_CHILD for directories.
+ *
+ *  - MAY_EXECUTE maps to RICHACE_EXECUTE.
+ *
+ *  (Some of these NFSv4 permissions have the same bit values.)
+ */
+#define RICHACE_POSIX_MODE_READ ( \
+ RICHACE_READ_DATA | \
+ RICHACE_LIST_DIRECTORY)
+#define RICHACE_POSIX_MODE_WRITE ( \
+ RICHACE_WRITE_DATA | \
+ RICHACE_ADD_FILE | \
+ RICHACE_APPEND_DATA | \
+ RICHACE_ADD_SUBDIRECTORY | \
+ RICHACE_DELETE_CHILD)
+#define RICHACE_POSIX_MODE_EXEC RICHACE_EXECUTE
+#define RICHACE_POSIX_MODE_ALL ( \
+ RICHACE_POSIX_MODE_READ | \
+ RICHACE_POSIX_MODE_WRITE | \
+ RICHACE_POSIX_MODE_EXEC)
+/*
+ * These permissions are always allowed
+ * no matter what the acl says.
+ */
+#define RICHACE_POSIX_ALWAYS_ALLOWED ( \
+ RICHACE_SYNCHRONIZE | \
+ RICHACE_READ_ATTRIBUTES | \
+ RICHACE_READ_ACL)
+/*
+ * The owner is implicitly granted
+ * these permissions under POSIX.
+ */
+#define RICHACE_POSIX_OWNER_ALLOWED ( \
+ RICHACE_WRITE_ATTRIBUTES | \
+ RICHACE_WRITE_OWNER | \
+ RICHACE_WRITE_ACL)
 /**
  * richacl_get  -  grab another reference to a richacl handle
  */
@@ -251,6 +294,8 @@ richace_is_same_identifier(const struct richace *a, const struct richace *b)
 extern struct richacl *richacl_alloc(int, gfp_t);
 extern struct richacl *richacl_clone(const struct richacl *, gfp_t);
 extern void richace_copy(struct richace *, const struct richace *);
-
+extern int richacl_masks_to_mode(const struct richacl *);
+extern unsigned int richacl_mode_to_mask(mode_t);
+extern unsigned int richacl_want_to_mask(unsigned int);
 
 #endif /* __RICHACL_H */
--
2.4.3

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [hidden email]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/
1234