root/src/add-ons/kernel/file_systems/ntfs/libntfs/acls.c
/**
 * acls.c - General function to process NTFS ACLs
 *
 *      This module is part of ntfs-3g library, but may also be
 *      integrated in tools running over Linux or Windows
 *
 * Copyright (c) 2007-2017 Jean-Pierre Andre
 *
 * This program/include file 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 of the License, or
 * (at your option) any later version.
 *
 * This program/include file 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (in the main directory of the NTFS-3G
 * distribution in the file COPYING); if not, write to the Free Software
 * Foundation,Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#include <unistd.h>
#include <pwd.h>
#include <grp.h>

#ifdef __HAIKU__
#define getgrgid(a) NULL
#define getpwuid(a) NULL
#define getgrnam(x) NULL
#define getpwnam(x) NULL
#endif

#include "types.h"
#include "layout.h"
#include "security.h"
#include "acls.h"
#include "misc.h"

/*
 *      A few useful constants
 */

/*
 *              null SID (S-1-0-0)
 */

static const char nullsidbytes[] = {
                1,              /* revision */
                1,              /* auth count */
                0, 0, 0, 0, 0, 0,       /* base */
                0, 0, 0, 0      /* 1st level */
        };

static const SID *nullsid = (const SID*)nullsidbytes;

/*
 *              SID for world  (S-1-1-0)
 */

static const char worldsidbytes[] = {
                1,              /* revision */
                1,              /* auth count */
                0, 0, 0, 0, 0, 1,       /* base */
                0, 0, 0, 0      /* 1st level */
} ;

const SID *worldsid = (const SID*)worldsidbytes;

/*
 *              SID for authenticated user (S-1-5-11)
 */

static const char authsidbytes[] = {
                1,              /* revision */
                1,              /* auth count */
                0, 0, 0, 0, 0, 5,       /* base */
                11, 0, 0, 0     /* 1st level */ 
};
                
static const SID *authsid = (const SID*)authsidbytes;

/*
 *              SID for administrator
 */

static const char adminsidbytes[] = {
                1,              /* revision */
                2,              /* auth count */
                0, 0, 0, 0, 0, 5,       /* base */
                32, 0, 0, 0,    /* 1st level */
                32, 2, 0, 0     /* 2nd level */
};

const SID *adminsid = (const SID*)adminsidbytes;

/*
 *              SID for system
 */

static const char systemsidbytes[] = {
                1,              /* revision */
                1,              /* auth count */
                0, 0, 0, 0, 0, 5,       /* base */
                18, 0, 0, 0     /* 1st level */
        };

static const SID *systemsid = (const SID*)systemsidbytes;

/*
 *              SID for generic creator-owner
 *              S-1-3-0
 */

static const char ownersidbytes[] = {
                1,              /* revision */
                1,              /* auth count */
                0, 0, 0, 0, 0, 3,       /* base */
                0, 0, 0, 0      /* 1st level */
} ;

static const SID *ownersid = (const SID*)ownersidbytes;

/*
 *              SID for generic creator-group
 *              S-1-3-1
 */

static const char groupsidbytes[] = {
                1,              /* revision */
                1,              /* auth count */
                0, 0, 0, 0, 0, 3,       /* base */
                1, 0, 0, 0      /* 1st level */
} ;

static const SID *groupsid = (const SID*)groupsidbytes;

/*
 *              Determine the size of a SID
 */

int ntfs_sid_size(const SID * sid)
{
        return (sid->sub_authority_count * 4 + 8);
}

/*
 *              Test whether two SID are equal
 */

BOOL ntfs_same_sid(const SID *first, const SID *second)
{
        int size;

        size = ntfs_sid_size(first);
        return ((ntfs_sid_size(second) == size)
                && !memcmp(first, second, size));
}

/*
 *              Test whether a SID means "world user"
 *      Local users group recognized as world
 *      Also interactive users so that /Users/Public is world accessible,
 *      but only if Posix ACLs are not enabled (if Posix ACLs are enabled,
 *      access to /Users/Public should be done by defining interactive users
 *      as a mapped group.)
 */

static int is_world_sid(const SID * usid)
{
        return (
             /* check whether S-1-1-0 : world */
               ((usid->sub_authority_count == 1)
            && (usid->identifier_authority.high_part ==  const_cpu_to_be16(0))
            && (usid->identifier_authority.low_part ==  const_cpu_to_be32(1))
            && (usid->sub_authority[0] == const_cpu_to_le32(0)))

             /* check whether S-1-5-32-545 : local user */
          ||   ((usid->sub_authority_count == 2)
            && (usid->identifier_authority.high_part ==  const_cpu_to_be16(0))
            && (usid->identifier_authority.low_part ==  const_cpu_to_be32(5))
            && (usid->sub_authority[0] == const_cpu_to_le32(32))
            && (usid->sub_authority[1] == const_cpu_to_le32(545)))

             /* check whether S-1-5-11 : authenticated user */
          ||   ((usid->sub_authority_count == 1)
            && (usid->identifier_authority.high_part ==  const_cpu_to_be16(0))
            && (usid->identifier_authority.low_part ==  const_cpu_to_be32(5))
            && (usid->sub_authority[0] == const_cpu_to_le32(11)))

#if !POSIXACLS
             /* check whether S-1-5-4 : interactive user */
          ||   ((usid->sub_authority_count == 1)
            && (usid->identifier_authority.high_part ==  const_cpu_to_be16(0))
            && (usid->identifier_authority.low_part ==  const_cpu_to_be32(5))
            && (usid->sub_authority[0] == const_cpu_to_le32(4)))
#endif /* !POSIXACLS */
                );
}

/*
 *              Test whether a SID means "some user (or group)"
 *      Currently we only check for S-1-5-21... but we should
 *      probably test for other configurations
 */

BOOL ntfs_is_user_sid(const SID *usid)
{
        return ((usid->sub_authority_count == 5)
            && (usid->identifier_authority.high_part ==  const_cpu_to_be16(0))
            && (usid->identifier_authority.low_part ==  const_cpu_to_be32(5))
            && (usid->sub_authority[0] ==  const_cpu_to_le32(21)));
}

/*
 *              Test whether a SID means "some special group"
 *      Currently we only check for a few S-1-5-n but we should
 *      probably test for other configurations.
 *
 *      This is useful for granting access to /Users/Public for
 *      specific users when the Posix ACLs are enabled.
 */

static BOOL ntfs_known_group_sid(const SID *usid)
{
                        /* count == 1 excludes S-1-5-5-X-Y (logon) */
        return ((usid->sub_authority_count == 1)
            && (usid->identifier_authority.high_part ==  const_cpu_to_be16(0))
            && (usid->identifier_authority.low_part ==  const_cpu_to_be32(5))
            && (le32_to_cpu(usid->sub_authority[0]) >=  1)
            && (le32_to_cpu(usid->sub_authority[0]) <=  6));
}

/*
 *              Determine the size of a security attribute
 *      whatever the order of fields
 */

unsigned int ntfs_attr_size(const char *attr)
{
        const SECURITY_DESCRIPTOR_RELATIVE *phead;
        const ACL *pdacl;
        const ACL *psacl;
        const SID *psid;
        unsigned int offdacl;
        unsigned int offsacl;
        unsigned int offowner;
        unsigned int offgroup;
        unsigned int endsid;
        unsigned int endacl;
        unsigned int attrsz;

        phead = (const SECURITY_DESCRIPTOR_RELATIVE*)attr;
                /*
                 * First check group, which is the last field in all descriptors
                 * we build, and in most descriptors built by Windows
                 */
        attrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE);
        offgroup = le32_to_cpu(phead->group);
        if (offgroup >= attrsz) {
                        /* find end of GSID */
                psid = (const SID*)&attr[offgroup];
                endsid = offgroup + ntfs_sid_size(psid);
                if (endsid > attrsz) attrsz = endsid;
        }
        offowner = le32_to_cpu(phead->owner);
        if (offowner >= attrsz) {
                        /* find end of USID */
                psid = (const SID*)&attr[offowner];
                endsid = offowner + ntfs_sid_size(psid);
                attrsz = endsid;
        }
        offsacl = le32_to_cpu(phead->sacl);
        if (offsacl >= attrsz) {
                        /* find end of SACL */
                psacl = (const ACL*)&attr[offsacl];
                endacl = offsacl + le16_to_cpu(psacl->size);
                if (endacl > attrsz)
                        attrsz = endacl;
        }


                /* find end of DACL */
        offdacl = le32_to_cpu(phead->dacl);
        if (offdacl >= attrsz) {
                pdacl = (const ACL*)&attr[offdacl];
                endacl = offdacl + le16_to_cpu(pdacl->size);
                if (endacl > attrsz)
                        attrsz = endacl;
        }
        return (attrsz);
}

/**
 * ntfs_valid_sid - determine if a SID is valid
 * @sid:        SID for which to determine if it is valid
 *
 * Determine if the SID pointed to by @sid is valid.
 *
 * Return TRUE if it is valid and FALSE otherwise.
 */
BOOL ntfs_valid_sid(const SID *sid)
{
        return sid && sid->revision == SID_REVISION &&
                sid->sub_authority_count <= SID_MAX_SUB_AUTHORITIES;
}

/*
 *              Check whether a SID is acceptable for an implicit
 *      mapping pattern.
 *      It should have been already checked it is a valid user SID.
 *
 *      The last authority reference has to be >= 1000 (Windows usage)
 *      and <= 0x7fffffff, so that 30 bits from a uid and 30 more bits
 *      from a gid an be inserted with no overflow.
 */

BOOL ntfs_valid_pattern(const SID *sid)
{
        int cnt;
        u32 auth;
        le32 leauth;

        cnt = sid->sub_authority_count;
        leauth = sid->sub_authority[cnt-1];
        auth = le32_to_cpu(leauth);
        return ((auth >= 1000) && (auth <= 0x7fffffff));
}

/*
 *              Compute the uid or gid associated to a SID
 *      through an implicit mapping
 *
 *      Returns 0 (root) if it does not match pattern
 */

static u32 findimplicit(const SID *xsid, const SID *pattern, int parity)
{
        BIGSID defsid;
        SID *psid;
        u32 xid; /* uid or gid */
        int cnt;
        u32 carry;
        le32 leauth;
        u32 uauth;
        u32 xlast;
        u32 rlast;

        memcpy(&defsid,pattern,ntfs_sid_size(pattern));
        psid = (SID*)&defsid;
        cnt = psid->sub_authority_count;
        xid = 0;
        if (xsid->sub_authority_count == cnt) {
                psid->sub_authority[cnt-1] = xsid->sub_authority[cnt-1];
                leauth = xsid->sub_authority[cnt-1];
                xlast = le32_to_cpu(leauth);
                leauth = pattern->sub_authority[cnt-1];
                rlast = le32_to_cpu(leauth);

                if ((xlast > rlast) && !((xlast ^ rlast ^ parity) & 1)) {
                        /* direct check for basic situation */
                        if (ntfs_same_sid(psid,xsid))
                                xid = ((xlast - rlast) >> 1) & 0x3fffffff;
                        else {
                                /*
                                 * check whether part of mapping had to be
                                 * recorded in a higher level authority
                                 */
                                carry = 1;
                                do {
                                        leauth = psid->sub_authority[cnt-2];
                                        uauth = le32_to_cpu(leauth) + 1;
                                        psid->sub_authority[cnt-2]
                                                = cpu_to_le32(uauth);
                                } while (!ntfs_same_sid(psid,xsid)
                                         && (++carry < 4));
                                if (carry < 4)
                                        xid = (((xlast - rlast) >> 1)
                                                & 0x3fffffff) | (carry << 30);
                        }
                }
        }
        return (xid);
}

/*
 *              Find usid mapped to a Linux user
 *      Returns NULL if not found
 */

const SID *ntfs_find_usid(const struct MAPPING* usermapping,
                uid_t uid, SID *defusid)
{
        const struct MAPPING *p;
        const SID *sid;
        le32 leauth;
        u32 uauth;
        int cnt;

        if (!uid)
                sid = adminsid;
        else {
                p = usermapping;
                while (p && p->xid && ((uid_t)p->xid != uid))
                        p = p->next;
                if (p && !p->xid) {
                        /*
                         * default pattern has been reached :
                         * build an implicit SID according to pattern
                         * (the pattern format was checked while reading
                         * the mapping file)
                         */
                        memcpy(defusid, p->sid, ntfs_sid_size(p->sid));
                        cnt = defusid->sub_authority_count;
                        leauth = defusid->sub_authority[cnt-1];
                        uauth = le32_to_cpu(leauth) + 2*(uid & 0x3fffffff);
                        defusid->sub_authority[cnt-1] = cpu_to_le32(uauth);
                        if (uid & 0xc0000000) {
                                leauth = defusid->sub_authority[cnt-2];
                                uauth = le32_to_cpu(leauth) + ((uid >> 30) & 3);
                                defusid->sub_authority[cnt-2] = cpu_to_le32(uauth);
                        }
                        sid = defusid;
                } else
                        sid = (p ? p->sid : (const SID*)NULL);
        }
        return (sid);
}

/*
 *              Find Linux group mapped to a gsid
 *      Returns 0 (root) if not found
 */

const SID *ntfs_find_gsid(const struct MAPPING* groupmapping,
                gid_t gid, SID *defgsid)
{
        const struct MAPPING *p;
        const SID *sid;
        le32 leauth;
        u32 uauth;
        int cnt;

        if (!gid)
                sid = adminsid;
        else {
                p = groupmapping;
                while (p && p->xid && ((gid_t)p->xid != gid))
                        p = p->next;
                if (p && !p->xid) {
                        /*
                         * default pattern has been reached :
                         * build an implicit SID according to pattern
                         * (the pattern format was checked while reading
                         * the mapping file)
                         */
                        memcpy(defgsid, p->sid, ntfs_sid_size(p->sid));
                        cnt = defgsid->sub_authority_count;
                        leauth = defgsid->sub_authority[cnt-1];
                        uauth = le32_to_cpu(leauth) + 2*(gid & 0x3fffffff) + 1;
                        defgsid->sub_authority[cnt-1] = cpu_to_le32(uauth);
                        if (gid & 0xc0000000) {
                                leauth = defgsid->sub_authority[cnt-2];
                                uauth = le32_to_cpu(leauth) + ((gid >> 30) & 3);
                                defgsid->sub_authority[cnt-2] = cpu_to_le32(uauth);
                        }
                        sid = defgsid;
                } else
                        sid = (p ? p->sid : (const SID*)NULL);
        }
        return (sid);
}

/*
 *              Find Linux owner mapped to a usid
 *      Returns 0 (root) if not found
 */

uid_t ntfs_find_user(const struct MAPPING* usermapping, const SID *usid)
{
        uid_t uid;
        const struct MAPPING *p;

        p = usermapping;
        while (p && p->xid && !ntfs_same_sid(usid, p->sid))
                p = p->next;
        if (p && !p->xid)
                /*
                 * No explicit mapping found, try implicit mapping
                 */
                uid = findimplicit(usid,p->sid,0);
        else
                uid = (p ? p->xid : 0);
        return (uid);
}

/*
 *              Find Linux group mapped to a gsid
 *      Returns 0 (root) if not found
 */

gid_t ntfs_find_group(const struct MAPPING* groupmapping, const SID * gsid)
{
        gid_t gid;
        const struct MAPPING *p;

        p = groupmapping;
        while (p && p->xid && !ntfs_same_sid(gsid, p->sid))
                p = p->next;
        if (p && !p->xid)
                /*
                 * No explicit mapping found, try implicit mapping
                 */
                gid = findimplicit(gsid,p->sid,1);
        else
                gid = (p ? p->xid : 0);
        return (gid);
}

/*
 *              Check the validity of the ACEs in a DACL or SACL
 */

static BOOL valid_acl(const ACL *pacl, unsigned int end)
{
        const ACCESS_ALLOWED_ACE *pace;
        unsigned int offace;
        unsigned int acecnt;
        unsigned int acesz;
        unsigned int nace;
        unsigned int wantsz;
        BOOL ok;

        ok = TRUE;
        acecnt = le16_to_cpu(pacl->ace_count);
        offace = sizeof(ACL);
        for (nace = 0; (nace < acecnt) && ok; nace++) {
                /* be sure the beginning is within range */
                if ((offace + sizeof(ACCESS_ALLOWED_ACE)) > end)
                        ok = FALSE;
                else {
                        pace = (const ACCESS_ALLOWED_ACE*)
                                &((const char*)pacl)[offace];
                        acesz = le16_to_cpu(pace->size);
                        switch (pace->type) {
                        case ACCESS_ALLOWED_ACE_TYPE :
                        case ACCESS_DENIED_ACE_TYPE :
                                wantsz = ntfs_sid_size(&pace->sid) + 8;
                                if (((offace + acesz) > end)
                                    || !ntfs_valid_sid(&pace->sid)
                                    || (wantsz != acesz))
                                        ok = FALSE;
                                break;
                        case SYSTEM_AUDIT_ACE_TYPE :
                        case ACCESS_ALLOWED_CALLBACK_ACE_TYPE :
                        case ACCESS_DENIED_CALLBACK_ACE_TYPE :
                        case SYSTEM_AUDIT_CALLBACK_ACE_TYPE :
                        case SYSTEM_MANDATORY_LABEL_ACE_TYPE :
                        case SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE :
                        case SYSTEM_SCOPED_POLICY_ID_ACE_TYPE :
                                /* Extra data after the SID */
                                wantsz = ntfs_sid_size(&pace->sid) + 8;
                                if (((offace + acesz) > end)
                                    || !ntfs_valid_sid(&pace->sid)
                                    || (wantsz > acesz))
                                        ok = FALSE;
                                break;
                        default :
                                /* SID at a different location */
                                if ((offace + acesz) > end)
                                        ok = FALSE;
                                break;
                        }
                        offace += acesz;
                }
        }
        return (ok);
}

/*
 *              Do sanity checks on security descriptors read from storage
 *      basically, we make sure that every field holds within
 *      allocated storage
 *      Should not be called with a NULL argument
 *      returns TRUE if considered safe
 *              if not, error should be logged by caller
 */

BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz)
{
        const SECURITY_DESCRIPTOR_RELATIVE *phead;
        const ACL *pdacl;
        const ACL *psacl;
        unsigned int offdacl;
        unsigned int offsacl;
        unsigned int offowner;
        unsigned int offgroup;
        BOOL ok;

        ok = TRUE;

        /*
         * first check overall size if within allocation range
         * and a DACL is present
         * and owner and group SID are valid
         */

        phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
        offdacl = le32_to_cpu(phead->dacl);
        offsacl = le32_to_cpu(phead->sacl);
        offowner = le32_to_cpu(phead->owner);
        offgroup = le32_to_cpu(phead->group);
        pdacl = (const ACL*)&securattr[offdacl];
        psacl = (const ACL*)&securattr[offsacl];

                /*
                 * size check occurs before the above pointers are used
                 *
                 * "DR Watson" standard directory on WinXP has an
                 * old revision and no DACL though SE_DACL_PRESENT is set
                 */
        if ((attrsz >= sizeof(SECURITY_DESCRIPTOR_RELATIVE))
                && (phead->revision == SECURITY_DESCRIPTOR_REVISION)
                && (offowner >= sizeof(SECURITY_DESCRIPTOR_RELATIVE))
                && ((offowner + 2) < attrsz)
                && (offgroup >= sizeof(SECURITY_DESCRIPTOR_RELATIVE))
                && ((offgroup + 2) < attrsz)
                && (!offdacl
                        || ((offdacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE))
                            && (offdacl+sizeof(ACL) <= attrsz)))
                && (!offsacl
                        || ((offsacl >= sizeof(SECURITY_DESCRIPTOR_RELATIVE))
                            && (offsacl+sizeof(ACL) <= attrsz)))
                && !(phead->owner & const_cpu_to_le32(3))
                && !(phead->group & const_cpu_to_le32(3))
                && !(phead->dacl & const_cpu_to_le32(3))
                && !(phead->sacl & const_cpu_to_le32(3))
                && (ntfs_attr_size(securattr) <= attrsz)
                && ntfs_valid_sid((const SID*)&securattr[offowner])
                && ntfs_valid_sid((const SID*)&securattr[offgroup])
                        /*
                         * if there is an ACL, as indicated by offdacl,
                         * require SE_DACL_PRESENT
                         * but "Dr Watson" has SE_DACL_PRESENT though no DACL
                         */
                && (!offdacl
                    || ((phead->control & SE_DACL_PRESENT)
                        && ((pdacl->revision == ACL_REVISION)
                           || (pdacl->revision == ACL_REVISION_DS))))
                        /* same for SACL */
                && (!offsacl
                    || ((phead->control & SE_SACL_PRESENT)
                        && ((psacl->revision == ACL_REVISION)
                            || (psacl->revision == ACL_REVISION_DS))))) {
                        /*
                         *  Check the DACL and SACL if present
                         */
                if ((offdacl && !valid_acl(pdacl,attrsz - offdacl))
                   || (offsacl && !valid_acl(psacl,attrsz - offsacl)))
                        ok = FALSE;
        } else
                ok = FALSE;
        return (ok);
}

/*
 *              Copy the inheritable parts of an ACL
 *
 *      Returns the size of the new ACL
 *              or zero if nothing is inheritable
 */

int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl,
                        const SID *usid, const SID *gsid, BOOL fordir,
                        le16 inherited)
{
        unsigned int src;
        unsigned int dst;
        int oldcnt;
        int newcnt;
        unsigned int selection;
        int nace;
        int acesz;
        int usidsz;
        int gsidsz;
        BOOL acceptable;
        const ACCESS_ALLOWED_ACE *poldace;
        ACCESS_ALLOWED_ACE *pnewace;
        ACCESS_ALLOWED_ACE *pauthace;
        ACCESS_ALLOWED_ACE *pownerace;

        pauthace = (ACCESS_ALLOWED_ACE*)NULL;
        pownerace = (ACCESS_ALLOWED_ACE*)NULL;
        usidsz = ntfs_sid_size(usid);
        gsidsz = ntfs_sid_size(gsid);

        /* ACL header */

        newacl->revision = ACL_REVISION;
        newacl->alignment1 = 0;
        newacl->alignment2 = const_cpu_to_le16(0);
        src = dst = sizeof(ACL);

        selection = (fordir ? CONTAINER_INHERIT_ACE : OBJECT_INHERIT_ACE);
        newcnt = 0;
        oldcnt = le16_to_cpu(oldacl->ace_count);
        for (nace = 0; nace < oldcnt; nace++) {
                poldace = (const ACCESS_ALLOWED_ACE*)((const char*)oldacl + src);
                acesz = le16_to_cpu(poldace->size);
                src += acesz;
                /*
                 * Currently only ACE for file or directory access are
                 * processed. More information needed about what to do
                 * for other types (whose SID may be at a different location)
                 */
                switch (poldace->type) {
                case ACCESS_ALLOWED_ACE_TYPE :
                case ACCESS_DENIED_ACE_TYPE :
                        acceptable = TRUE;
                        break;
                default :
                        acceptable = FALSE;
                        break;
                }
                /*
                 * Extract inheritance for access, including inheritance for
                 * access from an ACE with is both applied and inheritable.
                 *
                 * must not output OBJECT_INHERIT_ACE or CONTAINER_INHERIT_ACE
                 *
                 *      According to MSDN :
                 * "For a case in which a container object inherits an ACE
                 * "that is both effective on the container and inheritable
                 * "by its descendants, the container may inherit two ACEs.
                 * "This occurs if the inheritable ACE contains generic
                 * "information."
                 */
                if ((poldace->flags & selection)
                    && acceptable
                    && (!fordir
                        || (poldace->flags & NO_PROPAGATE_INHERIT_ACE)
                        || (poldace->mask & (GENERIC_ALL | GENERIC_READ
                                        | GENERIC_WRITE | GENERIC_EXECUTE)))
                    && !ntfs_same_sid(&poldace->sid, ownersid)
                    && !ntfs_same_sid(&poldace->sid, groupsid)) {
                        pnewace = (ACCESS_ALLOWED_ACE*)
                                        ((char*)newacl + dst);
                        memcpy(pnewace,poldace,acesz);
                                /* reencode GENERIC_ALL */
                        if (pnewace->mask & GENERIC_ALL) {
                                pnewace->mask &= ~GENERIC_ALL;
                                if (fordir)
                                        pnewace->mask |= OWNER_RIGHTS
                                                        | DIR_READ
                                                        | DIR_WRITE
                                                        | DIR_EXEC;
                                else
                        /*
                         * The last flag is not defined for a file,
                         * however Windows sets it, so do the same
                         */
                                        pnewace->mask |= OWNER_RIGHTS
                                                        | FILE_READ
                                                        | FILE_WRITE
                                                        | FILE_EXEC
                                                        | const_cpu_to_le32(0x40);
                        }
                                /* reencode GENERIC_READ (+ EXECUTE) */
                        if (pnewace->mask & GENERIC_READ) {
                                if (fordir)
                                        pnewace->mask |= OWNER_RIGHTS
                                                        | DIR_READ
                                                        | DIR_EXEC;
                                else
                                        pnewace->mask |= OWNER_RIGHTS
                                                        | FILE_READ
                                                        | FILE_EXEC;
                                pnewace->mask &= ~(GENERIC_READ
                                                | GENERIC_EXECUTE
                                                | WRITE_DAC
                                                | WRITE_OWNER
                                                | DELETE | FILE_WRITE_EA
                                                | FILE_WRITE_ATTRIBUTES);
                        }
                                /* reencode GENERIC_WRITE */
                        if (pnewace->mask & GENERIC_WRITE) {
                                if (fordir)
                                        pnewace->mask |= OWNER_RIGHTS
                                                        | DIR_WRITE;
                                else
                                        pnewace->mask |= OWNER_RIGHTS
                                                        | FILE_WRITE;
                                pnewace->mask &= ~(GENERIC_WRITE
                                                        | WRITE_DAC
                                                        | WRITE_OWNER
                                                        | FILE_DELETE_CHILD);
                        }
                                /* remove inheritance flags */
                        pnewace->flags &= ~(OBJECT_INHERIT_ACE
                                                | CONTAINER_INHERIT_ACE
                                                | INHERIT_ONLY_ACE);
                        /*
                         * Group similar ACE for authenticated users
                         * (should probably be done for other SIDs)
                         */
                        if ((poldace->type == ACCESS_ALLOWED_ACE_TYPE)
                            && ntfs_same_sid(&poldace->sid, authsid)) {
                                if (pauthace) {
                                        pauthace->flags |= pnewace->flags;
                                        pauthace->mask |= pnewace->mask;
                                } else {
                                        pauthace = pnewace;
                                        if (inherited)
                                                pnewace->flags |= INHERITED_ACE;
                                        dst += acesz;
                                        newcnt++;
                                }
                        } else {
                                if (inherited)
                                        pnewace->flags |= INHERITED_ACE;
                                dst += acesz;
                                newcnt++;
                        }
                }
                        /*
                         * Inheritance for access, specific to
                         * creator-owner (and creator-group)
                         */
                if ((fordir || !inherited
                        || (poldace->flags
                           & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE)))
                    && acceptable) {
                        pnewace = (ACCESS_ALLOWED_ACE*)
                                        ((char*)newacl + dst);
                        memcpy(pnewace,poldace,acesz);
                                /*
                                 * Replace generic creator-owner and
                                 * creator-group by owner and group
                                 * (but keep for further inheritance)
                                 */
                        if (ntfs_same_sid(&pnewace->sid, ownersid)) {
                                memcpy(&pnewace->sid, usid, usidsz);
                                pnewace->size = cpu_to_le16(usidsz + 8);
                                        /* remove inheritance flags */
                                pnewace->flags &= ~(OBJECT_INHERIT_ACE
                                                | CONTAINER_INHERIT_ACE
                                                | INHERIT_ONLY_ACE);
                                if (inherited)
                                        pnewace->flags |= INHERITED_ACE;
                                if ((pnewace->type == ACCESS_ALLOWED_ACE_TYPE)
                                    && pownerace
                                    && !(pnewace->flags & ~pownerace->flags)) {
                                        pownerace->mask |= pnewace->mask;
                                } else {
                                        dst += usidsz + 8;
                                        newcnt++;
                                }
                        }
                        if (ntfs_same_sid(&pnewace->sid, groupsid)) {
                                memcpy(&pnewace->sid, gsid, gsidsz);
                                pnewace->size = cpu_to_le16(gsidsz + 8);
                                        /* remove inheritance flags */
                                pnewace->flags &= ~(OBJECT_INHERIT_ACE
                                                | CONTAINER_INHERIT_ACE
                                                | INHERIT_ONLY_ACE);
                                if (inherited)
                                        pnewace->flags |= INHERITED_ACE;
                                dst += gsidsz + 8;
                                newcnt++;
                        }
                }

                        /*
                         * inheritance for further inheritance
                         *
                         * Situations leading to output CONTAINER_INHERIT_ACE
                         *      or OBJECT_INHERIT_ACE
                         */
                if (fordir
                   && (poldace->flags
                           & (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE))) {
                        pnewace = (ACCESS_ALLOWED_ACE*)
                                        ((char*)newacl + dst);
                        memcpy(pnewace,poldace,acesz);
                        if ((poldace->flags & OBJECT_INHERIT_ACE)
                           && !(poldace->flags & (CONTAINER_INHERIT_ACE
                                        | NO_PROPAGATE_INHERIT_ACE)))
                                pnewace->flags |= INHERIT_ONLY_ACE;
                        if (acceptable
                            && (poldace->flags & CONTAINER_INHERIT_ACE)
                            && !(poldace->flags & NO_PROPAGATE_INHERIT_ACE)
                            && !ntfs_same_sid(&poldace->sid, ownersid)
                            && !ntfs_same_sid(&poldace->sid, groupsid)) {
                                if ((poldace->mask & (GENERIC_ALL | GENERIC_READ
                                        | GENERIC_WRITE | GENERIC_EXECUTE)))
                                        pnewace->flags |= INHERIT_ONLY_ACE;
                                else
                                        pnewace->flags &= ~INHERIT_ONLY_ACE;
                        }
                        if (inherited)
                                pnewace->flags |= INHERITED_ACE;
                        /*
                         * Prepare grouping similar ACE for authenticated users
                         */
                        if ((poldace->type == ACCESS_ALLOWED_ACE_TYPE)
                            && !pauthace
                            && !(pnewace->flags & INHERIT_ONLY_ACE)
                            && ntfs_same_sid(&poldace->sid, authsid)) {
                                pauthace = pnewace;
                        }
                        /*
                         * Prepare grouping similar ACE for owner
                         */
                        if ((poldace->type == ACCESS_ALLOWED_ACE_TYPE)
                            && !pownerace
                            && !(pnewace->flags & INHERIT_ONLY_ACE)
                            && ntfs_same_sid(&poldace->sid, usid)) {
                                pownerace = pnewace;
                        }
                        dst += acesz;
                        newcnt++;
                }
        }
                /*
                 * Adjust header if something was inherited
                 */
        if (dst > sizeof(ACL)) {
                newacl->ace_count = cpu_to_le16(newcnt);
                newacl->size = cpu_to_le16(dst);
        } else
                dst = 0;
        return (dst);
}

#if POSIXACLS

/*
 *              Do sanity checks on a Posix descriptor
 *      Should not be called with a NULL argument
 *      returns TRUE if considered safe
 *              if not, error should be logged by caller
 */

BOOL ntfs_valid_posix(const struct POSIX_SECURITY *pxdesc)
{
        const struct POSIX_ACL *pacl;
        int i;
        BOOL ok;
        u16 tag;
        u32 id;
        int perms;
        struct {
                u16 previous;
                u32 previousid;
                u16 tagsset;
                mode_t mode;
                int owners;
                int groups;
                int others;
        } checks[2], *pchk;

        for (i=0; i<2; i++) {
                checks[i].mode = 0;
                checks[i].tagsset = 0;
                checks[i].owners = 0;
                checks[i].groups = 0;
                checks[i].others = 0;
                checks[i].previous = 0;
                checks[i].previousid = 0;
        }
        ok = TRUE;
        pacl = &pxdesc->acl;
                        /*
                         * header (strict for now)
                         */
        if ((pacl->version != POSIX_VERSION)
            || (pacl->flags != 0)
            || (pacl->filler != 0))
                ok = FALSE;
                        /*
                         * Reject multiple owner, group or other
                         * but do not require them to be present
                         * Also check the ACEs are in correct order
                         * which implies there is no duplicates
                         */
        for (i=0; i<pxdesc->acccnt + pxdesc->defcnt; i++) {
                if (i >= pxdesc->firstdef)
                        pchk = &checks[1];
                else
                        pchk = &checks[0];
                perms = pacl->ace[i].perms;
                tag = pacl->ace[i].tag;
                pchk->tagsset |= tag;
                id = pacl->ace[i].id;
                if (perms & ~7) ok = FALSE;
                if ((tag < pchk->previous)
                        || ((tag == pchk->previous)
                         && (id <= pchk->previousid)))
                                ok = FALSE;
                pchk->previous = tag;
                pchk->previousid = id;
                switch (tag) {
                case POSIX_ACL_USER_OBJ :
                        if (pchk->owners++)
                                ok = FALSE;
                        if (id != (u32)-1)
                                ok = FALSE;
                        pchk->mode |= perms << 6;
                        break;
                case POSIX_ACL_GROUP_OBJ :
                        if (pchk->groups++)
                                ok = FALSE;
                        if (id != (u32)-1)
                                ok = FALSE;
                        pchk->mode = (pchk->mode & 07707) | (perms << 3);
                        break;
                case POSIX_ACL_OTHER :
                        if (pchk->others++)
                                ok = FALSE;
                        if (id != (u32)-1)
                                ok = FALSE;
                        pchk->mode |= perms;
                        break;
                case POSIX_ACL_USER :
                case POSIX_ACL_GROUP :
                        if (id == (u32)-1)
                                ok = FALSE;
                        break;
                case POSIX_ACL_MASK :
                        if (id != (u32)-1)
                                ok = FALSE;
                        pchk->mode = (pchk->mode & 07707) | (perms << 3);
                        break;
                default :
                        ok = FALSE;
                        break;
                }
        }
        if ((pxdesc->acccnt > 0)
           && ((checks[0].owners != 1) || (checks[0].groups != 1) 
                || (checks[0].others != 1)))
                ok = FALSE;
                /* do not check owner, group or other are present in */
                /* the default ACL, Windows does not necessarily set them */
                        /* descriptor */
        if (pxdesc->defcnt && (pxdesc->acccnt > pxdesc->firstdef))
                ok = FALSE;
        if ((pxdesc->acccnt < 0) || (pxdesc->defcnt < 0))
                ok = FALSE;
                        /* check mode, unless null or no tag set */
        if (pxdesc->mode
            && checks[0].tagsset
            && (checks[0].mode != (pxdesc->mode & 0777)))
                ok = FALSE;
                        /* check tagsset */
        if (pxdesc->tagsset != checks[0].tagsset)
                ok = FALSE;
        return (ok);
}

/*
 *              Set standard header data into a Posix ACL
 *      The mode argument should provide the 3 upper bits of target mode
 */

static mode_t posix_header(struct POSIX_SECURITY *pxdesc, mode_t basemode)
{
        mode_t mode;
        u16 tagsset;
        struct POSIX_ACE *pace;
        int i;

        mode = basemode & 07000;
        tagsset = 0;
        for (i=0; i<pxdesc->acccnt; i++) {
                pace = &pxdesc->acl.ace[i];
                tagsset |= pace->tag;
                switch(pace->tag) {
                case POSIX_ACL_USER_OBJ :
                        mode |= (pace->perms & 7) << 6;
                        break;
                case POSIX_ACL_GROUP_OBJ :
                case POSIX_ACL_MASK :
                        mode = (mode & 07707) | ((pace->perms & 7) << 3);
                        break;
                case POSIX_ACL_OTHER :
                        mode |= pace->perms & 7;
                        break;
                default :
                        break;
                }
        }
        pxdesc->tagsset = tagsset;
        pxdesc->mode = mode;
        pxdesc->acl.version = POSIX_VERSION;
        pxdesc->acl.flags = 0;
        pxdesc->acl.filler = 0;
        return (mode);
}

/*
 *              Sort ACEs in a Posix ACL
 *      This is useful for always getting reusable converted ACLs,
 *      it also helps in merging ACEs.
 *      Repeated tag+id are allowed and not merged here.
 *
 *      Tags should be in ascending sequence and for a repeatable tag
 *      ids should be in ascending sequence.
 */

void ntfs_sort_posix(struct POSIX_SECURITY *pxdesc)
{
        struct POSIX_ACL *pacl;
        struct POSIX_ACE ace;
        int i;
        int offs;
        BOOL done;
        u16 tag;
        u16 previous;
        u32 id;
        u32 previousid;


                        /*
                         * Check sequencing of tag+id in access ACE's
                         */
        pacl = &pxdesc->acl;
        do {
                done = TRUE;
                previous = pacl->ace[0].tag;
                previousid = pacl->ace[0].id;
                for (i=1; i<pxdesc->acccnt; i++) {
                        tag = pacl->ace[i].tag;
                        id = pacl->ace[i].id;

                        if ((tag < previous)
                           || ((tag == previous) && (id < previousid))) {
                                done = FALSE;
                                memcpy(&ace,&pacl->ace[i-1],sizeof(struct POSIX_ACE));
                                memcpy(&pacl->ace[i-1],&pacl->ace[i],sizeof(struct POSIX_ACE));
                                memcpy(&pacl->ace[i],&ace,sizeof(struct POSIX_ACE));
                        } else {
                                previous = tag;
                                previousid = id;
                        }
                }
        } while (!done);
                                /*
                                 * Same for default ACEs
                                 */
        do {
                done = TRUE;
                if ((pxdesc->defcnt) > 1) {
                        offs = pxdesc->firstdef;
                        previous = pacl->ace[offs].tag;
                        previousid = pacl->ace[offs].id;
                        for (i=offs+1; i<offs+pxdesc->defcnt; i++) {
                                tag = pacl->ace[i].tag;
                                id = pacl->ace[i].id;

                                if ((tag < previous)
                                   || ((tag == previous) && (id < previousid))) {
                                        done = FALSE;
                                        memcpy(&ace,&pacl->ace[i-1],sizeof(struct POSIX_ACE));
                                        memcpy(&pacl->ace[i-1],&pacl->ace[i],sizeof(struct POSIX_ACE));
                                        memcpy(&pacl->ace[i],&ace,sizeof(struct POSIX_ACE));
                                } else {
                                        previous = tag;
                                        previousid = id;
                                }
                        }
                }
        } while (!done);
}

