/*
 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */

#include <sys/appleapiopts.h>
#include <sys/types.h>
#include <sys/acl.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include "aclvar.h"

int
acl_copy_entry(acl_entry_t dest, acl_entry_t src)
{
	/* validate arguments */
	_ACL_VALIDATE_ENTRY(dest);
	_ACL_VALIDATE_ENTRY(src);
	if (dest == src) {
		errno = EINVAL;
		return(-1);
	}
	bcopy(src, dest, sizeof(*src));
	return(0);
}

int
acl_create_entry_np(acl_t *acl_p, acl_entry_t *entry_p, int index)
{
	struct _acl	*ap = *acl_p;
	int		i;

	/* validate arguments */
	_ACL_VALIDATE_ACL(ap);
	if (ap->a_entries >= ACL_MAX_ENTRIES) {
		errno = ENOMEM;
		return(-1);
	}
	if (index == ACL_LAST_ENTRY)
		index = ap->a_entries;
	if (index > ap->a_entries) {
		errno = ERANGE;
		return(-1);
	}

	/* move following entries out of the way */
	for (i = ap->a_entries; i > index; i--)
		ap->a_ace[i] = ap->a_ace[i - 1];
	ap->a_entries++;

	/* initialise new entry */
	ap->a_ace[index].ae_magic = _ACL_ENTRY_MAGIC;
	ap->a_ace[index].ae_tag = ACL_UNDEFINED_TAG;

	*entry_p = &ap->a_ace[index];
	return(0);
}

int
acl_create_entry(acl_t *acl_p, acl_entry_t *entry_p)
{
	return(acl_create_entry_np(acl_p, entry_p, ACL_LAST_ENTRY));
}

int
acl_delete_entry(acl_t acl, acl_entry_t entry)
{
	int	i;

	_ACL_VALIDATE_ACL(acl);
	_ACL_VALIDATE_ENTRY(entry);
	_ACL_VALIDATE_ENTRY_CONTAINED(acl, entry);

	/* copy following entries down & invalidate last slot */
	acl->a_entries--;
	for (i = entry - &acl->a_ace[0]; i < acl->a_entries; i++)
		acl->a_ace[i] = acl->a_ace[i + 1];
	acl->a_ace[acl->a_entries].ae_magic = 0;
	/* Sync up the iterator's position if necessary */
	if (acl->a_last_get >= (entry - &acl->a_ace[0]))
	  acl->a_last_get--;

	return(0);
}

int
acl_get_entry(acl_t acl, int entry_id, acl_entry_t *entry_p)
{

	_ACL_VALIDATE_ACL(acl);
	if ((entry_id != ACL_FIRST_ENTRY) &&
	    (entry_id != ACL_NEXT_ENTRY) &&
	    (entry_id != ACL_LAST_ENTRY) &&
	    ((entry_id < 0) || (entry_id >= acl->a_entries))) {
		errno = EINVAL;
		return(-1);
	}
	if (entry_id == ACL_FIRST_ENTRY)
	  entry_id = 0;
	else
	  if (entry_id == ACL_NEXT_ENTRY) {
	    entry_id = acl->a_last_get + 1;
	  }
	  else
	    if (entry_id == ACL_LAST_ENTRY)
	      entry_id = acl->a_entries - 1;

	if (entry_id >= acl->a_entries) {
	  errno = EINVAL;
	  return (-1);
	}

	*entry_p = &acl->a_ace[entry_id];
	acl->a_last_get = entry_id;

	return(0);
}

void *
acl_get_qualifier(acl_entry_t entry)
{
	acl_tag_t	tag_type;
	void		*result;
	int		error;

	result = NULL;
	if (!_ACL_VALID_ENTRY(entry)) {
		errno = EINVAL;
	} else if ((error = acl_get_tag_type(entry, &tag_type)) != 0) {
		/* errno is set by acl_get_tag_type */
	} else {
		switch(tag_type) {
		case ACL_EXTENDED_ALLOW:
		case ACL_EXTENDED_DENY:
			if ((result = malloc(sizeof(guid_t))) != NULL)
				bcopy(&entry->ae_applicable, result, sizeof(guid_t));
			break;
		default:
			errno = EINVAL;
			break;
		}
	}
	return(result);
}

int
acl_get_tag_type(acl_entry_t entry, acl_tag_t *tag_type_p)
{
	_ACL_VALIDATE_ENTRY(entry);

	*tag_type_p = entry->ae_tag;
	return(0);
}

int
acl_set_qualifier(acl_entry_t entry, const void *tag_qualifier_p)
{
	acl_tag_t	tag_type;
	int		error;

	_ACL_VALIDATE_ENTRY(entry);
	if ((error = acl_get_tag_type(entry, &tag_type)) != 0)
		return(error);

	switch(tag_type) {
	case ACL_EXTENDED_ALLOW:
	case ACL_EXTENDED_DENY:
		bcopy(tag_qualifier_p, &entry->ae_applicable, sizeof(guid_t));
		error = 0;
		break;
	default:
		error = EINVAL;
	}
	return(error);
}

int
acl_set_tag_type(acl_entry_t entry, acl_tag_t tag_type)
{
	_ACL_VALIDATE_ENTRY(entry);

	switch(tag_type) {
	case ACL_EXTENDED_ALLOW:
	case ACL_EXTENDED_DENY:
		entry->ae_tag = tag_type;
		break;
	default:
		return(EINVAL);
	}
	return(0);
}
