Re: [RFC v7 29/41] nfsd: Use richacls as internal acl representation

From: J. Bruce Fields
Date: Thu Sep 24 2015 - 15:29:18 EST


On Sat, Sep 05, 2015 at 12:27:24PM +0200, Andreas Gruenbacher wrote:
> When converting from NFSv4 ACLs to POSIX ACLs, nfsd so far was using
> struct nfs4_acl as its internal representation. This representation is a
> subset of richacls, so get rid of struct nfs4_acl. Richacls even have a
> more compact in-memory representation, so a few more ACL entries can
> easily be supported.

Looks good to me, ACK.

--b.

>
> Signed-off-by: Andreas Gruenbacher <agruenba@xxxxxxxxxx>
> ---
> fs/Kconfig | 6 +
> fs/nfs_common/Makefile | 1 +
> fs/nfs_common/nfs4acl.c | 44 ++++++
> fs/nfsd/Kconfig | 1 +
> fs/nfsd/acl.h | 24 ++--
> fs/nfsd/nfs4acl.c | 367 ++++++++++++++++++++++--------------------------
> fs/nfsd/nfs4proc.c | 15 +-
> fs/nfsd/nfs4xdr.c | 64 +++------
> fs/nfsd/xdr4.h | 6 +-
> include/linux/nfs4.h | 23 ---
> include/linux/nfs4acl.h | 7 +
> 11 files changed, 273 insertions(+), 285 deletions(-)
> create mode 100644 fs/nfs_common/nfs4acl.c
> create mode 100644 include/linux/nfs4acl.h
>
> diff --git a/fs/Kconfig b/fs/Kconfig
> index 3e09c06..dd3f2d6 100644
> --- a/fs/Kconfig
> +++ b/fs/Kconfig
> @@ -268,6 +268,12 @@ config NFS_COMMON
> depends on NFSD || NFS_FS || LOCKD
> default y
>
> +config NFS_RICHACL
> + bool
> + depends on NFSD_V4 || NFS_V4
> + select FS_RICHACL
> + default y
> +
> source "net/sunrpc/Kconfig"
> source "fs/ceph/Kconfig"
> source "fs/cifs/Kconfig"
> diff --git a/fs/nfs_common/Makefile b/fs/nfs_common/Makefile
> index d153ca3..e055139 100644
> --- a/fs/nfs_common/Makefile
> +++ b/fs/nfs_common/Makefile
> @@ -4,5 +4,6 @@
>
> obj-$(CONFIG_NFS_ACL_SUPPORT) += nfs_acl.o
> nfs_acl-objs := nfsacl.o
> +obj-$(CONFIG_NFS_RICHACL) += nfs4acl.o
>
> obj-$(CONFIG_GRACE_PERIOD) += grace.o
> diff --git a/fs/nfs_common/nfs4acl.c b/fs/nfs_common/nfs4acl.c
> new file mode 100644
> index 0000000..02df064
> --- /dev/null
> +++ b/fs/nfs_common/nfs4acl.c
> @@ -0,0 +1,44 @@
> +#include <linux/fs.h>
> +#include <linux/richacl.h>
> +#include <linux/nfs4acl.h>
> +
> +static struct special_id {
> + char *who;
> + int len;
> +} special_who_map[] = {
> + [RICHACE_OWNER_SPECIAL_ID] = {
> + .who = "OWNER@",
> + .len = sizeof("OWNER@") - 1 },
> + [RICHACE_GROUP_SPECIAL_ID] = {
> + .who = "GROUP@",
> + .len = sizeof("GROUP@") - 1 },
> + [RICHACE_EVERYONE_SPECIAL_ID] = {
> + .who = "EVERYONE@",
> + .len = sizeof("EVERYONE@") - 1 }
> +};
> +
> +int nfs4acl_who_to_special_id(const char *who, u32 len)
> +{
> + int n;
> +
> + for (n = 0; n < ARRAY_SIZE(special_who_map); n++) {
> + if (len == special_who_map[n].len &&
> + !memcmp(who, special_who_map[n].who, len))
> + return n;
> + }
> + return -1;
> +}
> +EXPORT_SYMBOL(nfs4acl_who_to_special_id);
> +
> +bool nfs4acl_special_id_to_who(unsigned int special_who,
> + const char **who, unsigned int *len)
> +{
> + struct special_id *special = &special_who_map[special_who];
> +
> + if (special_who > ARRAY_SIZE(special_who_map) || !special->len)
> + return false;
> + *who = special->who;
> + *len = special->len;
> + return true;
> +}
> +EXPORT_SYMBOL(nfs4acl_special_id_to_who);
> diff --git a/fs/nfsd/Kconfig b/fs/nfsd/Kconfig
> index a0b77fc..811379a 100644
> --- a/fs/nfsd/Kconfig
> +++ b/fs/nfsd/Kconfig
> @@ -70,6 +70,7 @@ config NFSD_V4
> depends on NFSD && PROC_FS
> select NFSD_V3
> select FS_POSIX_ACL
> + select FS_RICHACL
> select SUNRPC_GSS
> select CRYPTO
> select GRACE_PERIOD
> diff --git a/fs/nfsd/acl.h b/fs/nfsd/acl.h
> index 4cd7c69..1c5deb5 100644
> --- a/fs/nfsd/acl.h
> +++ b/fs/nfsd/acl.h
> @@ -35,25 +35,27 @@
> #ifndef LINUX_NFS4_ACL_H
> #define LINUX_NFS4_ACL_H
>
> -struct nfs4_acl;
> +struct richacl;
> +struct richace;
> struct svc_fh;
> struct svc_rqst;
>
> /*
> * Maximum ACL we'll accept from a client; chosen (somewhat
> * arbitrarily) so that kmalloc'ing the ACL shouldn't require a
> - * high-order allocation. This allows 204 ACEs on x86_64:
> + * high-order allocation. This allows 339 ACEs on x86_64:
> */
> -#define NFS4_ACL_MAX ((PAGE_SIZE - sizeof(struct nfs4_acl)) \
> - / sizeof(struct nfs4_ace))
> +#define NFSD4_ACL_MAX ((PAGE_SIZE - sizeof(struct richacl)) \
> + / sizeof(struct richace))
>
> -int nfs4_acl_bytes(int entries);
> -int nfs4_acl_get_whotype(char *, u32);
> -__be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who);
> +__be32 nfsd4_decode_ace_who(struct richace *ace, struct svc_rqst *rqstp,
> + char *who, u32 len);
> +__be32 nfsd4_encode_ace_who(struct xdr_stream *xdr, struct svc_rqst *rqstp,
> + struct richace *ace);
>
> -int nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
> - struct nfs4_acl **acl);
> -__be32 nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
> - struct nfs4_acl *acl);
> +int nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry,
> + struct richacl **acl);
> +__be32 nfsd4_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
> + struct richacl *acl);
>
> #endif /* LINUX_NFS4_ACL_H */
> diff --git a/fs/nfsd/nfs4acl.c b/fs/nfsd/nfs4acl.c
> index eb5accf..582f772 100644
> --- a/fs/nfsd/nfs4acl.c
> +++ b/fs/nfsd/nfs4acl.c
> @@ -36,45 +36,48 @@
>
> #include <linux/slab.h>
> #include <linux/nfs_fs.h>
> +#include <linux/richacl_compat.h>
> +#include <linux/nfs4acl.h>
> #include "nfsfh.h"
> #include "nfsd.h"
> +#include "idmap.h"
> #include "acl.h"
> #include "vfs.h"
>
> -#define NFS4_ACL_TYPE_DEFAULT 0x01
> -#define NFS4_ACL_DIR 0x02
> -#define NFS4_ACL_OWNER 0x04
> +#define FLAG_DEFAULT_ACL 0x01
> +#define FLAG_DIRECTORY 0x02
> +#define FLAG_OWNER 0x04
>
> /* mode bit translations: */
> -#define NFS4_READ_MODE (NFS4_ACE_READ_DATA)
> -#define NFS4_WRITE_MODE (NFS4_ACE_WRITE_DATA | NFS4_ACE_APPEND_DATA)
> -#define NFS4_EXECUTE_MODE NFS4_ACE_EXECUTE
> -#define NFS4_ANYONE_MODE (NFS4_ACE_READ_ATTRIBUTES | NFS4_ACE_READ_ACL | NFS4_ACE_SYNCHRONIZE)
> -#define NFS4_OWNER_MODE (NFS4_ACE_WRITE_ATTRIBUTES | NFS4_ACE_WRITE_ACL)
> +#define RICHACE_READ_MODE (RICHACE_READ_DATA)
> +#define RICHACE_WRITE_MODE (RICHACE_WRITE_DATA | RICHACE_APPEND_DATA)
> +#define RICHACE_EXECUTE_MODE RICHACE_EXECUTE
> +#define RICHACE_ANYONE_MODE (RICHACE_READ_ATTRIBUTES | RICHACE_READ_ACL | RICHACE_SYNCHRONIZE)
> +#define RICHACE_OWNER_MODE (RICHACE_WRITE_ATTRIBUTES | RICHACE_WRITE_ACL)
>
> /* flags used to simulate posix default ACLs */
> -#define NFS4_INHERITANCE_FLAGS (NFS4_ACE_FILE_INHERIT_ACE \
> - | NFS4_ACE_DIRECTORY_INHERIT_ACE)
> -
> -#define NFS4_SUPPORTED_FLAGS (NFS4_INHERITANCE_FLAGS \
> - | NFS4_ACE_INHERIT_ONLY_ACE \
> - | NFS4_ACE_IDENTIFIER_GROUP)
> +#define RICHACE_SUPPORTED_FLAGS ( \
> + RICHACE_FILE_INHERIT_ACE | \
> + RICHACE_DIRECTORY_INHERIT_ACE | \
> + RICHACE_INHERIT_ONLY_ACE | \
> + RICHACE_IDENTIFIER_GROUP | \
> + RICHACE_SPECIAL_WHO)
>
> static u32
> mask_from_posix(unsigned short perm, unsigned int flags)
> {
> - int mask = NFS4_ANYONE_MODE;
> + int mask = RICHACE_ANYONE_MODE;
>
> - if (flags & NFS4_ACL_OWNER)
> - mask |= NFS4_OWNER_MODE;
> + if (flags & FLAG_OWNER)
> + mask |= RICHACE_OWNER_MODE;
> if (perm & ACL_READ)
> - mask |= NFS4_READ_MODE;
> + mask |= RICHACE_READ_MODE;
> if (perm & ACL_WRITE)
> - mask |= NFS4_WRITE_MODE;
> - if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR))
> - mask |= NFS4_ACE_DELETE_CHILD;
> + mask |= RICHACE_WRITE_MODE;
> + if ((perm & ACL_WRITE) && (flags & FLAG_DIRECTORY))
> + mask |= RICHACE_DELETE_CHILD;
> if (perm & ACL_EXECUTE)
> - mask |= NFS4_EXECUTE_MODE;
> + mask |= RICHACE_EXECUTE_MODE;
> return mask;
> }
>
> @@ -84,13 +87,13 @@ deny_mask_from_posix(unsigned short perm, u32 flags)
> u32 mask = 0;
>
> if (perm & ACL_READ)
> - mask |= NFS4_READ_MODE;
> + mask |= RICHACE_READ_MODE;
> if (perm & ACL_WRITE)
> - mask |= NFS4_WRITE_MODE;
> - if ((perm & ACL_WRITE) && (flags & NFS4_ACL_DIR))
> - mask |= NFS4_ACE_DELETE_CHILD;
> + mask |= RICHACE_WRITE_MODE;
> + if ((perm & ACL_WRITE) && (flags & FLAG_DIRECTORY))
> + mask |= RICHACE_DELETE_CHILD;
> if (perm & ACL_EXECUTE)
> - mask |= NFS4_EXECUTE_MODE;
> + mask |= RICHACE_EXECUTE_MODE;
> return mask;
> }
>
> @@ -106,32 +109,33 @@ deny_mask_from_posix(unsigned short perm, u32 flags)
> static void
> low_mode_from_nfs4(u32 perm, unsigned short *mode, unsigned int flags)
> {
> - u32 write_mode = NFS4_WRITE_MODE;
> + u32 write_mode = RICHACE_WRITE_MODE;
>
> - if (flags & NFS4_ACL_DIR)
> - write_mode |= NFS4_ACE_DELETE_CHILD;
> + if (flags & FLAG_DIRECTORY)
> + write_mode |= RICHACE_DELETE_CHILD;
> *mode = 0;
> - if ((perm & NFS4_READ_MODE) == NFS4_READ_MODE)
> + if ((perm & RICHACE_READ_MODE) == RICHACE_READ_MODE)
> *mode |= ACL_READ;
> if ((perm & write_mode) == write_mode)
> *mode |= ACL_WRITE;
> - if ((perm & NFS4_EXECUTE_MODE) == NFS4_EXECUTE_MODE)
> + if ((perm & RICHACE_EXECUTE_MODE) == RICHACE_EXECUTE_MODE)
> *mode |= ACL_EXECUTE;
> }
>
> -static short ace2type(struct nfs4_ace *);
> -static void _posix_to_nfsv4_one(struct posix_acl *, struct nfs4_acl *,
> +static short ace2type(struct richace *);
> +static void _posix_to_richacl_one(struct posix_acl *, struct richacl_alloc *,
> unsigned int);
>
> int
> -nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
> - struct nfs4_acl **acl)
> +nfsd4_get_acl(struct svc_rqst *rqstp, struct dentry *dentry,
> + struct richacl **acl)
> {
> 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 size = 0;
> + int count;
>
> pacl = get_acl(inode, ACL_TYPE_ACCESS);
> if (!pacl)
> @@ -141,10 +145,10 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
> return PTR_ERR(pacl);
>
> /* allocate for worst case: one (deny, allow) pair each: */
> - size += 2 * pacl->a_count;
> + count = 2 * pacl->a_count;
>
> if (S_ISDIR(inode->i_mode)) {
> - flags = NFS4_ACL_DIR;
> + flags = FLAG_DIRECTORY;
> dpacl = get_acl(inode, ACL_TYPE_DEFAULT);
> if (IS_ERR(dpacl)) {
> error = PTR_ERR(dpacl);
> @@ -152,20 +156,20 @@ nfsd4_get_nfs4_acl(struct svc_rqst *rqstp, struct dentry *dentry,
> }
>
> if (dpacl)
> - size += 2 * dpacl->a_count;
> + count += 2 * dpacl->a_count;
> }
>
> - *acl = kmalloc(nfs4_acl_bytes(size), GFP_KERNEL);
> - if (*acl == NULL) {
> + if (!richacl_prepare(&alloc, count)) {
> error = -ENOMEM;
> goto out;
> }
> - (*acl)->naces = 0;
>
> - _posix_to_nfsv4_one(pacl, *acl, flags & ~NFS4_ACL_TYPE_DEFAULT);
> + _posix_to_richacl_one(pacl, &alloc, flags);
>
> if (dpacl)
> - _posix_to_nfsv4_one(dpacl, *acl, flags | NFS4_ACL_TYPE_DEFAULT);
> + _posix_to_richacl_one(dpacl, &alloc, flags | FLAG_DEFAULT_ACL);
> +
> + *acl = alloc.acl;
>
> out:
> posix_acl_release(dpacl);
> @@ -228,21 +232,22 @@ summarize_posix_acl(struct posix_acl *acl, struct posix_acl_summary *pas)
>
> /* We assume the acl has been verified with posix_acl_valid. */
> static void
> -_posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
> - unsigned int flags)
> +_posix_to_richacl_one(struct posix_acl *pacl, struct richacl_alloc *alloc,
> + unsigned int flags)
> {
> struct posix_acl_entry *pa, *group_owner_entry;
> - struct nfs4_ace *ace;
> + struct richace *ace;
> struct posix_acl_summary pas;
> unsigned short deny;
> - int eflag = ((flags & NFS4_ACL_TYPE_DEFAULT) ?
> - NFS4_INHERITANCE_FLAGS | NFS4_ACE_INHERIT_ONLY_ACE : 0);
> + int e_flags = ((flags & FLAG_DEFAULT_ACL) ?
> + (RICHACE_FILE_INHERIT_ACE |
> + RICHACE_DIRECTORY_INHERIT_ACE |
> + RICHACE_INHERIT_ONLY_ACE) : 0);
>
> BUG_ON(pacl->a_count < 3);
> summarize_posix_acl(pacl, &pas);
>
> pa = pacl->a_entries;
> - ace = acl->aces + acl->naces;
>
> /* We could deny everything not granted by the owner: */
> deny = ~pas.owner;
> @@ -252,42 +257,35 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
> */
> deny &= pas.users | pas.group | pas.groups | pas.other;
> if (deny) {
> - ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
> - ace->flag = eflag;
> - ace->access_mask = deny_mask_from_posix(deny, flags);
> - ace->whotype = NFS4_ACL_WHO_OWNER;
> - ace++;
> - acl->naces++;
> + ace = richacl_append_entry(alloc);
> + ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
> + ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
> + ace->e_mask = deny_mask_from_posix(deny, flags);
> + ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
> }
>
> - ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
> - ace->flag = eflag;
> - ace->access_mask = mask_from_posix(pa->e_perm, flags | NFS4_ACL_OWNER);
> - ace->whotype = NFS4_ACL_WHO_OWNER;
> - ace++;
> - acl->naces++;
> + ace = richacl_append_entry(alloc);
> + ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
> + ace->e_mask = mask_from_posix(pa->e_perm, flags | FLAG_OWNER);
> + ace->e_id.special = RICHACE_OWNER_SPECIAL_ID;
> pa++;
>
> while (pa->e_tag == ACL_USER) {
> deny = ~(pa->e_perm & pas.mask);
> deny &= pas.groups | pas.group | pas.other;
> if (deny) {
> - ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
> - ace->flag = eflag;
> - ace->access_mask = deny_mask_from_posix(deny, flags);
> - ace->whotype = NFS4_ACL_WHO_NAMED;
> - ace->who_uid = pa->e_uid;
> - ace++;
> - acl->naces++;
> + ace = richacl_append_entry(alloc);
> + ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
> + ace->e_flags = e_flags;
> + ace->e_mask = deny_mask_from_posix(deny, flags);
> + ace->e_id.uid = pa->e_uid;
> }
> - ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
> - ace->flag = eflag;
> - ace->access_mask = mask_from_posix(pa->e_perm & pas.mask,
> - flags);
> - ace->whotype = NFS4_ACL_WHO_NAMED;
> - ace->who_uid = pa->e_uid;
> - ace++;
> - acl->naces++;
> + ace = richacl_append_entry(alloc);
> + ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = e_flags;
> + ace->e_mask = mask_from_posix(pa->e_perm & pas.mask, flags);
> + ace->e_id.uid = pa->e_uid;
> pa++;
> }
>
> @@ -298,23 +296,19 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
>
> group_owner_entry = pa;
>
> - ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
> - ace->flag = eflag;
> - ace->access_mask = mask_from_posix(pas.group, flags);
> - ace->whotype = NFS4_ACL_WHO_GROUP;
> - ace++;
> - acl->naces++;
> + ace = richacl_append_entry(alloc);
> + ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
> + ace->e_mask = mask_from_posix(pas.group, flags);
> + ace->e_id.special = RICHACE_GROUP_SPECIAL_ID;
> pa++;
>
> while (pa->e_tag == ACL_GROUP) {
> - ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
> - ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
> - ace->access_mask = mask_from_posix(pa->e_perm & pas.mask,
> - flags);
> - ace->whotype = NFS4_ACL_WHO_NAMED;
> - ace->who_gid = pa->e_gid;
> - ace++;
> - acl->naces++;
> + ace = richacl_append_entry(alloc);
> + ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = e_flags | RICHACE_IDENTIFIER_GROUP;
> + ace->e_mask = mask_from_posix(pa->e_perm & pas.mask, flags);
> + ace->e_id.gid = pa->e_gid;
> pa++;
> }
>
> @@ -324,12 +318,11 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
>
> deny = ~pas.group & pas.other;
> if (deny) {
> - ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
> - ace->flag = eflag;
> - ace->access_mask = deny_mask_from_posix(deny, flags);
> - ace->whotype = NFS4_ACL_WHO_GROUP;
> - ace++;
> - acl->naces++;
> + ace = richacl_append_entry(alloc);
> + ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
> + ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
> + ace->e_mask = deny_mask_from_posix(deny, flags);
> + ace->e_id.special = RICHACE_GROUP_SPECIAL_ID;
> }
> pa++;
>
> @@ -337,24 +330,22 @@ _posix_to_nfsv4_one(struct posix_acl *pacl, struct nfs4_acl *acl,
> deny = ~(pa->e_perm & pas.mask);
> deny &= pas.other;
> if (deny) {
> - ace->type = NFS4_ACE_ACCESS_DENIED_ACE_TYPE;
> - ace->flag = eflag | NFS4_ACE_IDENTIFIER_GROUP;
> - ace->access_mask = deny_mask_from_posix(deny, flags);
> - ace->whotype = NFS4_ACL_WHO_NAMED;
> - ace->who_gid = pa->e_gid;
> - ace++;
> - acl->naces++;
> + ace = richacl_append_entry(alloc);
> + ace->e_type = RICHACE_ACCESS_DENIED_ACE_TYPE;
> + ace->e_flags = e_flags | RICHACE_IDENTIFIER_GROUP;
> + ace->e_mask = deny_mask_from_posix(deny, flags);
> + ace->e_id.gid = pa->e_gid;
> }
> pa++;
> }
>
> if (pa->e_tag == ACL_MASK)
> pa++;
> - ace->type = NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE;
> - ace->flag = eflag;
> - ace->access_mask = mask_from_posix(pa->e_perm, flags);
> - ace->whotype = NFS4_ACL_WHO_EVERYONE;
> - acl->naces++;
> + ace = richacl_append_entry(alloc);
> + ace->e_type = RICHACE_ACCESS_ALLOWED_ACE_TYPE;
> + ace->e_flags = e_flags | RICHACE_SPECIAL_WHO;
> + ace->e_mask = mask_from_posix(pa->e_perm, flags);
> + ace->e_id.special = RICHACE_EVERYONE_SPECIAL_ID;
> }
>
> static bool
> @@ -498,7 +489,7 @@ posix_state_to_acl(struct posix_acl_state *state, unsigned int flags)
> * and effective cases: when there are no inheritable ACEs,
> * calls ->set_acl with a NULL ACL structure.
> */
> - if (state->empty && (flags & NFS4_ACL_TYPE_DEFAULT))
> + if (state->empty && (flags & FLAG_DEFAULT_ACL))
> return NULL;
>
> /*
> @@ -617,24 +608,24 @@ static void allow_bits_array(struct posix_ace_state_array *a, u32 mask)
> }
>
> static void process_one_v4_ace(struct posix_acl_state *state,
> - struct nfs4_ace *ace)
> + struct richace *ace)
> {
> - u32 mask = ace->access_mask;
> + u32 mask = ace->e_mask;
> int i;
>
> state->empty = 0;
>
> switch (ace2type(ace)) {
> case ACL_USER_OBJ:
> - if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
> + if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
> allow_bits(&state->owner, mask);
> } else {
> deny_bits(&state->owner, mask);
> }
> break;
> case ACL_USER:
> - i = find_uid(state, ace->who_uid);
> - if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
> + i = find_uid(state, ace->e_id.uid);
> + if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
> allow_bits(&state->users->aces[i].perms, mask);
> } else {
> deny_bits(&state->users->aces[i].perms, mask);
> @@ -643,7 +634,7 @@ static void process_one_v4_ace(struct posix_acl_state *state,
> }
> break;
> case ACL_GROUP_OBJ:
> - if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
> + if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
> allow_bits(&state->group, mask);
> } else {
> deny_bits(&state->group, mask);
> @@ -655,8 +646,8 @@ static void process_one_v4_ace(struct posix_acl_state *state,
> }
> break;
> case ACL_GROUP:
> - i = find_gid(state, ace->who_gid);
> - if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
> + i = find_gid(state, ace->e_id.gid);
> + if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
> allow_bits(&state->groups->aces[i].perms, mask);
> } else {
> deny_bits(&state->groups->aces[i].perms, mask);
> @@ -669,7 +660,7 @@ static void process_one_v4_ace(struct posix_acl_state *state,
> }
> break;
> case ACL_OTHER:
> - if (ace->type == NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE) {
> + if (ace->e_type == RICHACE_ACCESS_ALLOWED_ACE_TYPE) {
> allow_bits(&state->owner, mask);
> allow_bits(&state->group, mask);
> allow_bits(&state->other, mask);
> @@ -687,32 +678,33 @@ static void process_one_v4_ace(struct posix_acl_state *state,
> }
> }
>
> -static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
> +static int nfs4_richacl_to_posix(struct richacl *acl,
> struct posix_acl **pacl, struct posix_acl **dpacl,
> unsigned int flags)
> {
> struct posix_acl_state effective_acl_state, default_acl_state;
> - struct nfs4_ace *ace;
> + struct richace *ace;
> int ret;
>
> - ret = init_state(&effective_acl_state, acl->naces);
> + ret = init_state(&effective_acl_state, acl->a_count);
> if (ret)
> return ret;
> - ret = init_state(&default_acl_state, acl->naces);
> + ret = init_state(&default_acl_state, acl->a_count);
> if (ret)
> goto out_estate;
> ret = -EINVAL;
> - for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
> - if (ace->type != NFS4_ACE_ACCESS_ALLOWED_ACE_TYPE &&
> - ace->type != NFS4_ACE_ACCESS_DENIED_ACE_TYPE)
> + richacl_for_each_entry(ace, acl) {
> + if (ace->e_type != RICHACE_ACCESS_ALLOWED_ACE_TYPE &&
> + ace->e_type != RICHACE_ACCESS_DENIED_ACE_TYPE)
> goto out_dstate;
> - if (ace->flag & ~NFS4_SUPPORTED_FLAGS)
> + if (ace->e_flags & ~RICHACE_SUPPORTED_FLAGS)
> goto out_dstate;
> - if ((ace->flag & NFS4_INHERITANCE_FLAGS) == 0) {
> + if ((ace->e_flags & (RICHACE_FILE_INHERIT_ACE |
> + RICHACE_DIRECTORY_INHERIT_ACE)) == 0) {
> process_one_v4_ace(&effective_acl_state, ace);
> continue;
> }
> - if (!(flags & NFS4_ACL_DIR))
> + if (!(flags & FLAG_DIRECTORY))
> goto out_dstate;
> /*
> * Note that when only one of FILE_INHERIT or DIRECTORY_INHERIT
> @@ -721,7 +713,7 @@ static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
> */
> process_one_v4_ace(&default_acl_state, ace);
>
> - if (!(ace->flag & NFS4_ACE_INHERIT_ONLY_ACE))
> + if (!(ace->e_flags & RICHACE_INHERIT_ONLY_ACE))
> process_one_v4_ace(&effective_acl_state, ace);
> }
> *pacl = posix_state_to_acl(&effective_acl_state, flags);
> @@ -731,7 +723,7 @@ static int nfs4_acl_nfsv4_to_posix(struct nfs4_acl *acl,
> goto out_dstate;
> }
> *dpacl = posix_state_to_acl(&default_acl_state,
> - flags | NFS4_ACL_TYPE_DEFAULT);
> + flags | FLAG_DEFAULT_ACL);
> if (IS_ERR(*dpacl)) {
> ret = PTR_ERR(*dpacl);
> *dpacl = NULL;
> @@ -750,8 +742,7 @@ out_estate:
> }
>
> __be32
> -nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
> - struct nfs4_acl *acl)
> +nfsd4_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, struct richacl *acl)
> {
> __be32 error;
> int host_error;
> @@ -772,9 +763,9 @@ nfsd4_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
> return nfserr_attrnotsupp;
>
> if (S_ISDIR(inode->i_mode))
> - flags = NFS4_ACL_DIR;
> + flags = FLAG_DIRECTORY;
>
> - host_error = nfs4_acl_nfsv4_to_posix(acl, &pacl, &dpacl, flags);
> + host_error = nfs4_richacl_to_posix(acl, &pacl, &dpacl, flags);
> if (host_error == -EINVAL)
> return nfserr_attrnotsupp;
> if (host_error < 0)
> @@ -801,82 +792,62 @@ out_nfserr:
>
>
> static short
> -ace2type(struct nfs4_ace *ace)
> +ace2type(struct richace *ace)
> {
> - switch (ace->whotype) {
> - case NFS4_ACL_WHO_NAMED:
> - return (ace->flag & NFS4_ACE_IDENTIFIER_GROUP ?
> - ACL_GROUP : ACL_USER);
> - case NFS4_ACL_WHO_OWNER:
> + if (ace->e_flags & RICHACE_SPECIAL_WHO) {
> + switch (ace->e_id.special) {
> + case RICHACE_OWNER_SPECIAL_ID:
> return ACL_USER_OBJ;
> - case NFS4_ACL_WHO_GROUP:
> + case RICHACE_GROUP_SPECIAL_ID:
> return ACL_GROUP_OBJ;
> - case NFS4_ACL_WHO_EVERYONE:
> + case RICHACE_EVERYONE_SPECIAL_ID:
> return ACL_OTHER;
> + default:
> + BUG();
> + }
> }
> - BUG();
> - return -1;
> -}
> -
> -/*
> - * return the size of the struct nfs4_acl required to represent an acl
> - * with @entries entries.
> - */
> -int nfs4_acl_bytes(int entries)
> -{
> - return sizeof(struct nfs4_acl) + entries * sizeof(struct nfs4_ace);
> + return ace->e_flags & RICHACE_IDENTIFIER_GROUP ? ACL_GROUP : ACL_USER;
> }
>
> -static struct {
> - char *string;
> - int stringlen;
> - int type;
> -} s2t_map[] = {
> - {
> - .string = "OWNER@",
> - .stringlen = sizeof("OWNER@") - 1,
> - .type = NFS4_ACL_WHO_OWNER,
> - },
> - {
> - .string = "GROUP@",
> - .stringlen = sizeof("GROUP@") - 1,
> - .type = NFS4_ACL_WHO_GROUP,
> - },
> - {
> - .string = "EVERYONE@",
> - .stringlen = sizeof("EVERYONE@") - 1,
> - .type = NFS4_ACL_WHO_EVERYONE,
> - },
> -};
> -
> -int
> -nfs4_acl_get_whotype(char *p, u32 len)
> +__be32 nfsd4_decode_ace_who(struct richace *ace, struct svc_rqst *rqstp,
> + char *who, u32 len)
> {
> - int i;
> -
> - for (i = 0; i < ARRAY_SIZE(s2t_map); i++) {
> - if (s2t_map[i].stringlen == len &&
> - 0 == memcmp(s2t_map[i].string, p, len))
> - return s2t_map[i].type;
> + int special_id;
> +
> + 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 nfs_ok;
> }
> - return NFS4_ACL_WHO_NAMED;
> + if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
> + return nfsd_map_name_to_gid(rqstp, who, len, &ace->e_id.gid);
> + else
> + return nfsd_map_name_to_uid(rqstp, who, len, &ace->e_id.uid);
> }
>
> -__be32 nfs4_acl_write_who(struct xdr_stream *xdr, int who)
> +__be32 nfsd4_encode_ace_who(struct xdr_stream *xdr, struct svc_rqst *rqstp,
> + struct richace *ace)
> {
> - __be32 *p;
> - int i;
> -
> - for (i = 0; i < ARRAY_SIZE(s2t_map); i++) {
> - if (s2t_map[i].type != who)
> - continue;
> - p = xdr_reserve_space(xdr, s2t_map[i].stringlen + 4);
> + if (ace->e_flags & RICHACE_SPECIAL_WHO) {
> + unsigned int special_id = ace->e_id.special;
> + const char *who;
> + unsigned int len;
> + __be32 *p;
> +
> + if (!nfs4acl_special_id_to_who(special_id, &who, &len)) {
> + WARN_ON_ONCE(1);
> + return nfserr_serverfault;
> + }
> + p = xdr_reserve_space(xdr, len + 4);
> if (!p)
> return nfserr_resource;
> - p = xdr_encode_opaque(p, s2t_map[i].string,
> - s2t_map[i].stringlen);
> + p = xdr_encode_opaque(p, who, len);
> return 0;
> }
> - WARN_ON_ONCE(1);
> - return nfserr_serverfault;
> + if (ace->e_flags & RICHACE_IDENTIFIER_GROUP)
> + return nfsd4_encode_group(xdr, rqstp, ace->e_id.gid);
> + else
> + return nfsd4_encode_user(xdr, rqstp, ace->e_id.uid);
> }
> diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c
> index 90cfda7..8c2cb16 100644
> --- a/fs/nfsd/nfs4proc.c
> +++ b/fs/nfsd/nfs4proc.c
> @@ -159,12 +159,12 @@ is_create_with_attrs(struct nfsd4_open *open)
> * in the returned attr bitmap.
> */
> static void
> -do_set_nfs4_acl(struct svc_rqst *rqstp, struct svc_fh *fhp,
> - struct nfs4_acl *acl, u32 *bmval)
> +do_set_acl(struct svc_rqst *rqstp, struct svc_fh *fhp, struct richacl *acl,
> + u32 *bmval)
> {
> __be32 status;
>
> - status = nfsd4_set_nfs4_acl(rqstp, fhp, acl);
> + status = nfsd4_set_acl(rqstp, fhp, acl);
> if (status)
> /*
> * We should probably fail the whole open at this point,
> @@ -299,7 +299,7 @@ do_open_lookup(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, stru
> goto out;
>
> if (is_create_with_attrs(open) && open->op_acl != NULL)
> - do_set_nfs4_acl(rqstp, *resfh, open->op_acl, open->op_bmval);
> + do_set_acl(rqstp, *resfh, open->op_acl, open->op_bmval);
>
> nfsd4_set_open_owner_reply_cache(cstate, open, *resfh);
> accmode = NFSD_MAY_NOP;
> @@ -674,8 +674,7 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> nfsd4_security_inode_setsecctx(&resfh, &create->cr_label, create->cr_bmval);
>
> if (create->cr_acl != NULL)
> - do_set_nfs4_acl(rqstp, &resfh, create->cr_acl,
> - create->cr_bmval);
> + do_set_acl(rqstp, &resfh, create->cr_acl, create->cr_bmval);
>
> fh_unlock(&cstate->current_fh);
> set_change_info(&create->cr_cinfo, &cstate->current_fh);
> @@ -940,8 +939,8 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
> goto out;
>
> if (setattr->sa_acl != NULL)
> - status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh,
> - setattr->sa_acl);
> + status = nfsd4_set_acl(rqstp, &cstate->current_fh,
> + setattr->sa_acl);
> if (status)
> goto out;
> if (setattr->sa_label.len)
> diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c
> index 0768251..465f82a 100644
> --- a/fs/nfsd/nfs4xdr.c
> +++ b/fs/nfsd/nfs4xdr.c
> @@ -303,7 +303,7 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
>
> static __be32
> nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
> - struct iattr *iattr, struct nfs4_acl **acl,
> + struct iattr *iattr, struct richacl **acl,
> struct xdr_netobj *label)
> {
> int expected_len, len = 0;
> @@ -326,38 +326,31 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
> }
> if (bmval[0] & FATTR4_WORD0_ACL) {
> u32 nace;
> - struct nfs4_ace *ace;
> + struct richace *ace;
>
> READ_BUF(4); len += 4;
> nace = be32_to_cpup(p++);
>
> - if (nace > NFS4_ACL_MAX)
> + if (nace > NFSD4_ACL_MAX)
> return nfserr_fbig;
>
> - *acl = svcxdr_tmpalloc(argp, nfs4_acl_bytes(nace));
> + *acl = svcxdr_alloc_richacl(argp, nace);
> if (*acl == NULL)
> return nfserr_jukebox;
>
> - (*acl)->naces = nace;
> - for (ace = (*acl)->aces; ace < (*acl)->aces + nace; ace++) {
> + richacl_for_each_entry(ace, *acl) {
> READ_BUF(16); len += 16;
> - ace->type = be32_to_cpup(p++);
> - ace->flag = be32_to_cpup(p++);
> - ace->access_mask = be32_to_cpup(p++);
> + 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)
> + return nfserr_inval;
> dummy32 = be32_to_cpup(p++);
> READ_BUF(dummy32);
> len += XDR_QUADLEN(dummy32) << 2;
> READMEM(buf, dummy32);
> - ace->whotype = nfs4_acl_get_whotype(buf, dummy32);
> - status = nfs_ok;
> - if (ace->whotype != NFS4_ACL_WHO_NAMED)
> - ;
> - else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
> - status = nfsd_map_name_to_gid(argp->rqstp,
> - buf, dummy32, &ace->who_gid);
> - else
> - status = nfsd_map_name_to_uid(argp->rqstp,
> - buf, dummy32, &ace->who_uid);
> + status = nfsd4_decode_ace_who(ace, argp->rqstp,
> + buf, dummy32);
> if (status)
> return status;
> }
> @@ -2147,18 +2140,6 @@ static u32 nfs4_file_type(umode_t mode)
> };
> }
>
> -static inline __be32
> -nfsd4_encode_aclname(struct xdr_stream *xdr, struct svc_rqst *rqstp,
> - struct nfs4_ace *ace)
> -{
> - if (ace->whotype != NFS4_ACL_WHO_NAMED)
> - return nfs4_acl_write_who(xdr, ace->whotype);
> - else if (ace->flag & NFS4_ACE_IDENTIFIER_GROUP)
> - return nfsd4_encode_group(xdr, rqstp, ace->who_gid);
> - else
> - return nfsd4_encode_user(xdr, rqstp, ace->who_uid);
> -}
> -
> #define WORD0_ABSENT_FS_ATTRS (FATTR4_WORD0_FS_LOCATIONS | FATTR4_WORD0_FSID | \
> FATTR4_WORD0_RDATTR_ERROR)
> #define WORD1_ABSENT_FS_ATTRS FATTR4_WORD1_MOUNTED_ON_FILEID
> @@ -2249,7 +2230,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
> u32 rdattr_err = 0;
> __be32 status;
> int err;
> - struct nfs4_acl *acl = NULL;
> + struct richacl *acl = NULL;
> void *context = NULL;
> int contextlen;
> bool contextsupport = false;
> @@ -2295,7 +2276,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
> fhp = tempfh;
> }
> if (bmval0 & FATTR4_WORD0_ACL) {
> - err = nfsd4_get_nfs4_acl(rqstp, dentry, &acl);
> + err = nfsd4_get_acl(rqstp, dentry, &acl);
> if (err == -EOPNOTSUPP)
> bmval0 &= ~FATTR4_WORD0_ACL;
> else if (err == -EINVAL) {
> @@ -2469,7 +2450,7 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
> *p++ = cpu_to_be32(rdattr_err);
> }
> if (bmval0 & FATTR4_WORD0_ACL) {
> - struct nfs4_ace *ace;
> + struct richace *ace;
>
> if (acl == NULL) {
> p = xdr_reserve_space(xdr, 4);
> @@ -2482,17 +2463,16 @@ nfsd4_encode_fattr(struct xdr_stream *xdr, struct svc_fh *fhp,
> p = xdr_reserve_space(xdr, 4);
> if (!p)
> goto out_resource;
> - *p++ = cpu_to_be32(acl->naces);
> + *p++ = cpu_to_be32(acl->a_count);
>
> - for (ace = acl->aces; ace < acl->aces + acl->naces; ace++) {
> + richacl_for_each_entry(ace, acl) {
> p = xdr_reserve_space(xdr, 4*3);
> if (!p)
> goto out_resource;
> - *p++ = cpu_to_be32(ace->type);
> - *p++ = cpu_to_be32(ace->flag);
> - *p++ = cpu_to_be32(ace->access_mask &
> - NFS4_ACE_MASK_ALL);
> - status = nfsd4_encode_aclname(xdr, rqstp, ace);
> + *p++ = cpu_to_be32(ace->e_type);
> + *p++ = cpu_to_be32(ace->e_flags & ~RICHACE_SPECIAL_WHO);
> + *p++ = cpu_to_be32(ace->e_mask & NFS4_ACE_MASK_ALL);
> + status = nfsd4_encode_ace_who(xdr, rqstp, ace);
> if (status)
> goto out;
> }
> @@ -2755,7 +2735,7 @@ out:
> if (context)
> security_release_secctx(context, contextlen);
> #endif /* CONFIG_NFSD_V4_SECURITY_LABEL */
> - kfree(acl);
> + richacl_put(acl);
> if (tempfh) {
> fh_put(tempfh);
> kfree(tempfh);
> diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h
> index b698585..c311066 100644
> --- a/fs/nfsd/xdr4.h
> +++ b/fs/nfsd/xdr4.h
> @@ -118,7 +118,7 @@ struct nfsd4_create {
> u32 cr_bmval[3]; /* request */
> struct iattr cr_iattr; /* request */
> struct nfsd4_change_info cr_cinfo; /* response */
> - struct nfs4_acl *cr_acl;
> + struct richacl *cr_acl;
> struct xdr_netobj cr_label;
> };
> #define cr_datalen u.link.datalen
> @@ -248,7 +248,7 @@ struct nfsd4_open {
> struct nfs4_file *op_file; /* used during processing */
> struct nfs4_ol_stateid *op_stp; /* used during processing */
> struct nfs4_clnt_odstate *op_odstate; /* used during processing */
> - struct nfs4_acl *op_acl;
> + struct richacl *op_acl;
> struct xdr_netobj op_label;
> };
>
> @@ -332,7 +332,7 @@ struct nfsd4_setattr {
> stateid_t sa_stateid; /* request */
> u32 sa_bmval[3]; /* request */
> struct iattr sa_iattr; /* request */
> - struct nfs4_acl *sa_acl;
> + struct richacl *sa_acl;
> struct xdr_netobj sa_label;
> };
>
> diff --git a/include/linux/nfs4.h b/include/linux/nfs4.h
> index b8e72aa..992ddc4 100644
> --- a/include/linux/nfs4.h
> +++ b/include/linux/nfs4.h
> @@ -16,29 +16,6 @@
> #include <linux/uidgid.h>
> #include <uapi/linux/nfs4.h>
>
> -enum nfs4_acl_whotype {
> - NFS4_ACL_WHO_NAMED = 0,
> - NFS4_ACL_WHO_OWNER,
> - NFS4_ACL_WHO_GROUP,
> - NFS4_ACL_WHO_EVERYONE,
> -};
> -
> -struct nfs4_ace {
> - uint32_t type;
> - uint32_t flag;
> - uint32_t access_mask;
> - int whotype;
> - union {
> - kuid_t who_uid;
> - kgid_t who_gid;
> - };
> -};
> -
> -struct nfs4_acl {
> - uint32_t naces;
> - struct nfs4_ace aces[0];
> -};
> -
> #define NFS4_MAXLABELLEN 2048
>
> struct nfs4_label {
> diff --git a/include/linux/nfs4acl.h b/include/linux/nfs4acl.h
> new file mode 100644
> index 0000000..db9f9a6
> --- /dev/null
> +++ b/include/linux/nfs4acl.h
> @@ -0,0 +1,7 @@
> +#ifndef __LINUX_NFS4ACL_H
> +#define __LINUX_NFS4ACL_H
> +
> +int nfs4acl_who_to_special_id(const char *, u32);
> +bool nfs4acl_special_id_to_who(unsigned int, const char **, unsigned int *);
> +
> +#endif
> --
> 2.4.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/