/*
 *              Merge a new mode into a Posix descriptor
 *      The Posix descriptor is not reallocated, its size is unchanged
 *
 *      returns 0 if ok
 */

int ntfs_merge_mode_posix(struct POSIX_SECURITY *pxdesc, mode_t mode)
{
        int i;
        BOOL maskfound;
        struct POSIX_ACE *pace;
        int todo;

        maskfound = FALSE;
        todo = POSIX_ACL_USER_OBJ | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER;
        for (i=pxdesc->acccnt-1; i>=0; i--) {
                pace = &pxdesc->acl.ace[i];
                switch(pace->tag) {
                case POSIX_ACL_USER_OBJ :
                        pace->perms = (mode >> 6) & 7;
                        todo &= ~POSIX_ACL_USER_OBJ;
                        break;
                case POSIX_ACL_GROUP_OBJ :
                        if (!maskfound)
                                pace->perms = (mode >> 3) & 7;
                        todo &= ~POSIX_ACL_GROUP_OBJ;
                        break;
                case POSIX_ACL_MASK :
                        pace->perms = (mode >> 3) & 7;
                        maskfound = TRUE;
                        break;
                case POSIX_ACL_OTHER :
                        pace->perms = mode & 7;
                        todo &= ~POSIX_ACL_OTHER;
                        break;
                default :
                        break;
                }
        }
        pxdesc->mode = mode;
        return (todo ? -1 : 0);
}

/*
 *              Replace an access or default Posix ACL
 *      The resulting ACL is checked for validity
 *
 *      Returns a new ACL or NULL if there is a problem
 */

struct POSIX_SECURITY *ntfs_replace_acl(const struct POSIX_SECURITY *oldpxdesc,
                const struct POSIX_ACL *newacl, int count, BOOL deflt)
{
        struct POSIX_SECURITY *newpxdesc;
        size_t newsize;
        int offset;
        int oldoffset;
        int i;

        if (deflt)
                newsize = sizeof(struct POSIX_SECURITY)
                        + (oldpxdesc->acccnt + count)*sizeof(struct POSIX_ACE);
        else
                newsize = sizeof(struct POSIX_SECURITY)
                        + (oldpxdesc->defcnt + count)*sizeof(struct POSIX_ACE);
        newpxdesc = (struct POSIX_SECURITY*)malloc(newsize);
        if (newpxdesc) {
                if (deflt) {
                        offset = oldpxdesc->acccnt;
                        newpxdesc->acccnt = oldpxdesc->acccnt;
                        newpxdesc->defcnt = count;
                        newpxdesc->firstdef = offset;
                                        /* copy access ACEs */
                        for (i=0; i<newpxdesc->acccnt; i++)
                                newpxdesc->acl.ace[i] = oldpxdesc->acl.ace[i];
                                        /* copy default ACEs */
                        for (i=0; i<count; i++)
                                newpxdesc->acl.ace[i + offset] = newacl->ace[i];
                } else {
                        offset = count;
                        newpxdesc->acccnt = count;
                        newpxdesc->defcnt = oldpxdesc->defcnt;
                        newpxdesc->firstdef = count;
                                        /* copy access ACEs */
                        for (i=0; i<count; i++)
                                newpxdesc->acl.ace[i] = newacl->ace[i];
                                        /* copy default ACEs */
                        oldoffset = oldpxdesc->firstdef;
                        for (i=0; i<newpxdesc->defcnt; i++)
                                newpxdesc->acl.ace[i + offset] = oldpxdesc->acl.ace[i + oldoffset];
                }
                        /* assume special flags unchanged */
                posix_header(newpxdesc, oldpxdesc->mode);
                if (!ntfs_valid_posix(newpxdesc)) {
                        /* do not log, this is an application error */
                        free(newpxdesc);
                        newpxdesc = (struct POSIX_SECURITY*)NULL;
                        errno = EINVAL;
                }
        } else
                errno = ENOMEM;
        return (newpxdesc);
}

/*
 *              Build a basic Posix ACL from a mode and umask,
 *      ignoring inheritance from the parent directory
 */

struct POSIX_SECURITY *ntfs_build_basic_posix(
                const struct POSIX_SECURITY *pxdesc __attribute__((unused)),
                mode_t mode, mode_t mask, BOOL isdir __attribute__((unused)))
{
        struct POSIX_SECURITY *pydesc;
        struct POSIX_ACE *pyace;

        pydesc = (struct POSIX_SECURITY*)malloc(
                sizeof(struct POSIX_SECURITY) + 3*sizeof(struct POSIX_ACE));
        if (pydesc) {
                pyace = &pydesc->acl.ace[0];
                pyace->tag = POSIX_ACL_USER_OBJ;
                pyace->perms = ((mode & ~mask) >> 6) & 7;
                pyace->id = -1;
                pyace = &pydesc->acl.ace[1];
                pyace->tag = POSIX_ACL_GROUP_OBJ;
                pyace->perms = ((mode & ~mask) >> 3) & 7;
                pyace->id = -1;
                pyace = &pydesc->acl.ace[2];
                pyace->tag = POSIX_ACL_OTHER;
                pyace->perms = (mode & ~mask) & 7;
                pyace->id = -1;
                pydesc->mode = mode;
                pydesc->tagsset = POSIX_ACL_USER_OBJ
                                | POSIX_ACL_GROUP_OBJ
                                | POSIX_ACL_OTHER;
                pydesc->acccnt = 3;
                pydesc->defcnt = 0;
                pydesc->firstdef = 6;
                pydesc->filler = 0;
                pydesc->acl.version = POSIX_VERSION;
                pydesc->acl.flags = 0;
                pydesc->acl.filler = 0;
        } else
                errno = ENOMEM;
        return (pydesc);
}

/*
 *              Build an inherited Posix descriptor from parent
 *      descriptor (if any) restricted to creation mode
 *
 *      Returns the inherited descriptor or NULL if there is a problem
 */

struct POSIX_SECURITY *ntfs_build_inherited_posix(
                const struct POSIX_SECURITY *pxdesc, mode_t mode,
                mode_t mask, BOOL isdir)
{
        struct POSIX_SECURITY *pydesc;
        struct POSIX_ACE *pyace;
        int count;
        int defcnt;
        int size;
        int i;
        s16 tagsset;

        if (pxdesc && pxdesc->defcnt) {
                if (isdir)
                        count = 2*pxdesc->defcnt + 3;
                else
                        count = pxdesc->defcnt + 3;
        } else
                count = 3;
        pydesc = (struct POSIX_SECURITY*)malloc(
                sizeof(struct POSIX_SECURITY) + count*sizeof(struct POSIX_ACE));
        if (pydesc) {
                        /*
                         * Copy inherited tags and adapt perms
                         * Use requested mode, ignoring umask
                         * (not possible with older versions of fuse)
                         */
                tagsset = 0;
                defcnt = (pxdesc ? pxdesc->defcnt : 0);
                for (i=defcnt-1; i>=0; i--) {
                        pyace = &pydesc->acl.ace[i];
                        *pyace = pxdesc->acl.ace[pxdesc->firstdef + i];
                        switch (pyace->tag) {
                        case POSIX_ACL_USER_OBJ :
                                pyace->perms &= (mode >> 6) & 7;
                                break;
                        case POSIX_ACL_GROUP_OBJ :
                                if (!(tagsset & POSIX_ACL_MASK))
                                        pyace->perms &= (mode >> 3) & 7;
                                break;
                        case POSIX_ACL_OTHER :
                                pyace->perms &= mode & 7;
                                break;
                        case POSIX_ACL_MASK :
                                pyace->perms &= (mode >> 3) & 7;
                                break;
                        default :
                                break;
                        }
                        tagsset |= pyace->tag;
                }
                pydesc->acccnt = defcnt;
                /*
                 * If some standard tags were missing, append them from mode
                 * and sort the list
                 * Here we have to use the umask'ed mode
                 */
                if (~tagsset & (POSIX_ACL_USER_OBJ
                                 | POSIX_ACL_GROUP_OBJ | POSIX_ACL_OTHER)) {
                        i = defcnt;
                                /* owner was missing */
                        if (!(tagsset & POSIX_ACL_USER_OBJ)) {
                                pyace = &pydesc->acl.ace[i];
                                pyace->tag = POSIX_ACL_USER_OBJ;
                                pyace->id = -1;
                                pyace->perms = ((mode & ~mask) >> 6) & 7;
                                tagsset |= POSIX_ACL_USER_OBJ;
                                i++;
                        }
                                /* owning group was missing */
                        if (!(tagsset & POSIX_ACL_GROUP_OBJ)) {
                                pyace = &pydesc->acl.ace[i];
                                pyace->tag = POSIX_ACL_GROUP_OBJ;
                                pyace->id = -1;
                                pyace->perms = ((mode & ~mask) >> 3) & 7;
                                tagsset |= POSIX_ACL_GROUP_OBJ;
                                i++;
                        }
                                /* other was missing */
                        if (!(tagsset & POSIX_ACL_OTHER)) {
                                pyace = &pydesc->acl.ace[i];
                                pyace->tag = POSIX_ACL_OTHER;
                                pyace->id = -1;
                                pyace->perms = mode & ~mask & 7;
                                tagsset |= POSIX_ACL_OTHER;
                                i++;
                        }
                        pydesc->acccnt = i;
                        pydesc->firstdef = i;
                        pydesc->defcnt = 0;
                        ntfs_sort_posix(pydesc);
                }

                /*
                 * append as a default ACL if a directory
                 */
                pydesc->firstdef = pydesc->acccnt;
                if (defcnt && isdir) {
                        size = sizeof(struct POSIX_ACE)*defcnt;
                        memcpy(&pydesc->acl.ace[pydesc->firstdef],
                                 &pxdesc->acl.ace[pxdesc->firstdef],size);
                        pydesc->defcnt = defcnt;
                } else {
                        pydesc->defcnt = 0;
                }
                        /* assume special bits are not inherited */
                posix_header(pydesc, mode & 07000);
                if (!ntfs_valid_posix(pydesc)) {
                        ntfs_log_error("Error building an inherited Posix desc\n");
                        errno = EIO;
                        free(pydesc);
                        pydesc = (struct POSIX_SECURITY*)NULL;
                }
        } else
                errno = ENOMEM;
        return (pydesc);
}

static int merge_lists_posix(struct POSIX_ACE *targetace,
                const struct POSIX_ACE *firstace,
                const struct POSIX_ACE *secondace,
                int firstcnt, int secondcnt)
{
        int k;

        k = 0;
                /*
                 * No list is exhausted :
                 *    if same tag+id in both list :
                 *       ignore ACE from second list
                 *    else take the one with smaller tag+id
                 */
        while ((firstcnt > 0) && (secondcnt > 0))
                if ((firstace->tag == secondace->tag)
                    && (firstace->id == secondace->id)) {
                        secondace++;
                        secondcnt--;
                } else
                        if ((firstace->tag < secondace->tag)
                           || ((firstace->tag == secondace->tag)
                                && (firstace->id < secondace->id))) {
                                targetace->tag = firstace->tag;
                                targetace->id = firstace->id;
                                targetace->perms = firstace->perms;
                                firstace++;
                                targetace++;
                                firstcnt--;
                                k++;
                        } else {
                                targetace->tag = secondace->tag;
                                targetace->id = secondace->id;
                                targetace->perms = secondace->perms;
                                secondace++;
                                targetace++;
                                secondcnt--;
                                k++;
                        }
                /*
                 * One list is exhausted, copy the other one
                 */
        while (firstcnt > 0) {
                targetace->tag = firstace->tag;
                targetace->id = firstace->id;
                targetace->perms = firstace->perms;
                firstace++;
                targetace++;
                firstcnt--;
                k++;
        }
        while (secondcnt > 0) {
                targetace->tag = secondace->tag;
                targetace->id = secondace->id;
                targetace->perms = secondace->perms;
                secondace++;
                targetace++;
                secondcnt--;
                k++;
        }
        return (k);
}

/*
 *              Merge two Posix ACLs
 *      The input ACLs have to be adequately sorted
 *
 *      Returns the merged ACL, which is allocated and has to be freed by caller,
 *      or NULL if failed
 */

struct POSIX_SECURITY *ntfs_merge_descr_posix(const struct POSIX_SECURITY *first,
                        const struct POSIX_SECURITY *second)
{
        struct POSIX_SECURITY *pxdesc;
        struct POSIX_ACE *targetace;
        const struct POSIX_ACE *firstace;
        const struct POSIX_ACE *secondace;
        size_t size;
        int k;

        size = sizeof(struct POSIX_SECURITY)
                + (first->acccnt + first->defcnt
                        + second->acccnt + second->defcnt)*sizeof(struct POSIX_ACE);
        pxdesc = (struct POSIX_SECURITY*)malloc(size);
        if (pxdesc) {
                        /*
                         * merge access ACEs
                         */
                firstace = first->acl.ace;
                secondace = second->acl.ace;
                targetace = pxdesc->acl.ace;
                k = merge_lists_posix(targetace,firstace,secondace,
                        first->acccnt,second->acccnt);
                pxdesc->acccnt = k;
                        /*
                         * merge default ACEs
                         */
                pxdesc->firstdef = k;
                firstace = &first->acl.ace[first->firstdef];
                secondace = &second->acl.ace[second->firstdef];
                targetace = &pxdesc->acl.ace[k];
                k = merge_lists_posix(targetace,firstace,secondace,
                        first->defcnt,second->defcnt);
                pxdesc->defcnt = k;
                        /*
                         * build header
                         */
                pxdesc->acl.version = POSIX_VERSION;
                pxdesc->acl.flags = 0;
                pxdesc->acl.filler = 0;
                pxdesc->mode = 0;
                pxdesc->tagsset = 0;
        } else
                errno = ENOMEM;
        return (pxdesc);
}

struct BUILD_CONTEXT {
        BOOL isdir;
        BOOL adminowns;
        BOOL groupowns;
        u16 selfuserperms;
        u16 selfgrpperms;
        u16 grpperms;
        u16 othperms;
        u16 mask;
        u16 designates;
        u16 withmask;
        u16 rootspecial;
} ;



static BOOL build_user_denials(ACL *pacl,
                const SID *usid, struct MAPPING* const mapping[],
                ACE_FLAGS flags, const struct POSIX_ACE *pxace,
                struct BUILD_CONTEXT *pset)
{
        BIGSID defsid;
        ACCESS_ALLOWED_ACE *pdace;
        const SID *sid;
        int sidsz;
        int pos;
        int acecnt;
        le32 grants;
        le32 denials;
        u16 perms;
        u16 mixperms;
        u16 tag;
        BOOL rejected;
        BOOL rootuser;
        BOOL avoidmask;

        rejected = FALSE;
        tag = pxace->tag;
        perms = pxace->perms;
        rootuser = FALSE;
        pos = le16_to_cpu(pacl->size);
        acecnt = le16_to_cpu(pacl->ace_count);
        avoidmask = (pset->mask == (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X))
                        && ((pset->designates && pset->withmask)
                           || (!pset->designates && !pset->withmask));
        if (tag == POSIX_ACL_USER_OBJ) {
                sid = usid;
                sidsz = ntfs_sid_size(sid);
                grants = OWNER_RIGHTS;
        } else {
                if (pxace->id) {
                        sid = ntfs_find_usid(mapping[MAPUSERS],
                                pxace->id, (SID*)&defsid);
                        grants = WORLD_RIGHTS;
                } else {
                        sid = adminsid;
                        rootuser = TRUE;
                        grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK;
                }
                if (sid) {
                        sidsz = ntfs_sid_size(sid);
                        /*
                         * Insert denial of complement of mask for
                         * each designated user (except root)
                         * WRITE_OWNER is inserted so that
                         * the mask can be identified
                         */
                        if (!avoidmask && !rootuser) {
                                denials = WRITE_OWNER;
                                pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos];
                                if (pset->isdir) {
                                        if (!(pset->mask & POSIX_PERM_X))
                                                denials |= DIR_EXEC;
                                        if (!(pset->mask & POSIX_PERM_W))
                                                denials |= DIR_WRITE;
                                        if (!(pset->mask & POSIX_PERM_R))
                                                denials |= DIR_READ;
                                } else {
                                        if (!(pset->mask & POSIX_PERM_X))
                                                denials |= FILE_EXEC;
                                        if (!(pset->mask & POSIX_PERM_W))
                                                denials |= FILE_WRITE;
                                        if (!(pset->mask & POSIX_PERM_R))
                                                denials |= FILE_READ;
                                }
                                if (rootuser)
                                        grants &= ~ROOT_OWNER_UNMARK;
                                pdace->type = ACCESS_DENIED_ACE_TYPE;
                                pdace->flags = flags;
                                pdace->size = cpu_to_le16(sidsz + 8);
                                pdace->mask = denials;
                                memcpy((char*)&pdace->sid, sid, sidsz);
                                pos += sidsz + 8;
                                acecnt++;
                        }
                } else
                        rejected = TRUE;
        }
        if (!rejected) {
                if (pset->isdir) {
                        if (perms & POSIX_PERM_X)
                                grants |= DIR_EXEC;
                        if (perms & POSIX_PERM_W)
                                grants |= DIR_WRITE;
                        if (perms & POSIX_PERM_R)
                                grants |= DIR_READ;
                } else {
                        if (perms & POSIX_PERM_X)
                                grants |= FILE_EXEC;
                        if (perms & POSIX_PERM_W)
                                grants |= FILE_WRITE;
                        if (perms & POSIX_PERM_R)
                                grants |= FILE_READ;
                }

                /* a possible ACE to deny owner what he/she would */
                /* induely get from administrator, group or world */
                /* unless owner is administrator or group */

                denials = const_cpu_to_le32(0);
                pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos];
                if (!pset->adminowns && !rootuser) {
                        if (!pset->groupowns) {
                                mixperms = pset->grpperms | pset->othperms;
                                if (tag == POSIX_ACL_USER_OBJ)
                                        mixperms |= pset->selfuserperms;
                                if (pset->isdir) {
                                        if (mixperms & POSIX_PERM_X)
                                                denials |= DIR_EXEC;
                                        if (mixperms & POSIX_PERM_W)
                                                denials |= DIR_WRITE;
                                        if (mixperms & POSIX_PERM_R)
                                                denials |= DIR_READ;
                                } else {
                                        if (mixperms & POSIX_PERM_X)
                                                denials |= FILE_EXEC;
                                        if (mixperms & POSIX_PERM_W)
                                                denials |= FILE_WRITE;
                                        if (mixperms & POSIX_PERM_R)
                                                denials |= FILE_READ;
                                }
                        } else {
                                mixperms = ~pset->grpperms & pset->othperms;
                                if (tag == POSIX_ACL_USER_OBJ)
                                        mixperms |= pset->selfuserperms;
                                if (pset->isdir) {
                                        if (mixperms & POSIX_PERM_X)
                                                denials |= DIR_EXEC;
                                        if (mixperms & POSIX_PERM_W)
                                                denials |= DIR_WRITE;
                                        if (mixperms & POSIX_PERM_R)
                                                denials |= DIR_READ;
                                } else {
                                        if (mixperms & POSIX_PERM_X)
                                                denials |= FILE_EXEC;
                                        if (mixperms & POSIX_PERM_W)
                                                denials |= FILE_WRITE;
                                        if (mixperms & POSIX_PERM_R)
                                                denials |= FILE_READ;
                                }
                        }
                        denials &= ~grants;
                        if (denials) {
                                pdace->type = ACCESS_DENIED_ACE_TYPE;
                                pdace->flags = flags;
                                pdace->size = cpu_to_le16(sidsz + 8);
                                pdace->mask = denials;
                                memcpy((char*)&pdace->sid, sid, sidsz);
                                pos += sidsz + 8;
                                acecnt++;
                        }
                }
        }
        pacl->size = cpu_to_le16(pos);
        pacl->ace_count = cpu_to_le16(acecnt);
        return (!rejected);
}

static BOOL build_user_grants(ACL *pacl,
                const SID *usid, struct MAPPING* const mapping[],
                ACE_FLAGS flags, const struct POSIX_ACE *pxace,
                struct BUILD_CONTEXT *pset)
{
        BIGSID defsid;
        ACCESS_ALLOWED_ACE *pgace;
        const SID *sid;
        int sidsz;
        int pos;
        int acecnt;
        le32 grants;
        u16 perms;
        u16 tag;
        BOOL rejected;
        BOOL rootuser;

        rejected = FALSE;
        tag = pxace->tag;
        perms = pxace->perms;
        rootuser = FALSE;
        pos = le16_to_cpu(pacl->size);
        acecnt = le16_to_cpu(pacl->ace_count);
        if (tag == POSIX_ACL_USER_OBJ) {
                sid = usid;
                sidsz = ntfs_sid_size(sid);
                grants = OWNER_RIGHTS;
        } else {
                if (pxace->id) {
                        sid = ntfs_find_usid(mapping[MAPUSERS],
                                pxace->id, (SID*)&defsid);
                        if (sid)
                                sidsz = ntfs_sid_size(sid);
                        else
                                rejected = TRUE;
                        grants = WORLD_RIGHTS;
                } else {
                        sid = adminsid;
                        sidsz = ntfs_sid_size(sid);
                        rootuser = TRUE;
                        grants = WORLD_RIGHTS & ~ROOT_OWNER_UNMARK;
                }
        }
        if (!rejected) {
                if (pset->isdir) {
                        if (perms & POSIX_PERM_X)
                                grants |= DIR_EXEC;
                        if (perms & POSIX_PERM_W)
                                grants |= DIR_WRITE;
                        if (perms & POSIX_PERM_R)
                                grants |= DIR_READ;
                } else {
                        if (perms & POSIX_PERM_X)
                                grants |= FILE_EXEC;
                        if (perms & POSIX_PERM_W)
                                grants |= FILE_WRITE;
                        if (perms & POSIX_PERM_R)
                                grants |= FILE_READ;
                }
                if (rootuser)
                        grants &= ~ROOT_OWNER_UNMARK;
                pgace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos];
                pgace->type = ACCESS_ALLOWED_ACE_TYPE;
                pgace->size = cpu_to_le16(sidsz + 8);
                pgace->flags = flags;
                pgace->mask = grants;
                memcpy((char*)&pgace->sid, sid, sidsz);
                pos += sidsz + 8;
                acecnt = le16_to_cpu(pacl->ace_count) + 1;
                pacl->ace_count = cpu_to_le16(acecnt);
                pacl->size = cpu_to_le16(pos);
        }
        return (!rejected);
}


                        /* a grant ACE for group */
                        /* unless group-obj has the same rights as world */
                        /* but present if group is owner or owner is administrator */
                        /* this ACE will be inserted after denials for group */

static BOOL build_group_denials_grant(ACL *pacl,
                const SID *gsid, struct MAPPING* const mapping[],
                ACE_FLAGS flags, const struct POSIX_ACE *pxace,
                struct BUILD_CONTEXT *pset)
{
        BIGSID defsid;
        ACCESS_ALLOWED_ACE *pdace;
        ACCESS_ALLOWED_ACE *pgace;
        const SID *sid;
        int sidsz;
        int pos;
        int acecnt;
        le32 grants;
        le32 denials;
        u16 perms;
        u16 mixperms;
        u16 tag;
        BOOL avoidmask;
        BOOL rootgroup;
        BOOL rejected;

        rejected = FALSE;
        tag = pxace->tag;
        perms = pxace->perms;
        pos = le16_to_cpu(pacl->size);
        acecnt = le16_to_cpu(pacl->ace_count);
        rootgroup = FALSE;
        avoidmask = (pset->mask == (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X))
                        && ((pset->designates && pset->withmask)
                           || (!pset->designates && !pset->withmask));
        if (tag == POSIX_ACL_GROUP_OBJ)
                sid = gsid;
        else
                if (pxace->id)
                        sid = ntfs_find_gsid(mapping[MAPGROUPS],
                                pxace->id, (SID*)&defsid);
                else {
                        sid = adminsid;
                        rootgroup = TRUE;
                }
        if (sid) {
                sidsz = ntfs_sid_size(sid);
                /*
                 * Insert denial of complement of mask for
                 * each group
                 * WRITE_OWNER is inserted so that
                 * the mask can be identified
                 * Note : this mask may lead on Windows to
                 * deny rights to administrators belonging
                 * to some user group
                 */
                if ((!avoidmask && !rootgroup)
                    || (pset->rootspecial
                        && (tag == POSIX_ACL_GROUP_OBJ))) {
                        denials = WRITE_OWNER;
                        pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos];
                        if (pset->isdir) {
                                if (!(pset->mask & POSIX_PERM_X))
                                        denials |= DIR_EXEC;
                                if (!(pset->mask & POSIX_PERM_W))
                                        denials |= DIR_WRITE;
                                if (!(pset->mask & POSIX_PERM_R))
                                        denials |= DIR_READ;
                        } else {
                                if (!(pset->mask & POSIX_PERM_X))
                                        denials |= FILE_EXEC;
                                if (!(pset->mask & POSIX_PERM_W))
                                        denials |= FILE_WRITE;
                                if (!(pset->mask & POSIX_PERM_R))
                                        denials |= FILE_READ;
                        }
                        pdace->type = ACCESS_DENIED_ACE_TYPE;
                        pdace->flags = flags;
                        pdace->size = cpu_to_le16(sidsz + 8);
                        pdace->mask = denials;
                        memcpy((char*)&pdace->sid, sid, sidsz);
                        pos += sidsz + 8;
                        acecnt++;
                }
        } else
                rejected = TRUE;
        if (!rejected
            && (pset->adminowns
                || pset->groupowns
                || avoidmask
                || rootgroup
                || (perms != pset->othperms))) {
                grants = WORLD_RIGHTS;
                if (rootgroup)
                        grants &= ~ROOT_GROUP_UNMARK;
                if (pset->isdir) {
                        if (perms & POSIX_PERM_X)
                                grants |= DIR_EXEC;
                        if (perms & POSIX_PERM_W)
                                grants |= DIR_WRITE;
                        if (perms & POSIX_PERM_R)
                                grants |= DIR_READ;
                } else {
                        if (perms & POSIX_PERM_X)
                                grants |= FILE_EXEC;
                        if (perms & POSIX_PERM_W)
                                grants |= FILE_WRITE;
                        if (perms & POSIX_PERM_R)
                                grants |= FILE_READ;
                }

                /* a possible ACE to deny group what it would get from world */
                /* or administrator, unless owner is administrator or group */

                denials = const_cpu_to_le32(0);
                pdace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos];
                if (!pset->adminowns
                    && !pset->groupowns
                    && !rootgroup) {
                        mixperms = pset->othperms;
                        if (tag == POSIX_ACL_GROUP_OBJ)
                                mixperms |= pset->selfgrpperms;
                        if (pset->isdir) {
                                if (mixperms & POSIX_PERM_X)
                                        denials |= DIR_EXEC;
                                if (mixperms & POSIX_PERM_W)
                                        denials |= DIR_WRITE;
                                if (mixperms & POSIX_PERM_R)
                                        denials |= DIR_READ;
                        } else {
                                if (mixperms & POSIX_PERM_X)
                                        denials |= FILE_EXEC;
                                if (mixperms & POSIX_PERM_W)
                                        denials |= FILE_WRITE;
                                if (mixperms & POSIX_PERM_R)
                                        denials |= FILE_READ;
                        }
                        denials &= ~(grants | OWNER_RIGHTS);
                        if (denials) {
                                pdace->type = ACCESS_DENIED_ACE_TYPE;
                                pdace->flags = flags;
                                pdace->size = cpu_to_le16(sidsz + 8);
                                pdace->mask = denials;
                                memcpy((char*)&pdace->sid, sid, sidsz);
                                pos += sidsz + 8;
                                acecnt++;
                        }
                }

                        /* now insert grants to group if more than world */
                if (pset->adminowns
                        || pset->groupowns
                        || (avoidmask && (pset->designates || pset->withmask))
                        || (perms & ~pset->othperms)
                        || (pset->rootspecial
                           && (tag == POSIX_ACL_GROUP_OBJ))
                        || (tag == POSIX_ACL_GROUP)) {
                        if (rootgroup)
                                grants &= ~ROOT_GROUP_UNMARK;
                        pgace = (ACCESS_DENIED_ACE*)&((char*)pacl)[pos];
                        pgace->type = ACCESS_ALLOWED_ACE_TYPE;
                        pgace->flags = flags;
                        pgace->size = cpu_to_le16(sidsz + 8);
                        pgace->mask = grants;
                        memcpy((char*)&pgace->sid, sid, sidsz);
                        pos += sidsz + 8;
                        acecnt++;
                }
        }
        pacl->size = cpu_to_le16(pos);
        pacl->ace_count = cpu_to_le16(acecnt);
        return (!rejected);
}


/*
 *              Build an ACL composed of several ACE's
 *      returns size of ACL or zero if failed
 *
 *      Three schemes are defined :
 *
 *      1) if root is neither owner nor group up to 7 ACE's are set up :
 *      - denials to owner (preventing grants to world or group to apply)
 *        + mask denials to designated user (unless mask allows all)
 *        + denials to designated user
 *      - grants to owner (always present - first grant)
 *        + grants to designated user
 *        + mask denial to group (unless mask allows all)
 *      - denials to group (preventing grants to world to apply) 
 *      - grants to group (unless group has no more than world rights)
 *        + mask denials to designated group (unless mask allows all)
 *        + grants to designated group
 *        + denials to designated group
 *      - grants to world (unless none)
 *      - full privileges to administrator, always present
 *      - full privileges to system, always present
 *
 *      The same scheme is applied for Posix ACLs, with the mask represented
 *      as denials prepended to grants for designated users and groups
 *
 *      This is inspired by an Internet Draft from Marius Aamodt Eriksen
 *      for mapping NFSv4 ACLs to Posix ACLs (draft-ietf-nfsv4-acl-mapping-00.txt)
 *      More recent versions of the draft (draft-ietf-nfsv4-acl-mapping-05.txt)
 *      are not followed, as they ignore the Posix mask and lead to
 *      loss of compatibility with Linux implementations on other fs.
 *
 *      Note that denials to group are located after grants to owner.
 *      This only occurs in the unfrequent situation where world
 *      has more rights than group and cannot be avoided if owner and other
 *      have some common right which is denied to group (eg for mode 745
 *      executing has to be denied to group, but not to owner or world).
 *      This rare situation is processed by Windows correctly, but
 *      Windows utilities may want to change the order, with a
 *      consequence of applying the group denials to the Windows owner.
 *      The interpretation on Linux is not affected by the order change.
 *
 *      2) if root is either owner or group, two problems arise :
 *      - granting full rights to administrator (as needed to transpose
 *        to Windows rights bypassing granting to root) would imply
 *        Linux permissions to always be seen as rwx, no matter the chmod
 *      - there is no different SID to separate an administrator owner
 *        from an administrator group. Hence Linux permissions for owner
 *        would always be similar to permissions to group.
 *
 *      as a work-around, up to 5 ACE's are set up if owner or group :
 *      - grants to owner, always present at first position
 *      - grants to group, always present
 *      - grants to world, unless none
 *      - full privileges to administrator, always present
 *      - full privileges to system, always present
 *
 *      On Windows, these ACE's are processed normally, though they
 *      are redundant (owner, group and administrator are the same,
 *      as a consequence any denials would damage administrator rights)
 *      but on Linux, privileges to administrator are ignored (they
 *      are not needed as root has always full privileges), and
 *      neither grants to group are applied to owner, nor grants to
 *      world are applied to owner or group.
 *
 *      3) finally a similar situation arises when group is owner (they
 *       have the same SID), but is not root.
 *       In this situation up to 6 ACE's are set up :
 *
 *      - denials to owner (preventing grants to world to apply)
 *      - grants to owner (always present)
 *      - grants to group (unless groups has same rights as world)
 *      - grants to world (unless none)
 *      - full privileges to administrator, always present
 *      - full privileges to system, always present
 *
 *      On Windows, these ACE's are processed normally, though they
 *      are redundant (as owner and group are the same), but this has
 *      no impact on administrator rights
 *
 *      Special flags (S_ISVTX, S_ISGID, S_ISUID) :
 *      an extra null ACE is inserted to hold these flags, using
 *      the same conventions as cygwin.
 *
 */

static int buildacls_posix(struct MAPPING* const mapping[],
                char *secattr, int offs, const struct POSIX_SECURITY *pxdesc,
                int isdir, const SID *usid, const SID *gsid)
{
        struct BUILD_CONTEXT aceset[2], *pset;
        BOOL adminowns;
        BOOL groupowns;
        ACL *pacl;
        ACCESS_ALLOWED_ACE *pgace;
        ACCESS_ALLOWED_ACE *pdace;
        const struct POSIX_ACE *pxace;
        BOOL ok;
        mode_t mode;
        u16 tag;
        u16 perms;
        ACE_FLAGS flags;
        int pos;
        int i;
        int k;
        BIGSID defsid;
        const SID *sid;
        int acecnt;
        int usidsz;
        int wsidsz;
        int asidsz;
        int ssidsz;
        int nsidsz;
        le32 grants;

        usidsz = ntfs_sid_size(usid);
        wsidsz = ntfs_sid_size(worldsid);
        asidsz = ntfs_sid_size(adminsid);
        ssidsz = ntfs_sid_size(systemsid);
        mode = pxdesc->mode;
                /* adminowns and groupowns are used for both lists */
        adminowns = ntfs_same_sid(usid, adminsid)
                 || ntfs_same_sid(gsid, adminsid);
        groupowns = !adminowns && ntfs_same_sid(usid, gsid);

        ok = TRUE;

        /* ACL header */
        pacl = (ACL*)&secattr[offs];
        pacl->revision = ACL_REVISION;
        pacl->alignment1 = 0;
        pacl->size = cpu_to_le16(sizeof(ACL) + usidsz + 8);
        pacl->ace_count = const_cpu_to_le16(0);
        pacl->alignment2 = const_cpu_to_le16(0);

                /*
                 * Determine what is allowed to some group or world
                 * to prevent designated users or other groups to get
                 * rights from groups or world
                 * Do the same if owner and group appear as designated
                 * user or group
                 * Also get global mask
                 */
        for (k=0; k<2; k++) {
                pset = &aceset[k];
                pset->selfuserperms = 0;
                pset->selfgrpperms = 0;
                pset->grpperms = 0;
                pset->othperms = 0;
                pset->mask = (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X);
                pset->designates = 0;
                pset->withmask = 0;
                pset->rootspecial = 0;
                pset->adminowns = adminowns;
                pset->groupowns = groupowns;
                pset->isdir = isdir;
        }

        for (i=pxdesc->acccnt+pxdesc->defcnt-1; i>=0; i--) {
                if (i >= pxdesc->acccnt) {
                        pset = &aceset[1];
                        pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt];
                } else {
                        pset = &aceset[0];
                        pxace = &pxdesc->acl.ace[i];
                }
                switch (pxace->tag) {
                case POSIX_ACL_USER :
                        pset->designates++;
                        if (pxace->id) {
                                sid = ntfs_find_usid(mapping[MAPUSERS],
                                        pxace->id, (SID*)&defsid);
                                if (sid && ntfs_same_sid(sid,usid))
                                        pset->selfuserperms |= pxace->perms;
                        } else
                                /* root as designated user is processed apart */
                                pset->rootspecial = TRUE;
                        break;
                case POSIX_ACL_GROUP :
                        pset->designates++;
                        if (pxace->id) {
                                sid = ntfs_find_gsid(mapping[MAPUSERS],
                                        pxace->id, (SID*)&defsid);
                                if (sid && ntfs_same_sid(sid,gsid))
                                        pset->selfgrpperms |= pxace->perms;
                        } else
                                /* root as designated group is processed apart */
                                pset->rootspecial = TRUE;
                        /* fall through */
                case POSIX_ACL_GROUP_OBJ :
                        pset->grpperms |= pxace->perms;
                        break;
                case POSIX_ACL_OTHER :
                        pset->othperms = pxace->perms;
                        break;
                case POSIX_ACL_MASK :
                        pset->withmask++;
                        pset->mask = pxace->perms;
                default :
                        break;
                }
        }

if (pxdesc->defcnt && (pxdesc->firstdef != pxdesc->acccnt)) {
ntfs_log_error("** error : access and default not consecutive\n");
return (0);
}
                        /*
                         * First insert all denials for owner and each
                         * designated user (with mask if needed)
                         */

        pacl->ace_count = const_cpu_to_le16(0);
        pacl->size = const_cpu_to_le16(sizeof(ACL));
        for (i=0; (i<(pxdesc->acccnt + pxdesc->defcnt)) && ok; i++) {
                if (i >= pxdesc->acccnt) {
                        flags = INHERIT_ONLY_ACE
                                | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
                        pset = &aceset[1];
                        pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt];
                } else {
                        if (pxdesc->defcnt)
                                flags = NO_PROPAGATE_INHERIT_ACE;
                        else
                                flags = (isdir ? DIR_INHERITANCE
                                           : FILE_INHERITANCE);
                        pset = &aceset[0];
                        pxace = &pxdesc->acl.ace[i];
                }
                tag = pxace->tag;
                perms = pxace->perms;
                switch (tag) {

                        /* insert denial ACEs for each owner or allowed user */

                case POSIX_ACL_USER :
                case POSIX_ACL_USER_OBJ :

                        ok = build_user_denials(pacl,
                                usid, mapping, flags, pxace, pset);
                        break;
                default :
                        break;
                }
        }

                /*
                 * for directories, insert a world execution denial
                 * inherited to plain files.
                 * This is to prevent Windows from granting execution
                 * of files through inheritance from parent directory
                 */

        if (isdir && ok) {
                pos = le16_to_cpu(pacl->size);
                pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos];
                pdace->type = ACCESS_DENIED_ACE_TYPE;
                pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE;
                pdace->size = cpu_to_le16(wsidsz + 8);
                pdace->mask = FILE_EXEC;
                memcpy((char*)&pdace->sid, worldsid, wsidsz);
                pos += wsidsz + 8;
                acecnt = le16_to_cpu(pacl->ace_count) + 1;
                pacl->ace_count = cpu_to_le16(acecnt);
                pacl->size = cpu_to_le16(pos);
        }

                        /*
                         * now insert (if needed)
                         * - grants to owner and designated users
                         * - mask and denials for all groups
                         * - grants to other
                         */

        for (i=0; (i<(pxdesc->acccnt + pxdesc->defcnt)) && ok; i++) {
                if (i >= pxdesc->acccnt) {
                        flags = INHERIT_ONLY_ACE
                                | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE;
                        pset = &aceset[1];
                        pxace = &pxdesc->acl.ace[i + pxdesc->firstdef - pxdesc->acccnt];
                } else {
                        if (pxdesc->defcnt)
                                flags = NO_PROPAGATE_INHERIT_ACE;
                        else
                                flags = (isdir ? DIR_INHERITANCE
                                           : FILE_INHERITANCE);
                        pset = &aceset[0];
                        pxace = &pxdesc->acl.ace[i];
                }
                tag = pxace->tag;
                perms = pxace->perms;
                switch (tag) {

                        /* ACE for each owner or allowed user */

                case POSIX_ACL_USER :
                case POSIX_ACL_USER_OBJ :
                        ok = build_user_grants(pacl,usid,
                                        mapping,flags,pxace,pset);
                        break;

                case POSIX_ACL_GROUP_OBJ :
                        /* denials and grants for group when needed */
                        if (pset->groupowns && !pset->adminowns
                            && (pset->grpperms == pset->othperms)
                            && !pset->designates && !pset->withmask) {
                                ok = TRUE;
                        } else {
                                ok = build_group_denials_grant(pacl,gsid,
                                                mapping,flags,pxace,pset);
                        }
                        break;

                case POSIX_ACL_GROUP :

                        /* denials and grants for designated groups */

                        ok = build_group_denials_grant(pacl,gsid,
                                        mapping,flags,pxace,pset);
                        break;

                case POSIX_ACL_OTHER :

                        /* grants for other users */

                        pos = le16_to_cpu(pacl->size);
                        pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
                        grants = WORLD_RIGHTS;
                        if (isdir) {
                                if (perms & POSIX_PERM_X)
                                        grants |= DIR_EXEC;
                                if (perms & POSIX_PERM_W)
                                        grants |= DIR_WRITE;
                                if (perms & POSIX_PERM_R)
                                        grants |= DIR_READ;
                        } else {
                                if (perms & POSIX_PERM_X)
                                        grants |= FILE_EXEC;
                                if (perms & POSIX_PERM_W)
                                        grants |= FILE_WRITE;
                                if (perms & POSIX_PERM_R)
                                        grants |= FILE_READ;
                        }
                        pgace->type = ACCESS_ALLOWED_ACE_TYPE;
                        pgace->flags = flags;
                        pgace->size = cpu_to_le16(wsidsz + 8);
                        pgace->mask = grants;
                        memcpy((char*)&pgace->sid, worldsid, wsidsz);
                        pos += wsidsz + 8;
                        acecnt = le16_to_cpu(pacl->ace_count) + 1;
                        pacl->ace_count = cpu_to_le16(acecnt);
                        pacl->size = cpu_to_le16(pos);
                        break;
                }
        }

        if (!ok) {
                errno = EINVAL;
                pos = 0;
        } else {
                /* an ACE for administrators */
                /* always full access */

                pos = le16_to_cpu(pacl->size);
                acecnt = le16_to_cpu(pacl->ace_count);
                if (isdir)
                        flags = OBJECT_INHERIT_ACE
                                | CONTAINER_INHERIT_ACE;
                else
                        flags = NO_PROPAGATE_INHERIT_ACE;
                pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
                pgace->type = ACCESS_ALLOWED_ACE_TYPE;
                pgace->flags = flags;
                pgace->size = cpu_to_le16(asidsz + 8);
                grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC;
                pgace->mask = grants;
                memcpy((char*)&pgace->sid, adminsid, asidsz);
                pos += asidsz + 8;
                acecnt++;

                /* an ACE for system (needed ?) */
                /* always full access */

                pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
                pgace->type = ACCESS_ALLOWED_ACE_TYPE;
                pgace->flags = flags;
                pgace->size = cpu_to_le16(ssidsz + 8);
                grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC;
                pgace->mask = grants;
                memcpy((char*)&pgace->sid, systemsid, ssidsz);
                pos += ssidsz + 8;
                acecnt++;

                /* a null ACE to hold special flags */
                /* using the same representation as cygwin */

                if (mode & (S_ISVTX | S_ISGID | S_ISUID)) {
                        nsidsz = ntfs_sid_size(nullsid);
                        pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
                        pgace->type = ACCESS_ALLOWED_ACE_TYPE;
                        pgace->flags = NO_PROPAGATE_INHERIT_ACE;
                        pgace->size = cpu_to_le16(nsidsz + 8);
                        grants = const_cpu_to_le32(0);
                        if (mode & S_ISUID)
                                grants |= FILE_APPEND_DATA;
                        if (mode & S_ISGID)
                                grants |= FILE_WRITE_DATA;
                        if (mode & S_ISVTX)
                                grants |= FILE_READ_DATA;
                        pgace->mask = grants;
                        memcpy((char*)&pgace->sid, nullsid, nsidsz);
                        pos += nsidsz + 8;
                        acecnt++;
                }

                /* fix ACL header */
                pacl->size = cpu_to_le16(pos);
                pacl->ace_count = cpu_to_le16(acecnt);
        }
        return (ok ? pos : 0);
}

#endif /* POSIXACLS */

static int buildacls(char *secattr, int offs, mode_t mode, int isdir,
               const SID * usid, const SID * gsid)
{
        ACL *pacl;
        ACCESS_ALLOWED_ACE *pgace;
        ACCESS_ALLOWED_ACE *pdace;
        BOOL adminowns;
        BOOL groupowns;
        ACE_FLAGS gflags;
        int pos;
        int acecnt;
        int usidsz;
        int gsidsz;
        int wsidsz;
        int asidsz;
        int ssidsz;
        int nsidsz;
        le32 grants;
        le32 denials;

        usidsz = ntfs_sid_size(usid);
        gsidsz = ntfs_sid_size(gsid);
        wsidsz = ntfs_sid_size(worldsid);
        asidsz = ntfs_sid_size(adminsid);
        ssidsz = ntfs_sid_size(systemsid);
        adminowns = ntfs_same_sid(usid, adminsid)
                 || ntfs_same_sid(gsid, adminsid);
        groupowns = !adminowns && ntfs_same_sid(usid, gsid);

        /* ACL header */
        pacl = (ACL*)&secattr[offs];
        pacl->revision = ACL_REVISION;
        pacl->alignment1 = 0;
        pacl->size = cpu_to_le16(sizeof(ACL) + usidsz + 8);
        pacl->ace_count = const_cpu_to_le16(1);
        pacl->alignment2 = const_cpu_to_le16(0);
        pos = sizeof(ACL);
        acecnt = 0;

        /* compute a grant ACE for owner */
        /* this ACE will be inserted after denial for owner */

        grants = OWNER_RIGHTS;
        if (isdir) {
                gflags = DIR_INHERITANCE;
                if (mode & S_IXUSR)
                        grants |= DIR_EXEC;
                if (mode & S_IWUSR)
                        grants |= DIR_WRITE;
                if (mode & S_IRUSR)
                        grants |= DIR_READ;
        } else {
                gflags = FILE_INHERITANCE;
                if (mode & S_IXUSR)
                        grants |= FILE_EXEC;
                if (mode & S_IWUSR)
                        grants |= FILE_WRITE;
                if (mode & S_IRUSR)
                        grants |= FILE_READ;
        }

        /* a possible ACE to deny owner what he/she would */
        /* induely get from administrator, group or world */
        /* unless owner is administrator or group */

        denials = const_cpu_to_le32(0);
        pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos];
        if (!adminowns) {
                if (!groupowns) {
                        if (isdir) {
                                pdace->flags = DIR_INHERITANCE;
                                if (mode & (S_IXGRP | S_IXOTH))
                                        denials |= DIR_EXEC;
                                if (mode & (S_IWGRP | S_IWOTH))
                                        denials |= DIR_WRITE;
                                if (mode & (S_IRGRP | S_IROTH))
                                        denials |= DIR_READ;
                        } else {
                                pdace->flags = FILE_INHERITANCE;
                                if (mode & (S_IXGRP | S_IXOTH))
                                        denials |= FILE_EXEC;
                                if (mode & (S_IWGRP | S_IWOTH))
                                        denials |= FILE_WRITE;
                                if (mode & (S_IRGRP | S_IROTH))
                                        denials |= FILE_READ;
                        }
                } else {
                        if (isdir) {
                                pdace->flags = DIR_INHERITANCE;
                                if ((mode & S_IXOTH) && !(mode & S_IXGRP))
                                        denials |= DIR_EXEC;
                                if ((mode & S_IWOTH) && !(mode & S_IWGRP))
                                        denials |= DIR_WRITE;
                                if ((mode & S_IROTH) && !(mode & S_IRGRP))
                                        denials |= DIR_READ;
                        } else {
                                pdace->flags = FILE_INHERITANCE;
                                if ((mode & S_IXOTH) && !(mode & S_IXGRP))
                                        denials |= FILE_EXEC;
                                if ((mode & S_IWOTH) && !(mode & S_IWGRP))
                                        denials |= FILE_WRITE;
                                if ((mode & S_IROTH) && !(mode & S_IRGRP))
                                        denials |= FILE_READ;
                        }
                }
                denials &= ~grants;
                if (denials) {
                        pdace->type = ACCESS_DENIED_ACE_TYPE;
                        pdace->size = cpu_to_le16(usidsz + 8);
                        pdace->mask = denials;
                        memcpy((char*)&pdace->sid, usid, usidsz);
                        pos += usidsz + 8;
                        acecnt++;
                }
        }
                /*
                 * for directories, a world execution denial
                 * inherited to plain files
                 */

        if (isdir) {
                pdace = (ACCESS_DENIED_ACE*) &secattr[offs + pos];
                        pdace->type = ACCESS_DENIED_ACE_TYPE;
                        pdace->flags = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE;
                        pdace->size = cpu_to_le16(wsidsz + 8);
                        pdace->mask = FILE_EXEC;
                        memcpy((char*)&pdace->sid, worldsid, wsidsz);
                        pos += wsidsz + 8;
                        acecnt++;
        }


                /* now insert grants to owner */
        pgace = (ACCESS_ALLOWED_ACE*) &secattr[offs + pos];
        pgace->type = ACCESS_ALLOWED_ACE_TYPE;
        pgace->size = cpu_to_le16(usidsz + 8);
        pgace->flags = gflags;
        pgace->mask = grants;
        memcpy((char*)&pgace->sid, usid, usidsz);
        pos += usidsz + 8;
        acecnt++;

        /* a grant ACE for group */
        /* unless group has the same rights as world */
        /* but present if group is owner or owner is administrator */
        /* this ACE will be inserted after denials for group */

        if (adminowns
            || (((mode >> 3) ^ mode) & 7)) {
                grants = WORLD_RIGHTS;
                if (isdir) {
                        gflags = DIR_INHERITANCE;
                        if (mode & S_IXGRP)
                                grants |= DIR_EXEC;
                        if (mode & S_IWGRP)
                                grants |= DIR_WRITE;
                        if (mode & S_IRGRP)
                                grants |= DIR_READ;
                } else {
                        gflags = FILE_INHERITANCE;
                        if (mode & S_IXGRP)
                                grants |= FILE_EXEC;
                        if (mode & S_IWGRP)
                                grants |= FILE_WRITE;
                        if (mode & S_IRGRP)
                                grants |= FILE_READ;
                }

                /* a possible ACE to deny group what it would get from world */
                /* or administrator, unless owner is administrator or group */

                denials = const_cpu_to_le32(0);
                pdace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
                if (!adminowns && !groupowns) {
                        if (isdir) {
                                pdace->flags = DIR_INHERITANCE;
                                if (mode & S_IXOTH)
                                        denials |= DIR_EXEC;
                                if (mode & S_IWOTH)
                                        denials |= DIR_WRITE;
                                if (mode & S_IROTH)
                                        denials |= DIR_READ;
                        } else {
                                pdace->flags = FILE_INHERITANCE;
                                if (mode & S_IXOTH)
                                        denials |= FILE_EXEC;
                                if (mode & S_IWOTH)
                                        denials |= FILE_WRITE;
                                if (mode & S_IROTH)
                                        denials |= FILE_READ;
                        }
                        denials &= ~(grants | OWNER_RIGHTS);
                        if (denials) {
                                pdace->type = ACCESS_DENIED_ACE_TYPE;
                                pdace->size = cpu_to_le16(gsidsz + 8);
                                pdace->mask = denials;
                                memcpy((char*)&pdace->sid, gsid, gsidsz);
                                pos += gsidsz + 8;
                                acecnt++;
                        }
                }

                if (adminowns
                   || groupowns
                   || ((mode >> 3) & ~mode & 7)) {
                                /* now insert grants to group */
                                /* if more rights than other */
                        pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
                        pgace->type = ACCESS_ALLOWED_ACE_TYPE;
                        pgace->flags = gflags;
                        pgace->size = cpu_to_le16(gsidsz + 8);
                        pgace->mask = grants;
                        memcpy((char*)&pgace->sid, gsid, gsidsz);
                        pos += gsidsz + 8;
                        acecnt++;
                }
        }

        /* an ACE for world users */

        pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
        pgace->type = ACCESS_ALLOWED_ACE_TYPE;
        grants = WORLD_RIGHTS;
        if (isdir) {
                pgace->flags = DIR_INHERITANCE;
                if (mode & S_IXOTH)
                        grants |= DIR_EXEC;
                if (mode & S_IWOTH)
                        grants |= DIR_WRITE;
                if (mode & S_IROTH)
                        grants |= DIR_READ;
        } else {
                pgace->flags = FILE_INHERITANCE;
                if (mode & S_IXOTH)
                        grants |= FILE_EXEC;
                if (mode & S_IWOTH)
                        grants |= FILE_WRITE;
                if (mode & S_IROTH)
                        grants |= FILE_READ;
        }
        pgace->size = cpu_to_le16(wsidsz + 8);
        pgace->mask = grants;
        memcpy((char*)&pgace->sid, worldsid, wsidsz);
        pos += wsidsz + 8;
        acecnt++;

        /* an ACE for administrators */
        /* always full access */

        pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
        pgace->type = ACCESS_ALLOWED_ACE_TYPE;
        if (isdir)
                pgace->flags = DIR_INHERITANCE;
        else
                pgace->flags = FILE_INHERITANCE;
        pgace->size = cpu_to_le16(asidsz + 8);
        grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC;
        pgace->mask = grants;
        memcpy((char*)&pgace->sid, adminsid, asidsz);
        pos += asidsz + 8;
        acecnt++;

        /* an ACE for system (needed ?) */
        /* always full access */

        pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
        pgace->type = ACCESS_ALLOWED_ACE_TYPE;
        if (isdir)
                pgace->flags = DIR_INHERITANCE;
        else
                pgace->flags = FILE_INHERITANCE;
        pgace->size = cpu_to_le16(ssidsz + 8);
        grants = OWNER_RIGHTS | FILE_READ | FILE_WRITE | FILE_EXEC;
        pgace->mask = grants;
        memcpy((char*)&pgace->sid, systemsid, ssidsz);
        pos += ssidsz + 8;
        acecnt++;

        /* a null ACE to hold special flags */
        /* using the same representation as cygwin */

        if (mode & (S_ISVTX | S_ISGID | S_ISUID)) {
                nsidsz = ntfs_sid_size(nullsid);
                pgace = (ACCESS_ALLOWED_ACE*)&secattr[offs + pos];
                pgace->type = ACCESS_ALLOWED_ACE_TYPE;
                pgace->flags = NO_PROPAGATE_INHERIT_ACE;
                pgace->size = cpu_to_le16(nsidsz + 8);
                grants = const_cpu_to_le32(0);
                if (mode & S_ISUID)
                        grants |= FILE_APPEND_DATA;
                if (mode & S_ISGID)
                        grants |= FILE_WRITE_DATA;
                if (mode & S_ISVTX)
                        grants |= FILE_READ_DATA;
                pgace->mask = grants;
                memcpy((char*)&pgace->sid, nullsid, nsidsz);
                pos += nsidsz + 8;
                acecnt++;
        }

        /* fix ACL header */
        pacl->size = cpu_to_le16(pos);
        pacl->ace_count = cpu_to_le16(acecnt);
        return (pos);
}

#if POSIXACLS

/*
 *              Build a full security descriptor from a Posix ACL
 *      returns descriptor in allocated memory, must free() after use
 */

char *ntfs_build_descr_posix(struct MAPPING* const mapping[],
                        struct POSIX_SECURITY *pxdesc,
                        int isdir, const SID *usid, const SID *gsid)
{
        int newattrsz;
        SECURITY_DESCRIPTOR_RELATIVE *pnhead;
        char *newattr;
        int aclsz;
        int usidsz;
        int gsidsz;
        int wsidsz;
        int asidsz;
        int ssidsz;
        int k;

        usidsz = ntfs_sid_size(usid);
        gsidsz = ntfs_sid_size(gsid);
        wsidsz = ntfs_sid_size(worldsid);
        asidsz = ntfs_sid_size(adminsid);
        ssidsz = ntfs_sid_size(systemsid);

        /* allocate enough space for the new security attribute */
        newattrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE)        /* header */
            + usidsz + gsidsz   /* usid and gsid */
            + sizeof(ACL)       /* acl header */
            + 2*(8 + usidsz)    /* two possible ACE for user */
            + 3*(8 + gsidsz)    /* three possible ACE for group and mask */
            + 8 + wsidsz        /* one ACE for world */
            + 8 + asidsz        /* one ACE for admin */
            + 8 + ssidsz;       /* one ACE for system */
        if (isdir)                      /* a world denial for directories */
                newattrsz += 8 + wsidsz;
        if (pxdesc->mode & 07000)       /* a NULL ACE for special modes */
                newattrsz += 8 + ntfs_sid_size(nullsid);
                                /* account for non-owning users and groups */
        for (k=0; k<pxdesc->acccnt; k++) {
                if ((pxdesc->acl.ace[k].tag == POSIX_ACL_USER)
                    || (pxdesc->acl.ace[k].tag == POSIX_ACL_GROUP))
                        newattrsz += 3*MAX_SID_SIZE;
        }
                                /* account for default ACE's */
        newattrsz += 2*MAX_SID_SIZE*pxdesc->defcnt;
        newattr = (char*)ntfs_malloc(newattrsz);
        if (newattr) {
                /* build the main header part */
                pnhead = (SECURITY_DESCRIPTOR_RELATIVE*)newattr;
                pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
                pnhead->alignment = 0;
                        /*
                         * The flag SE_DACL_PROTECTED prevents the ACL
                         * to be changed in an inheritance after creation
                         */
                pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED
                                    | SE_SELF_RELATIVE;
                        /*
                         * Windows prefers ACL first, do the same to
                         * get the same hash value and avoid duplication
                         */
                /* build permissions */
                aclsz = buildacls_posix(mapping,newattr,
                          sizeof(SECURITY_DESCRIPTOR_RELATIVE),
                          pxdesc, isdir, usid, gsid);
                if (aclsz && ((int)(sizeof(SECURITY_DESCRIPTOR_RELATIVE)
                                + aclsz + usidsz + gsidsz) <= newattrsz)) {
                        /* append usid and gsid */
                        memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE)
                                 + aclsz], usid, usidsz);
                        memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE)
                                + aclsz + usidsz], gsid, gsidsz);
                        /* positions of ACL, USID and GSID into header */
                        pnhead->owner =
                            cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)
                                 + aclsz);
                        pnhead->group =
                            cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)
                                 + aclsz + usidsz);
                        pnhead->sacl = const_cpu_to_le32(0);
                        pnhead->dacl =
                            const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE));
                } else {
                        /* ACL failure (errno set) or overflow */
                        free(newattr);
                        newattr = (char*)NULL;
                        if (aclsz) {
                                /* hope error was detected before overflowing */
                                ntfs_log_error("Security descriptor is longer than expected\n");
                                errno = EIO;
                        }
                }
        } else
                errno = ENOMEM;
        return (newattr);
}

#endif /* POSIXACLS */

/*
 *              Build a full security descriptor
 *      returns descriptor in allocated memory, must free() after use
 */

char *ntfs_build_descr(mode_t mode,
                        int isdir, const SID * usid, const SID * gsid)
{
        int newattrsz;
        SECURITY_DESCRIPTOR_RELATIVE *pnhead;
        char *newattr;
        int aclsz;
        int usidsz;
        int gsidsz;
        int wsidsz;
        int asidsz;
        int ssidsz;

        usidsz = ntfs_sid_size(usid);
        gsidsz = ntfs_sid_size(gsid);
        wsidsz = ntfs_sid_size(worldsid);
        asidsz = ntfs_sid_size(adminsid);
        ssidsz = ntfs_sid_size(systemsid);

        /* allocate enough space for the new security attribute */
        newattrsz = sizeof(SECURITY_DESCRIPTOR_RELATIVE)        /* header */
            + usidsz + gsidsz   /* usid and gsid */
            + sizeof(ACL)       /* acl header */
            + 2*(8 + usidsz)    /* two possible ACE for user */
            + 2*(8 + gsidsz)    /* two possible ACE for group */
            + 8 + wsidsz        /* one ACE for world */
            + 8 + asidsz        /* one ACE for admin */
            + 8 + ssidsz;       /* one ACE for system */
        if (isdir)                      /* a world denial for directories */
                newattrsz += 8 + wsidsz;
        if (mode & 07000)       /* a NULL ACE for special modes */
                newattrsz += 8 + ntfs_sid_size(nullsid);
        newattr = (char*)ntfs_malloc(newattrsz);
        if (newattr) {
                /* build the main header part */
                pnhead = (SECURITY_DESCRIPTOR_RELATIVE*) newattr;
                pnhead->revision = SECURITY_DESCRIPTOR_REVISION;
                pnhead->alignment = 0;
                        /*
                         * The flag SE_DACL_PROTECTED prevents the ACL
                         * to be changed in an inheritance after creation
                         */
                pnhead->control = SE_DACL_PRESENT | SE_DACL_PROTECTED
                                    | SE_SELF_RELATIVE;
                        /*
                         * Windows prefers ACL first, do the same to
                         * get the same hash value and avoid duplication
                         */
                /* build permissions */
                aclsz = buildacls(newattr,
                          sizeof(SECURITY_DESCRIPTOR_RELATIVE),
                          mode, isdir, usid, gsid);
                if (((int)sizeof(SECURITY_DESCRIPTOR_RELATIVE)
                                + aclsz + usidsz + gsidsz) <= newattrsz) {
                        /* append usid and gsid */
                        memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE)
                                 + aclsz], usid, usidsz);
                        memcpy(&newattr[sizeof(SECURITY_DESCRIPTOR_RELATIVE)
                                + aclsz + usidsz], gsid, gsidsz);
                        /* positions of ACL, USID and GSID into header */
                        pnhead->owner =
                            cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)
                                 + aclsz);
                        pnhead->group =
                            cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE)
                                 + aclsz + usidsz);
                        pnhead->sacl = const_cpu_to_le32(0);
                        pnhead->dacl =
                            const_cpu_to_le32(sizeof(SECURITY_DESCRIPTOR_RELATIVE));
                } else {
                        /* hope error was detected before overflowing */
                        free(newattr);
                        newattr = (char*)NULL;
                        ntfs_log_error("Security descriptor is longer than expected\n");
                        errno = EIO;
                }
        } else
                errno = ENOMEM;
        return (newattr);
}

/*
 *              Create a mode_t permission set
 *      from owner, group and world grants as represented in ACEs
 */

static int merge_permissions(BOOL isdir,
                le32 owner, le32 group, le32 world, le32 special)

{
        int perm;

        perm = 0;
        /* build owner permission */
        if (owner) {
                if (isdir) {
                        /* exec if any of list, traverse */
                        if (owner & DIR_GEXEC)
                                perm |= S_IXUSR;
                        /* write if any of addfile, adddir, delchild */
                        if (owner & DIR_GWRITE)
                                perm |= S_IWUSR;
                        /* read if any of list */
                        if (owner & DIR_GREAD)
                                perm |= S_IRUSR;
                } else {
                        /* exec if execute or generic execute */
                        if (owner & FILE_GEXEC)
                                perm |= S_IXUSR;
                        /* write if any of writedata or generic write */
                        if (owner & FILE_GWRITE)
                                perm |= S_IWUSR;
                        /* read if any of readdata or generic read */
                        if (owner & FILE_GREAD)
                                perm |= S_IRUSR;
                }
        }
        /* build group permission */
        if (group) {
                if (isdir) {
                        /* exec if any of list, traverse */
                        if (group & DIR_GEXEC)
                                perm |= S_IXGRP;
                        /* write if any of addfile, adddir, delchild */
                        if (group & DIR_GWRITE)
                                perm |= S_IWGRP;
                        /* read if any of list */
                        if (group & DIR_GREAD)
                                perm |= S_IRGRP;
                } else {
                        /* exec if execute */
                        if (group & FILE_GEXEC)
                                perm |= S_IXGRP;
                        /* write if any of writedata, appenddata */
                        if (group & FILE_GWRITE)
                                perm |= S_IWGRP;
                        /* read if any of readdata */
                        if (group & FILE_GREAD)
                                perm |= S_IRGRP;
                }
        }
        /* build world permission */
        if (world) {
                if (isdir) {
                        /* exec if any of list, traverse */
                        if (world & DIR_GEXEC)
                                perm |= S_IXOTH;
                        /* write if any of addfile, adddir, delchild */
                        if (world & DIR_GWRITE)
                                perm |= S_IWOTH;
                        /* read if any of list */
                        if (world & DIR_GREAD)
                                perm |= S_IROTH;
                } else {
                        /* exec if execute */
                        if (world & FILE_GEXEC)
                                perm |= S_IXOTH;
                        /* write if any of writedata, appenddata */
                        if (world & FILE_GWRITE)
                                perm |= S_IWOTH;
                        /* read if any of readdata */
                        if (world & FILE_GREAD)
                                perm |= S_IROTH;
                }
        }
        /* build special permission flags */
        if (special) {
                if (special & FILE_APPEND_DATA)
                        perm |= S_ISUID;
                if (special & FILE_WRITE_DATA)
                        perm |= S_ISGID;
                if (special & FILE_READ_DATA)
                        perm |= S_ISVTX;
        }
        return (perm);
}

#if POSIXACLS

/*
 *              Normalize a Posix ACL either from a sorted raw set of
 *              access ACEs or default ACEs
 *              (standard case : different owner, group and administrator)
 */

static int norm_std_permissions_posix(struct POSIX_SECURITY *posix_desc,
                BOOL groupowns, int start, int count, int target)
{
        int j,k;
        s32 id;
        u16 tag;
        u16 tagsset;
        struct POSIX_ACE *pxace;
        mode_t grantgrps;
        mode_t grantwrld;
        mode_t denywrld;
        mode_t allow;
        mode_t deny;
        mode_t perms;
        mode_t mode;

        mode = 0;
        tagsset = 0;
                /*
                 * Determine what is granted to some group or world
                 * Also get denials to world which are meant to prevent
                 * execution flags to be inherited by plain files
                 */
        pxace = posix_desc->acl.ace;
        grantgrps = 0;
        grantwrld = 0;
        denywrld = 0;
        for (j=start; j<(start + count); j++) {
                if (pxace[j].perms & POSIX_PERM_DENIAL) {
                                /* deny world exec unless for default */
                        if ((pxace[j].tag == POSIX_ACL_OTHER)
                        && !start)
                                denywrld = pxace[j].perms;
                } else {
                        switch (pxace[j].tag) {
                        case POSIX_ACL_GROUP_OBJ :
                                grantgrps |= pxace[j].perms;
                                break;
                        case POSIX_ACL_GROUP :
                                if (pxace[j].id)
                                        grantgrps |= pxace[j].perms;
                                break;
                        case POSIX_ACL_OTHER :
                                grantwrld = pxace[j].perms;
                                break;
                        default :
                                break;
                        }
                }
        }
                /*
                 * Collect groups of ACEs related to the same id
                 * and determine what is granted and what is denied.
                 * It is important the ACEs have been sorted
                 */
        j = start;
        k = target;
        while (j < (start + count)) {
                tag = pxace[j].tag;
                id = pxace[j].id;
                if (pxace[j].perms & POSIX_PERM_DENIAL) {
                        deny = pxace[j].perms | denywrld;
                        allow = 0;
                } else {
                        deny = denywrld;
                        allow = pxace[j].perms;
                }
                j++;
                while ((j < (start + count))
                    && (pxace[j].tag == tag)
                    && (pxace[j].id == id)) {
                        if (pxace[j].perms & POSIX_PERM_DENIAL)
                                deny |= pxace[j].perms;
                        else
                                allow |= pxace[j].perms;
                        j++;
                }
                        /*
                         * Build the permissions equivalent to grants and denials
                         */
                if (groupowns) {
                        if (tag == POSIX_ACL_MASK)
                                perms = ~deny;
                        else
                                perms = allow & ~deny;
                } else
                        switch (tag) {
                        case POSIX_ACL_USER_OBJ :
                                perms = (allow | grantgrps | grantwrld) & ~deny;
                                break;
                        case POSIX_ACL_USER :
                                if (id)
                                        perms = (allow | grantgrps | grantwrld)
                                                & ~deny;
                                else
                                        perms = allow;
                                break;
                        case POSIX_ACL_GROUP_OBJ :
                                perms = (allow | grantwrld) & ~deny;
                                break;
                        case POSIX_ACL_GROUP :
                                if (id)
                                        perms = (allow | grantwrld) & ~deny;
                                else
                                        perms = allow;
                                break;
                        case POSIX_ACL_MASK :
                                perms = ~deny;
                                break;
                        default :
                                perms = allow & ~deny;
                                break;
                        }
                        /*
                         * Store into a Posix ACE
                         */
                if (tag != POSIX_ACL_SPECIAL) {
                        pxace[k].tag = tag;
                        pxace[k].id = id;
                        pxace[k].perms = perms
                                 & (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X);
                        tagsset |= tag;
                        k++;
                }
                switch (tag) {
                case POSIX_ACL_USER_OBJ :
                        mode |= ((perms & 7) << 6);
                        break;
                case POSIX_ACL_GROUP_OBJ :
                case POSIX_ACL_MASK :
                        mode = (mode & 07707) | ((perms & 7) << 3);
                        break;
                case POSIX_ACL_OTHER :
                        mode |= perms & 7;
                        break;
                case POSIX_ACL_SPECIAL :
                        mode |= (perms & (S_ISVTX | S_ISUID | S_ISGID));
                        break;
                default :
                        break;
                }
        }
        if (!start) { /* not satisfactory */
                posix_desc->mode = mode;
                posix_desc->tagsset = tagsset;
        }
        return (k - target);
}

#endif /* POSIXACLS */

/*
 *              Interpret an ACL and extract meaningful grants
 *              (standard case : different owner, group and administrator)
 */

static int build_std_permissions(const char *securattr,
                const SID *usid, const SID *gsid, BOOL isdir)
{
        const SECURITY_DESCRIPTOR_RELATIVE *phead;
        const ACL *pacl;
        const ACCESS_ALLOWED_ACE *pace;
        int offdacl;
        int offace;
        int acecnt;
        int nace;
        BOOL noown;
        le32 special;
        le32 allowown, allowgrp, allowall;
        le32 denyown, denygrp, denyall;

        phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
        offdacl = le32_to_cpu(phead->dacl);
        pacl = (const ACL*)&securattr[offdacl];
        special = const_cpu_to_le32(0);
        allowown = allowgrp = allowall = const_cpu_to_le32(0);
        denyown = denygrp = denyall = const_cpu_to_le32(0);
        noown = TRUE;
        if (offdacl) {
                acecnt = le16_to_cpu(pacl->ace_count);
                offace = offdacl + sizeof(ACL);
        } else {
                acecnt = 0;
                offace = 0;
        }
        for (nace = 0; nace < acecnt; nace++) {
                pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace];
                if (!(pace->flags & INHERIT_ONLY_ACE)) {
                        if (ntfs_same_sid(usid, &pace->sid)
                          || ntfs_same_sid(ownersid, &pace->sid)) {
                                noown = FALSE;
                                if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
                                        allowown |= pace->mask;
                                else if (pace->type == ACCESS_DENIED_ACE_TYPE)
                                        denyown |= pace->mask;
                                } else
                                if (ntfs_same_sid(gsid, &pace->sid)
                                    && !(pace->mask & WRITE_OWNER)) {
                                        if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
                                                allowgrp |= pace->mask;
                                        else if (pace->type == ACCESS_DENIED_ACE_TYPE)
                                                denygrp |= pace->mask;
                                } else
                                        if (is_world_sid((const SID*)&pace->sid)) {
                                                if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
                                                        allowall |= pace->mask;
                                                else
                                                        if (pace->type == ACCESS_DENIED_ACE_TYPE)
                                                                denyall |= pace->mask;
                                        } else
                                        if ((ntfs_same_sid((const SID*)&pace->sid,nullsid))
                                           && (pace->type == ACCESS_ALLOWED_ACE_TYPE))
                                                special |= pace->mask;
                        }
                        offace += le16_to_cpu(pace->size);
                }
                /*
                 * No indication about owner's rights : grant basic rights
                 * This happens for files created by Windows in directories
                 * created by Linux and owned by root, because Windows
                 * merges the admin ACEs
                 */
        if (noown)
                allowown = (FILE_READ_DATA | FILE_WRITE_DATA | FILE_EXECUTE);
                /*
                 *  Add to owner rights granted to group or world
                 * unless denied personaly, and add to group rights
                 * granted to world unless denied specifically
                 */
        allowown |= (allowgrp | allowall);
        allowgrp |= allowall;
        return (merge_permissions(isdir,
                                allowown & ~(denyown | denyall),
                                allowgrp & ~(denygrp | denyall),
                                allowall & ~denyall,
                                special));
}

/*
 *              Interpret an ACL and extract meaningful grants
 *              (special case : owner and group are the same,
 *              and not administrator)
 */

static int build_owngrp_permissions(const char *securattr,
                        const SID *usid, BOOL isdir)
{
        const SECURITY_DESCRIPTOR_RELATIVE *phead;
        const ACL *pacl;
        const ACCESS_ALLOWED_ACE *pace;
        int offdacl;
        int offace;
        int acecnt;
        int nace;
        le32 special;
        BOOL grppresent;
        BOOL ownpresent;
        le32 allowown, allowgrp, allowall;
        le32 denyown, denygrp, denyall;

        phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
        offdacl = le32_to_cpu(phead->dacl);
        pacl = (const ACL*)&securattr[offdacl];
        special = const_cpu_to_le32(0);
        allowown = allowgrp = allowall = const_cpu_to_le32(0);
        denyown = denygrp = denyall = const_cpu_to_le32(0);
        ownpresent = FALSE;
        grppresent = FALSE;
        if (offdacl) {
                acecnt = le16_to_cpu(pacl->ace_count);
                offace = offdacl + sizeof(ACL);
        } else {
                acecnt = 0;
                offace = 0;
        }
        for (nace = 0; nace < acecnt; nace++) {
                pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace];
                if (!(pace->flags & INHERIT_ONLY_ACE)) {
                        if ((ntfs_same_sid(usid, &pace->sid)
                           || ntfs_same_sid(ownersid, &pace->sid))
                            && (pace->mask & WRITE_OWNER)) {
                                if (pace->type == ACCESS_ALLOWED_ACE_TYPE) {
                                        allowown |= pace->mask;
                                        ownpresent = TRUE;
                                }
                        } else
                                if (ntfs_same_sid(usid, &pace->sid)
                                   && (!(pace->mask & WRITE_OWNER))) {
                                        if (pace->type == ACCESS_ALLOWED_ACE_TYPE) {
                                                allowgrp |= pace->mask;
                                                grppresent = TRUE;
                                        }
                                } else
                                        if (is_world_sid((const SID*)&pace->sid)) {
                                                if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
                                                        allowall |= pace->mask;
                                                else
                                                        if (pace->type == ACCESS_DENIED_ACE_TYPE)
                                                                denyall |= pace->mask;
                                        } else
                                        if ((ntfs_same_sid((const SID*)&pace->sid,nullsid))
                                           && (pace->type == ACCESS_ALLOWED_ACE_TYPE))
                                                special |= pace->mask;
                        }
                        offace += le16_to_cpu(pace->size);
                }
        if (!ownpresent)
                allowown = allowall;
        if (!grppresent)
                allowgrp = allowall;
        return (merge_permissions(isdir,
                                allowown & ~(denyown | denyall),
                                allowgrp & ~(denygrp | denyall),
                                allowall & ~denyall,
                                special));
}

#if POSIXACLS

/*
 *              Normalize a Posix ACL either from a sorted raw set of
 *              access ACEs or default ACEs
 *              (special case : owner or/and group is administrator)
 */

static int norm_ownadmin_permissions_posix(struct POSIX_SECURITY *posix_desc,
                int start, int count, int target)
{
        int j,k;
        s32 id;
        u16 tag;
        u16 tagsset;
        struct POSIX_ACE *pxace;
        mode_t denywrld;
        mode_t allow;
        mode_t deny;
        mode_t perms;
        mode_t mode;

        mode = 0;
        pxace = posix_desc->acl.ace;
        tagsset = 0;
        denywrld = 0;
                /*
                 * Get denials to world which are meant to prevent
                 * execution flags to be inherited by plain files
                 */
        for (j=start; j<(start + count); j++) {
                if (pxace[j].perms & POSIX_PERM_DENIAL) {
                                /* deny world exec not for default */
                        if ((pxace[j].tag == POSIX_ACL_OTHER)
                        && !start)
                                denywrld = pxace[j].perms;
                }
        }
                /*
                 * Collect groups of ACEs related to the same id
                 * and determine what is granted (denials are ignored)
                 * It is important the ACEs have been sorted
                 */
        j = start;
        k = target;
        deny = 0;
        while (j < (start + count)) {
                allow = 0;
                tag = pxace[j].tag;
                id = pxace[j].id;
                if (tag == POSIX_ACL_MASK) {
                        deny = pxace[j].perms;
                        j++;
                        while ((j < (start + count))
                            && (pxace[j].tag == POSIX_ACL_MASK))
                                j++;
                } else {
                        if (!(pxace[j].perms & POSIX_PERM_DENIAL))
                                allow = pxace[j].perms;
                        j++;
                        while ((j < (start + count))
                            && (pxace[j].tag == tag)
                            && (pxace[j].id == id)) {
                                if (!(pxace[j].perms & POSIX_PERM_DENIAL))
                                        allow |= pxace[j].perms;
                                j++;
                        }
                }

                        /*
                         * Store the grants into a Posix ACE
                         */
                if (tag == POSIX_ACL_MASK)
                        perms = ~deny;
                else
                        perms = allow & ~denywrld;
                if (tag != POSIX_ACL_SPECIAL) {
                        pxace[k].tag = tag;
                        pxace[k].id = id;
                        pxace[k].perms = perms
                                 & (POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X);
                        tagsset |= tag;
                        k++;
                }
                switch (tag) {
                case POSIX_ACL_USER_OBJ :
                        mode |= ((perms & 7) << 6);
                        break;
                case POSIX_ACL_GROUP_OBJ :
                case POSIX_ACL_MASK :
                        mode = (mode & 07707) | ((perms & 7) << 3);
                        break;
                case POSIX_ACL_OTHER :
                        mode |= perms & 7;
                        break;
                case POSIX_ACL_SPECIAL :
                        mode |= perms & (S_ISVTX | S_ISUID | S_ISGID);
                        break;
                default :
                        break;
                }
        }
        if (!start) { /* not satisfactory */
                posix_desc->mode = mode;
                posix_desc->tagsset = tagsset;
        }
        return (k - target);
}

#endif /* POSIXACLS */

/*
 *              Interpret an ACL and extract meaningful grants
 *              (special case : owner or/and group is administrator)
 */


static int build_ownadmin_permissions(const char *securattr,
                        const SID *usid, const SID *gsid, BOOL isdir)
{
        const SECURITY_DESCRIPTOR_RELATIVE *phead;
        const ACL *pacl;
        const ACCESS_ALLOWED_ACE *pace;
        int offdacl;
        int offace;
        int acecnt;
        int nace;
        BOOL firstapply;
        int isforeign;
        le32 special;
        le32 allowown, allowgrp, allowall;
        le32 denyown, denygrp, denyall;

        phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
        offdacl = le32_to_cpu(phead->dacl);
        pacl = (const ACL*)&securattr[offdacl];
        special = const_cpu_to_le32(0);
        allowown = allowgrp = allowall = const_cpu_to_le32(0);
        denyown = denygrp = denyall = const_cpu_to_le32(0);
        if (offdacl) {
                acecnt = le16_to_cpu(pacl->ace_count);
                offace = offdacl + sizeof(ACL);
        } else {
                acecnt = 0;
                offace = 0;
        }
        firstapply = TRUE;
        isforeign = 3;
        for (nace = 0; nace < acecnt; nace++) {
                pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace];
                if (!(pace->flags & INHERIT_ONLY_ACE)
                   && !(~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK))) {
                        if ((ntfs_same_sid(usid, &pace->sid)
                           || ntfs_same_sid(ownersid, &pace->sid))
                             && (((pace->mask & WRITE_OWNER) && firstapply))) {
                                if (pace->type == ACCESS_ALLOWED_ACE_TYPE) {
                                        allowown |= pace->mask;
                                        isforeign &= ~1;
                                } else
                                        if (pace->type == ACCESS_DENIED_ACE_TYPE)
                                                denyown |= pace->mask;
                                } else
                                    if (ntfs_same_sid(gsid, &pace->sid)
                                        && (!(pace->mask & WRITE_OWNER))) {
                                                if (pace->type == ACCESS_ALLOWED_ACE_TYPE) {
                                                        allowgrp |= pace->mask;
                                                        isforeign &= ~2;
                                                } else
                                                        if (pace->type == ACCESS_DENIED_ACE_TYPE)
                                                                denygrp |= pace->mask;
                                        } else if (is_world_sid((const SID*)&pace->sid)) {
                                                if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
                                                        allowall |= pace->mask;
                                                else
                                                        if (pace->type == ACCESS_DENIED_ACE_TYPE)
                                                                denyall |= pace->mask;
                                        }
                        firstapply = FALSE;
                        } else
                                if (!(pace->flags & INHERIT_ONLY_ACE))
                                        if ((ntfs_same_sid((const SID*)&pace->sid,nullsid))
                                           && (pace->type == ACCESS_ALLOWED_ACE_TYPE))
                                                special |= pace->mask;
                        offace += le16_to_cpu(pace->size);
                }
        if (isforeign) {
                allowown |= (allowgrp | allowall);
                allowgrp |= allowall;
        }
        return (merge_permissions(isdir,
                                allowown & ~(denyown | denyall),
                                allowgrp & ~(denygrp | denyall),
                                allowall & ~denyall,
                                special));
}

#if OWNERFROMACL

/*
 *              Define the owner of a file as the first user allowed
 *      to change the owner, instead of the user defined as owner.
 *
 *      This produces better approximations for files written by a
 *      Windows user in an inheritable directory owned by another user,
 *      as the access rights are inheritable but the ownership is not.
 *
 *      An important case is the directories "Documents and Settings/user"
 *      which the users must have access to, though Windows considers them
 *      as owned by administrator.
 */

const SID *ntfs_acl_owner(const char *securattr)
{
        const SECURITY_DESCRIPTOR_RELATIVE *phead;
        const SID *usid;
        const ACL *pacl;
        const ACCESS_ALLOWED_ACE *pace;
        int offdacl;
        int offace;
        int acecnt;
        int nace;
        BOOL found;

        found = FALSE;
        phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
        offdacl = le32_to_cpu(phead->dacl);
        if (offdacl) {
                pacl = (const ACL*)&securattr[offdacl];
                acecnt = le16_to_cpu(pacl->ace_count);
                offace = offdacl + sizeof(ACL);
                nace = 0;
                do {
                        pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace];
                        if ((pace->mask & WRITE_OWNER)
                           && (pace->type == ACCESS_ALLOWED_ACE_TYPE)
                           && ntfs_is_user_sid(&pace->sid))
                                found = TRUE;
                        offace += le16_to_cpu(pace->size);
                } while (!found && (++nace < acecnt));
        }
        if (found)
                usid = &pace->sid;
        else
                usid = (const SID*)&securattr[le32_to_cpu(phead->owner)];
        return (usid);
}

#else

/*
 *              Special case for files owned by administrator with full
 *      access granted to a mapped user : consider this user as the tenant
 *      of the file.
 *
 *      This situation cannot be represented with Linux concepts and can
 *      only be found for files or directories created by Windows.
 *      Typical situation : directory "Documents and Settings/user" which
 *      is on the path to user's files and must be given access to user
 *      only.
 *
 *      Check file is owned by administrator and no user has rights before
 *      calling.
 *      Returns the uid of tenant or zero if none
 */


static uid_t find_tenant(struct MAPPING *const mapping[],
                        const char *securattr)
{
        const SECURITY_DESCRIPTOR_RELATIVE *phead;
        const ACL *pacl;
        const ACCESS_ALLOWED_ACE *pace;
        int offdacl;
        int offace;
        int acecnt;
        int nace;
        uid_t tid;
        uid_t xid;

        phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
        offdacl = le32_to_cpu(phead->dacl);
        pacl = (const ACL*)&securattr[offdacl];
        tid = 0;
        if (offdacl) {
                acecnt = le16_to_cpu(pacl->ace_count);
                offace = offdacl + sizeof(ACL);
        } else
                acecnt = 0;
        for (nace = 0; nace < acecnt; nace++) {
                pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace];
                if ((pace->type == ACCESS_ALLOWED_ACE_TYPE)
                   && (pace->mask & DIR_WRITE)) {
                        xid = ntfs_find_user(mapping[MAPUSERS], &pace->sid);
                        if (xid) tid = xid;
                }
                offace += le16_to_cpu(pace->size);
        }
        return (tid);
}

#endif /* OWNERFROMACL */

#if POSIXACLS

/*
 *              Build Posix permissions from an ACL
 *      returns a pointer to the requested permissions
 *      or a null pointer (with errno set) if there is a problem
 *
 *      If the NTFS ACL was created according to our rules, the retrieved
 *      Posix ACL should be the exact ACL which was set. However if
 *      the NTFS ACL was built by a different tool, the result could
 *      be a a poor approximation of what was expected
 */

struct POSIX_SECURITY *ntfs_build_permissions_posix(
                        struct MAPPING *const mapping[],
                        const char *securattr,
                        const SID *usid, const SID *gsid, BOOL isdir)
{
        const SECURITY_DESCRIPTOR_RELATIVE *phead;
        struct POSIX_SECURITY *pxdesc;
        const ACL *pacl;
        const ACCESS_ALLOWED_ACE *pace;
        struct POSIX_ACE *pxace;
        struct {
                uid_t prevuid;
                gid_t prevgid;
                int groupmasks;
                s16 tagsset;
                BOOL gotowner;
                BOOL gotownermask;
                BOOL gotgroup;
                mode_t permswrld;
        } ctx[2], *pctx;
        int offdacl;
        int offace;
        int alloccnt;
        int acecnt;
        uid_t uid;
        gid_t gid;
        int i,j;
        int k,l;
        BOOL ignore;
        BOOL adminowns;
        BOOL groupowns;
        BOOL firstinh;
        BOOL genericinh;

        phead = (const SECURITY_DESCRIPTOR_RELATIVE*)securattr;
        offdacl = le32_to_cpu(phead->dacl);
        if (offdacl) {
                pacl = (const ACL*)&securattr[offdacl];
                acecnt = le16_to_cpu(pacl->ace_count);
                offace = offdacl + sizeof(ACL);
        } else {
                acecnt = 0;
                offace = 0;
        }
        adminowns = FALSE;
        groupowns = ntfs_same_sid(gsid,usid);
        firstinh = FALSE;
        genericinh = FALSE;
                /*
                 * Build a raw posix security descriptor
                 * by just translating permissions and ids
                 * Add 2 to the count of ACE to be able to insert
                 * a group ACE later in access and default ACLs
                 * and add 2 more to be able to insert ACEs for owner
                 * and 2 more for other
                 */
        alloccnt = acecnt + 6;
        pxdesc = (struct POSIX_SECURITY*)malloc(
                                sizeof(struct POSIX_SECURITY)
                                + alloccnt*sizeof(struct POSIX_ACE));
        k = 0;
        l = alloccnt;
        for (i=0; i<2; i++) {
                pctx = &ctx[i];
                pctx->permswrld = 0;
                pctx->prevuid = -1;
                pctx->prevgid = -1;
                pctx->groupmasks = 0;
                pctx->tagsset = 0;
                pctx->gotowner = FALSE;
                pctx->gotgroup = FALSE;
                pctx->gotownermask = FALSE;
        }
        for (j=0; j<acecnt; j++) {
                pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace];
                if (pace->flags & INHERIT_ONLY_ACE) {
                        pxace = &pxdesc->acl.ace[l - 1];
                        pctx = &ctx[1];
                } else {
                        pxace = &pxdesc->acl.ace[k];
                        pctx = &ctx[0];
                }
                ignore = FALSE;
                        /*
                         * grants for root as a designated user or group
                         */
                if ((~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK))
                   && (pace->type == ACCESS_ALLOWED_ACE_TYPE)
                   && ntfs_same_sid(&pace->sid, adminsid)) {
                        pxace->tag = (pace->mask & ROOT_OWNER_UNMARK ? POSIX_ACL_GROUP : POSIX_ACL_USER);
                        pxace->id = 0;
                        if ((pace->mask & (GENERIC_ALL | WRITE_OWNER))
                           && (pace->flags & INHERIT_ONLY_ACE))
                                ignore = genericinh = TRUE;
                } else
                if (ntfs_same_sid(usid, &pace->sid)) {
                        pxace->id = -1;
                                /*
                                 * Owner has no write-owner right :
                                 * a group was defined same as owner
                                 * or admin was owner or group :
                                 * denials are meant to owner
                                 * and grants are meant to group
                                 */
                        if (!(pace->mask & (WRITE_OWNER | GENERIC_ALL))
                            && (pace->type == ACCESS_ALLOWED_ACE_TYPE)) {
                                if (ntfs_same_sid(gsid,usid)) {
                                        pxace->tag = POSIX_ACL_GROUP_OBJ;
                                        pxace->id = -1;
                                } else {
                                        if (ntfs_same_sid(&pace->sid,usid))
                                                groupowns = TRUE;
                                        gid = ntfs_find_group(mapping[MAPGROUPS],&pace->sid);
                                        if (gid) {
                                                pxace->tag = POSIX_ACL_GROUP;
                                                pxace->id = gid;
                                                pctx->prevgid = gid;
                                        } else {
                                        uid = ntfs_find_user(mapping[MAPUSERS],&pace->sid);
                                        if (uid) {
                                                pxace->tag = POSIX_ACL_USER;
                                                pxace->id = uid;
                                        } else
                                                ignore = TRUE;
                                        }
                                }
                        } else {
                                /*
                                 * when group owns, late denials for owner
                                 * mean group mask
                                 */
                                if ((pace->type == ACCESS_DENIED_ACE_TYPE)
                                    && (pace->mask & WRITE_OWNER)) {
                                        pxace->tag = POSIX_ACL_MASK;
                                        pctx->gotownermask = TRUE;
                                        if (pctx->gotowner)
                                                pctx->groupmasks++;
                                } else {
                                        if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
                                                pctx->gotowner = TRUE;
                                        if (pctx->gotownermask && !pctx->gotowner) {
                                                uid = ntfs_find_user(mapping[MAPUSERS],&pace->sid);
                                                pxace->id = uid;
                                                pxace->tag = POSIX_ACL_USER;
                                        } else
                                                pxace->tag = POSIX_ACL_USER_OBJ;
                                                /* system ignored, and admin */
                                                /* ignored at first position */
                                        if (pace->flags & INHERIT_ONLY_ACE) {
                                                if ((firstinh && ntfs_same_sid(&pace->sid,adminsid))
                                                   || ntfs_same_sid(&pace->sid,systemsid))
                                                        ignore = TRUE;
                                                if (!firstinh) {
                                                        firstinh = TRUE;
                                                }
                                        } else {
                                                if ((adminowns && ntfs_same_sid(&pace->sid,adminsid))
                                                   || ntfs_same_sid(&pace->sid,systemsid))
                                                        ignore = TRUE;
                                                if (ntfs_same_sid(usid,adminsid))
                                                        adminowns = TRUE;
                                        }
                                }
                        }
                } else if (ntfs_same_sid(gsid, &pace->sid)) {
                        if ((pace->type == ACCESS_DENIED_ACE_TYPE)
                            && (pace->mask & WRITE_OWNER)) {
                                pxace->tag = POSIX_ACL_MASK;
                                pxace->id = -1;
                                if (pctx->gotowner)
                                        pctx->groupmasks++;
                        } else {
                                if (pctx->gotgroup || (pctx->groupmasks > 1)) {
                                        gid = ntfs_find_group(mapping[MAPGROUPS],&pace->sid);
                                        if (gid) {
                                                pxace->id = gid;
                                                pxace->tag = POSIX_ACL_GROUP;
                                                pctx->prevgid = gid;
                                        } else
                                                ignore = TRUE;
                                } else {
                                        pxace->id = -1;
                                        pxace->tag = POSIX_ACL_GROUP_OBJ;
                                        if (pace->type == ACCESS_ALLOWED_ACE_TYPE)
                                                pctx->gotgroup = TRUE;
                                }

                                if (ntfs_same_sid(gsid,adminsid)
                                    || ntfs_same_sid(gsid,systemsid)) {
                                        if (pace->mask & (WRITE_OWNER | GENERIC_ALL))
                                                ignore = TRUE;
                                        if (ntfs_same_sid(gsid,adminsid))
                                                adminowns = TRUE;
                                        else
                                                genericinh = ignore;
                                }
                        }
                } else if (is_world_sid((const SID*)&pace->sid)) {
                        pxace->id = -1;
                        pxace->tag = POSIX_ACL_OTHER;
                        if ((pace->type == ACCESS_DENIED_ACE_TYPE)
                           && (pace->flags & INHERIT_ONLY_ACE))
                                ignore = TRUE;
                } else if (ntfs_same_sid((const SID*)&pace->sid,nullsid)) {
                        pxace->id = -1;
                        pxace->tag = POSIX_ACL_SPECIAL;
                } else {
                        uid = ntfs_find_user(mapping[MAPUSERS],&pace->sid);
                        if (uid) {
                                if ((pace->type == ACCESS_DENIED_ACE_TYPE)
                                    && (pace->mask & WRITE_OWNER)
                                    && (pctx->prevuid != uid)) {
                                        pxace->id = -1;
                                        pxace->tag = POSIX_ACL_MASK;
                                } else {
                                        pxace->id = uid;
                                        pxace->tag = POSIX_ACL_USER;
                                }
                                pctx->prevuid = uid;
                        } else {
                                gid = ntfs_find_group(mapping[MAPGROUPS],&pace->sid);
                                if (gid) {
                                        if ((pace->type == ACCESS_DENIED_ACE_TYPE)
                                            && (pace->mask & WRITE_OWNER)
                                            && (pctx->prevgid != gid)) {
                                                pxace->tag = POSIX_ACL_MASK;
                                                pctx->groupmasks++;
                                        } else {
                                                pxace->tag = POSIX_ACL_GROUP;
                                        }
                                        pxace->id = gid;
                                        pctx->prevgid = gid;
                                } else {
                                        /*
                                         * do not grant rights to unknown
                                         * people and do not define root as a
                                         * designated user or group
                                         */
                                        ignore = TRUE;
                                }
                        }
                }
                if (((pace->type == ACCESS_ALLOWED_ACE_TYPE)
                        || (pace->type == ACCESS_DENIED_ACE_TYPE))
                    && !ignore) {
                        pxace->perms = 0;
                                /* specific decoding for vtx/uid/gid */
                        if (pxace->tag == POSIX_ACL_SPECIAL) {
                                if (pace->mask & FILE_APPEND_DATA)
                                        pxace->perms |= S_ISUID;
                                if (pace->mask & FILE_WRITE_DATA)
                                        pxace->perms |= S_ISGID;
                                if (pace->mask & FILE_READ_DATA)
                                        pxace->perms |= S_ISVTX;
                        } else
                                if (isdir) {
                                        if (pace->mask & DIR_GEXEC)
                                                pxace->perms |= POSIX_PERM_X;
                                        if (pace->mask & DIR_GWRITE)
                                                pxace->perms |= POSIX_PERM_W;
                                        if (pace->mask & DIR_GREAD)
                                                pxace->perms |= POSIX_PERM_R;
                                        if ((pace->mask & GENERIC_ALL)
                                           && (pace->flags & INHERIT_ONLY_ACE))
                                                pxace->perms |= POSIX_PERM_X
                                                                | POSIX_PERM_W
                                                                | POSIX_PERM_R;
                                } else {
                                        if (pace->mask & FILE_GEXEC)
                                                pxace->perms |= POSIX_PERM_X;
                                        if (pace->mask & FILE_GWRITE)
                                                pxace->perms |= POSIX_PERM_W;
                                        if (pace->mask & FILE_GREAD)
                                                pxace->perms |= POSIX_PERM_R;
                                }

                        if (pace->type != ACCESS_ALLOWED_ACE_TYPE)
                                pxace->perms |= POSIX_PERM_DENIAL;
                        else
                                if (pxace->tag == POSIX_ACL_OTHER)
                                        pctx->permswrld |= pxace->perms;
                        pctx->tagsset |= pxace->tag;
                        if (pace->flags & INHERIT_ONLY_ACE) {
                                l--;
                        } else {
                                k++;
                        }
                }
                offace += le16_to_cpu(pace->size);
        }
                /*
                 * Create world perms if none (both lists)
                 */
        for (i=0; i<2; i++)
                if ((genericinh || !i)
                    && !(ctx[i].tagsset & POSIX_ACL_OTHER)) {
                        if (i)
                                pxace = &pxdesc->acl.ace[--l];
                        else
                                pxace = &pxdesc->acl.ace[k++];
                        pxace->tag = POSIX_ACL_OTHER;
                        pxace->id = -1;
                        pxace->perms = 0;
                        ctx[i].tagsset |= POSIX_ACL_OTHER;
                        ctx[i].permswrld = 0;
                }
                /*
                 * Set basic owner perms if none (both lists)
                 * This happens for files created by Windows in directories
                 * created by Linux and owned by root, because Windows
                 * merges the admin ACEs
                 */
        for (i=0; i<2; i++)
                if (!(ctx[i].tagsset & POSIX_ACL_USER_OBJ)
                  && (ctx[i].tagsset & POSIX_ACL_OTHER)) {
                        if (i)
                                pxace = &pxdesc->acl.ace[--l];
                        else
                                pxace = &pxdesc->acl.ace[k++];
                        pxace->tag = POSIX_ACL_USER_OBJ;
                        pxace->id = -1;
                        pxace->perms = POSIX_PERM_R | POSIX_PERM_W | POSIX_PERM_X;
                        ctx[i].tagsset |= POSIX_ACL_USER_OBJ;
                }
                /*
                 * Duplicate world perms as group_obj perms if none
                 */
        for (i=0; i<2; i++)
                if ((ctx[i].tagsset & POSIX_ACL_OTHER)
                    && !(ctx[i].tagsset & POSIX_ACL_GROUP_OBJ)) {
                        if (i)
                                pxace = &pxdesc->acl.ace[--l];
                        else
                                pxace = &pxdesc->acl.ace[k++];
                        pxace->tag = POSIX_ACL_GROUP_OBJ;
                        pxace->id = -1;
                        pxace->perms = ctx[i].permswrld;
                        ctx[i].tagsset |= POSIX_ACL_GROUP_OBJ;
                }
                /*
                 * Also duplicate world perms as group perms if they
                 * were converted to mask and not followed by a group entry
                 */
        if (ctx[0].groupmasks) {
                for (j=k-2; j>=0; j--) {
                        if ((pxdesc->acl.ace[j].tag == POSIX_ACL_MASK)
                           && (pxdesc->acl.ace[j].id != -1)
                           && ((pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP)
                             || (pxdesc->acl.ace[j+1].id
                                != pxdesc->acl.ace[j].id))) {
                                pxace = &pxdesc->acl.ace[k];
                                pxace->tag = POSIX_ACL_GROUP;
                                pxace->id = pxdesc->acl.ace[j].id;
                                pxace->perms = ctx[0].permswrld;
                                ctx[0].tagsset |= POSIX_ACL_GROUP;
                                k++;
                        }
                        if (pxdesc->acl.ace[j].tag == POSIX_ACL_MASK)
                                pxdesc->acl.ace[j].id = -1;
                }
        }
        if (ctx[1].groupmasks) {
                for (j=l; j<(alloccnt-1); j++) {
                        if ((pxdesc->acl.ace[j].tag == POSIX_ACL_MASK)
                           && (pxdesc->acl.ace[j].id != -1)
                           && ((pxdesc->acl.ace[j+1].tag != POSIX_ACL_GROUP)
                             || (pxdesc->acl.ace[j+1].id
                                != pxdesc->acl.ace[j].id))) {
                                pxace = &pxdesc->acl.ace[l - 1];
                                pxace->tag = POSIX_ACL_GROUP;
                                pxace->id = pxdesc->acl.ace[j].id;
                                pxace->perms = ctx[1].permswrld;
                                ctx[1].tagsset |= POSIX_ACL_GROUP;
                                l--;
                        }
                        if (pxdesc->acl.ace[j].tag == POSIX_ACL_MASK)
                                pxdesc->acl.ace[j].id = -1;
                }
        }

                /*
                 * Insert default mask if none present and
                 * there are designated users or groups
                 * (the space for it has not beed used)
                 */
        for (i=0; i<2; i++)
                if ((ctx[i].tagsset & (POSIX_ACL_USER | POSIX_ACL_GROUP))
                    && !(ctx[i].tagsset & POSIX_ACL_MASK)) {
                        if (i)
                                pxace = &pxdesc->acl.ace[--l];
                        else
                                pxace = &pxdesc->acl.ace[k++];
                        pxace->tag = POSIX_ACL_MASK;
                        pxace->id = -1;
                        pxace->perms = POSIX_PERM_DENIAL;
                        ctx[i].tagsset |= POSIX_ACL_MASK;
                }

        if (k > l) {
                ntfs_log_error("Posix descriptor is longer than expected\n");
                errno = EIO;
                free(pxdesc);
                pxdesc = (struct POSIX_SECURITY*)NULL;
        } else {
                pxdesc->acccnt = k;
                pxdesc->defcnt = alloccnt - l;
                pxdesc->firstdef = l;
                pxdesc->tagsset = ctx[0].tagsset;
                pxdesc->acl.version = POSIX_VERSION;
                pxdesc->acl.flags = 0;
                pxdesc->acl.filler = 0;
                ntfs_sort_posix(pxdesc);
                if (adminowns) {
                        k = norm_ownadmin_permissions_posix(pxdesc,
                                        0, pxdesc->acccnt, 0);
                        pxdesc->acccnt = k;
                        l = norm_ownadmin_permissions_posix(pxdesc,
                                        pxdesc->firstdef, pxdesc->defcnt, k);
                        pxdesc->firstdef = k;
                        pxdesc->defcnt = l;
                } else {
                        k = norm_std_permissions_posix(pxdesc,groupowns,
                                        0, pxdesc->acccnt, 0);
                        pxdesc->acccnt = k;
                        l = norm_std_permissions_posix(pxdesc,groupowns,
                                        pxdesc->firstdef, pxdesc->defcnt, k);
                        pxdesc->firstdef = k;
                        pxdesc->defcnt = l;
                }
        }
        if (pxdesc && !ntfs_valid_posix(pxdesc)) {
                ntfs_log_error("Invalid Posix descriptor built\n");
                errno = EIO;
                free(pxdesc);
                pxdesc = (struct POSIX_SECURITY*)NULL;
        }
        return (pxdesc);
}

#endif /* POSIXACLS */

/*
 *              Build unix-style (mode_t) permissions from an ACL
 *      returns the requested permissions
 *      or a negative result (with errno set) if there is a problem
 */

int ntfs_build_permissions(const char *securattr,
                        const SID *usid, const SID *gsid, BOOL isdir)
{
        int perm;
        BOOL adminowns;
        BOOL groupowns;

        adminowns = ntfs_same_sid(usid,adminsid)
                 || ntfs_same_sid(gsid,adminsid);
        groupowns = !adminowns && ntfs_same_sid(gsid,usid);
        if (adminowns)
                perm = build_ownadmin_permissions(securattr, usid, gsid, isdir);
        else
                if (groupowns)
                        perm = build_owngrp_permissions(securattr, usid, isdir);
                else
                        perm = build_std_permissions(securattr, usid, gsid, isdir);
        return (perm);
}

#ifndef __HAIKU__
/*
 *              The following must be in some library...
 */

static unsigned long atoul(const char *p)
{                               /* must be somewhere ! */
        unsigned long v;

        v = 0;
        while ((*p >= '0') && (*p <= '9'))
                v = v * 10 + (*p++) - '0';
        return (v);
}
#endif

/*
 *              Build an internal representation of a SID
 *      Returns a copy in allocated memory if it succeeds
 *      The SID is checked to be a valid user one.
 */

static SID *encodesid(const char *sidstr)
{
        SID *sid;
        int cnt;
        BIGSID bigsid;
        SID *bsid;
        u32 auth;
        const char *p;

        sid = (SID*) NULL;
        if (!strncmp(sidstr, "S-1-", 4)) {
                bsid = (SID*)&bigsid;
                bsid->revision = SID_REVISION;
                p = &sidstr[4];
                auth = atoul(p);
                bsid->identifier_authority.high_part = const_cpu_to_be16(0);
                bsid->identifier_authority.low_part = cpu_to_be32(auth);
                cnt = 0;
                p = strchr(p, '-');
                while (p && (cnt < 8)) {
                        p++;
                        auth = atoul(p);
                        bsid->sub_authority[cnt] = cpu_to_le32(auth);
                        p = strchr(p, '-');
                        cnt++;
                }
                bsid->sub_authority_count = cnt;
                if ((cnt > 0) && ntfs_valid_sid(bsid)
                    && (ntfs_is_user_sid(bsid) || ntfs_known_group_sid(bsid))) {
                        sid = (SID*) ntfs_malloc(4 * cnt + 8);
                        if (sid)
                                memcpy(sid, bsid, 4 * cnt + 8);
                }
        }
        return (sid);
}

/*
 *              Get a single mapping item from buffer
 *
 *      Always reads a full line, truncating long lines
 *      Refills buffer when exhausted
 *      Returns pointer to item, or NULL when there is no more
 */

static struct MAPLIST *getmappingitem(FILEREADER reader, void *fileid,
                off_t *poffs, char *buf, int *psrc, s64 *psize)
{
        int src;
        int dst;
        char *q;
        char *pu;
        char *pg;
        int gotend;
        struct MAPLIST *item;

        src = *psrc;
        dst = 0;
                        /* allocate and get a full line */
        item = (struct MAPLIST*)ntfs_malloc(sizeof(struct MAPLIST));
        if (item) {
                do {
                        gotend = 0;
                        while ((src < *psize)
                               && (buf[src] != '\n')) {
                                if (dst < LINESZ)
                                        item->maptext[dst++] = buf[src];
                                src++;
                        }
                        if (src >= *psize) {
                                *poffs += *psize;
                                *psize = reader(fileid, buf, (size_t)BUFSZ, *poffs);
                                src = 0;
                        } else {
                                gotend = 1;
                                src++;
                                item->maptext[dst] = '\0';
                                dst = 0;
                        }
                } while (*psize && ((item->maptext[0] == '#') || !gotend));
                if (gotend) {
                        pu = pg = (char*)NULL;
                        /* decompose into uid, gid and sid */
                        item->uidstr = item->maptext;
                        item->gidstr = strchr(item->uidstr, ':');
                        if (item->gidstr) {
                                pu = item->gidstr++;
                                item->sidstr = strchr(item->gidstr, ':');
                                if (item->sidstr) {
                                        pg = item->sidstr++;
                                        q = strchr(item->sidstr, ':');
                                        if (q) *q = 0;
                                }
                        }
                        if (pu && pg)
                                *pu = *pg = '\0';
                        else {
                                ntfs_log_early_error("Bad mapping item \"%s\"\n",
                                        item->maptext);
                                free(item);
                                item = (struct MAPLIST*)NULL;
                        }
                } else {
                        free(item);     /* free unused item */
                        item = (struct MAPLIST*)NULL;
                }
        }
        *psrc = src;
        return (item);
}

/*
 *              Read user mapping file and split into their attribute.
 *      Parameters are kept as text in a chained list until logins
 *      are converted to uid.
 *      Returns the head of list, if any
 *
 *      If an absolute path is provided, the mapping file is assumed
 *      to be located in another mounted file system, and plain read()
 *      are used to get its contents.
 *      If a relative path is provided, the mapping file is assumed
 *      to be located on the current file system, and internal IO
 *      have to be used since we are still mounting and we have not
 *      entered the fuse loop yet.
 */

struct MAPLIST *ntfs_read_mapping(FILEREADER reader, void *fileid)
{
        char buf[BUFSZ];
        struct MAPLIST *item;
        struct MAPLIST *firstitem;
        struct MAPLIST *lastitem;
        int src;
        off_t offs;
        s64 size;

        firstitem = (struct MAPLIST*)NULL;
        lastitem = (struct MAPLIST*)NULL;
        offs = 0;
        size = reader(fileid, buf, (size_t)BUFSZ, (off_t)0);
        if (size > 0) {
                src = 0;
                do {
                        item = getmappingitem(reader, fileid, &offs,
                                buf, &src, &size);
                        if (item) {
                                item->next = (struct MAPLIST*)NULL;
                                if (lastitem)
                                        lastitem->next = item;
                                else
                                        firstitem = item;
                                lastitem = item;
                        }
                } while (item);
        }
        return (firstitem);
}

/*
 *              Free memory used to store the user mapping
 *      The only purpose is to facilitate the detection of memory leaks
 */

void ntfs_free_mapping(struct MAPPING *mapping[])
{
        struct MAPPING *user;
        struct MAPPING *group;

                /* free user mappings */
        while (mapping[MAPUSERS]) {
                user = mapping[MAPUSERS];
                /* do not free SIDs used for group mappings */
                group = mapping[MAPGROUPS];
                while (group && (group->sid != user->sid))
                        group = group->next;
                if (!group)
                        free(user->sid);
                        /* free group list if any */
                if (user->grcnt)
                        free(user->groups);
                        /* unchain item and free */
                mapping[MAPUSERS] = user->next;
                free(user);
        }
                /* free group mappings */
        while (mapping[MAPGROUPS]) {
                group = mapping[MAPGROUPS];
                free(group->sid);
                        /* unchain item and free */
                mapping[MAPGROUPS] = group->next;
                free(group);
        }
}


/*
 *              Build the user mapping list
 *      user identification may be given in symbolic or numeric format
 *
 *      ! Note ! : does getpwnam() read /etc/passwd or some other file ?
 *              if so there is a possible recursion into fuse if this
 *              file is on NTFS, and fuse is not recursion safe.
 */

struct MAPPING *ntfs_do_user_mapping(struct MAPLIST *firstitem)
{
        struct MAPLIST *item;
        struct MAPPING *firstmapping;
        struct MAPPING *lastmapping;
        struct MAPPING *mapping;
        struct passwd *pwd;
        SID *sid;
        int uid;

        firstmapping = (struct MAPPING*)NULL;
        lastmapping = (struct MAPPING*)NULL;
        for (item = firstitem; item; item = item->next) {
                if ((item->uidstr[0] >= '0') && (item->uidstr[0] <= '9'))
                        uid = atoi(item->uidstr);
                else {
                        uid = 0;
                        if (item->uidstr[0]) {
                                pwd = getpwnam(item->uidstr);
                                if (pwd)
                                        uid = pwd->pw_uid;
                                else
                                        ntfs_log_early_error("Invalid user \"%s\"\n",
                                                item->uidstr);
                        }
                }
                        /*
                         * Records with no uid and no gid are inserted
                         * to define the implicit mapping pattern
                         */
                if (uid
                   || (!item->uidstr[0] && !item->gidstr[0])) {
                        sid = encodesid(item->sidstr);
                        if (sid && ntfs_known_group_sid(sid)) {
                                ntfs_log_error("Bad user SID %s\n",
                                        item->sidstr);
                                free(sid);
                                sid = (SID*)NULL;
                        }
                        if (sid && !item->uidstr[0] && !item->gidstr[0]
                            && !ntfs_valid_pattern(sid)) {
                                ntfs_log_error("Bad implicit SID pattern %s\n",
                                        item->sidstr);
                                sid = (SID*)NULL;
                                }
                        if (sid) {
                                mapping =
                                    (struct MAPPING*)
                                    ntfs_malloc(sizeof(struct MAPPING));
                                if (mapping) {
                                        mapping->sid = sid;
                                        mapping->xid = uid;
                                        mapping->grcnt = 0;
                                        mapping->next = (struct MAPPING*)NULL;
                                        if (lastmapping)
                                                lastmapping->next = mapping;
                                        else
                                                firstmapping = mapping;
                                        lastmapping = mapping;
                                }
                        }
                }
        }
        return (firstmapping);
}

/*
 *              Build the group mapping list
 *      group identification may be given in symbolic or numeric format
 *
 *      gid not associated to a uid are processed first in order
 *      to favour real groups
 *
 *      ! Note ! : does getgrnam() read /etc/group or some other file ?
 *              if so there is a possible recursion into fuse if this
 *              file is on NTFS, and fuse is not recursion safe.
 */

struct MAPPING *ntfs_do_group_mapping(struct MAPLIST *firstitem)
{
        struct MAPLIST *item;
        struct MAPPING *firstmapping;
        struct MAPPING *lastmapping;
        struct MAPPING *mapping;
        struct group *grp;
        BOOL secondstep;
        BOOL ok;
        int step;
        SID *sid;
        int gid;

        firstmapping = (struct MAPPING*)NULL;
        lastmapping = (struct MAPPING*)NULL;
        for (step=1; step<=2; step++) {
                for (item = firstitem; item; item = item->next) {
                        secondstep = (item->uidstr[0] != '\0')
                                || !item->gidstr[0];
                        ok = (step == 1 ? !secondstep : secondstep);
                        if ((item->gidstr[0] >= '0')
                             && (item->gidstr[0] <= '9'))
                                gid = atoi(item->gidstr);
                        else {
                                gid = 0;
                                if (item->gidstr[0]) {
                                        grp = getgrnam(item->gidstr);
                                        if (grp)
                                                gid = grp->gr_gid;
                                        else
                                                ntfs_log_early_error("Invalid group \"%s\"\n",
                                                        item->gidstr);
                                }
                        }
                        /*
                         * Records with no uid and no gid are inserted in the
                         * second step to define the implicit mapping pattern
                         */
                        if (ok
                            && (gid
                                 || (!item->uidstr[0] && !item->gidstr[0]))) {
                                sid = encodesid(item->sidstr);
                                if (sid && !item->uidstr[0] && !item->gidstr[0]
                                    && !ntfs_valid_pattern(sid)) {
                                        /* error already logged */
                                        sid = (SID*)NULL;
                                        }
                                if (sid) {
                                        mapping = (struct MAPPING*)
                                            ntfs_malloc(sizeof(struct MAPPING));
                                        if (mapping) {
                                                mapping->sid = sid;
                                                mapping->xid = gid;
                                        /* special groups point to themselves */
                                                if (ntfs_known_group_sid(sid)) {
                                                        mapping->groups =
                                                          (gid_t*)&mapping->xid;
                                                        mapping->grcnt = 1;
                                                } else
                                                        mapping->grcnt = 0;


                                                mapping->next = (struct MAPPING*)NULL;
                                                if (lastmapping)
                                                        lastmapping->next = mapping;
                                                else
                                                        firstmapping = mapping;
                                                lastmapping = mapping;
                                        }
                                }
                        }
                }
        }
        return (firstmapping);
}