/*
 * Copyright (c) 1995 Sun Microsystems, Inc.  All Rights Reserved
 *
 * module:
 *	acls.c
 *
 * purpose:
 * 	routines to manipulate access control lists, mapping between
 *	the data structures required by the filesystem ACL system calls
 *	and the representation used in our fileinfo structure.
 *
 */
#ident	"@(#)acls.c	1.2	96/03/22 SMI"

#include <stdio.h>
#include <stdlib.h>

#include "filesync.h"
#include "database.h"

#ifdef NO_ACLS
/*
 * Solaris 2.4 libc.so does not contain this entry point, so if we
 * want to build a 2.4 version of filesync, we need to provide a
 * dummy entry point that will fail when-ever it is called.
 */
#define	acl	bogus_acl

static int acl(const char *name, int opcode, int count, aclent_t *acls)
{
	return (-1);
}
#endif

/*
 * routine:
 *	get_acls
 *
 * purpose:
 *	to read the ACL (if any) from a file into a fileinfo structure
 *
 * parameters:
 *	name of file
 * 	pointer to fileinfo structure
 *
 * returns:
 *	number of ACL entries
 */
int
get_acls(const char *name, struct fileinfo *ip)
{	int count;
	int i;
	static aclent_t acls[MAX_ACL_ENTRIES];
	aclent_t *list;

	count = acl(name, GETACL, MAX_ACL_ENTRIES, acls);
	if (count <= 0)
		return (0);

	/* with a count of 3 or 4 there may not be any real ones */
	if (count > 4)
		goto gotsome;

	/* look for anything beyond the normal unix protection	*/
	for (i = 0; i < count; i++)
		switch (acls[i].a_type) {
			default:	/* weird types are real */
				goto gotsome;

			case USER_OBJ:
			case GROUP_OBJ:
			case OTHER_OBJ:
			case CLASS_OBJ:
				continue; /* all file have these */
		}

	return (0);	/* nothing interesting	*/

gotsome:
	/* allocate an array to hold the acls		*/
	list = (aclent_t *) malloc(count * sizeof (*list));
	if (list == 0)
		nomem("Access Control List");

	/* copy the acls into the new list		*/
	for (i = 0; i < count; i++) {
		list[i].a_type = acls[i].a_type;
		list[i].a_id = acls[i].a_id;
		list[i].a_perm = acls[i].a_perm;
	}

	ip->f_acls = list;
	ip->f_numacls = count;
	return (ip->f_numacls);
}

/*
 * routine:
 *	cmp_acls
 *
 * purpose:
 *	determine whether or not two ACLs are the same
 *
 * parameters:
 *	pointer to first fileinfo
 *	pointer to second fileinfo
 *
 * returns:
 *	true 	equal
 *	false	different
 */
int
cmp_acls(struct fileinfo *f1, struct fileinfo *f2)
{	int i;

	if (f1->f_numacls != f2->f_numacls)
		return (0);

	if (f1->f_numacls == 0)
		return (1);

	for (i = 0; i < f1->f_numacls; i++) {
		if (f1->f_acls[i].a_type != f2->f_acls[i].a_type)
			return (0);
		if (f1->f_acls[i].a_id != f2->f_acls[i].a_id)
			return (0);
		if (f1->f_acls[i].a_perm != f2->f_acls[i].a_perm)
			return (0);
	}

	return (1);
}

/*
 * routine:
 *	set_acls
 *
 * purpose:
 *	to write the ACL of a file
 *
 * parameters:
 *	name of file
 *	fileinfo pointer (which contains an acl pointer)
 *
 * returns:
 *	retcode and errno
 */
int
set_acls(const char *name, struct fileinfo *fp)
{	int rc;
	int nacl;
	aclent_t acls[4], *list;

	if (fp->f_numacls == 0) {
		/* fabricate a standard set of bogus ACLs */
		acls[0].a_type = USER_OBJ;
		acls[0].a_id = fp->f_uid;
		acls[0].a_perm = (fp->f_mode >> 6) & 7;

		acls[1].a_type = GROUP_OBJ;
		acls[1].a_id = fp->f_gid;
		acls[1].a_perm = (fp->f_mode >> 3) & 7;

		acls[2].a_type = CLASS_OBJ;
		acls[2].a_id = 0;
		acls[2].a_perm = (fp->f_mode >> 6) & 7;

		acls[3].a_type = OTHER_OBJ;
		acls[3].a_id = 0;
		acls[3].a_perm = fp->f_mode & 7;

		nacl = 4;
		list = acls;
	} else {
		nacl = fp->f_numacls;
		list = fp->f_acls;
	}

	rc = acl(name, SETACL, nacl, list);

	/* non-negative number mean success		*/
	if (rc < 0)
		return (rc);
	else
		return (0);
}

/*
 * routine:
 *	show_acls
 *
 * purpose:
 *	to map an acl into arguments for a setfacl command
 *
 * paramters:
 *	number of elements in list
 *	pointer to list
 *
 * returns:
 *	pointer to character buffer containing arguments
 */
char
*show_acls(int numacl, aclent_t *list)
{	int i, j;
	int type, perm, id;
	char *s;
	static char buf[ MAX_LINE ];

	s = buf;

	if (numacl > 0) {
		*s++ = '-';
		*s++ = 's';
		*s++ = ' ';
	} else {
		*s++ = '-';
		*s++ = 'd';
	}

	for (i = 0; i < numacl; i++) {
		type = list[i].a_type;
		id = list[i].a_id;
		perm = list[i].a_perm;

		if (i > 0)
			*s++ = ',';

		/* note whether this is per-file or default	*/
		if (type & ACL_DEFAULT) {
			*s++ = 'd';
			*s++ = ':';
		}

		/* print out the entry type			*/
		if (type & (USER_OBJ|USER)) {
			*s++ = 'u';
			*s++ = ':';
		} else if (type & (GROUP_OBJ|GROUP)) {
			*s++ = 'g';
			*s++ = ':';
		} else if (type & OTHER_OBJ) {
			*s++ = 'o';
			*s++ = ':';
		} else if (type & CLASS_OBJ) {
			*s++ = 'm';
			*s++ = ':';
		}

		/* print out the ID for this ACL		*/
		if (type & (USER_OBJ|GROUP_OBJ))
			*s++ = ':';
		else if (type & (USER|GROUP)) {
			for (j = 1; id/j > 10; j *= 10);

			while (j > 0) {
				*s++ = '0' + (id/j);
				id %= j*10;
				j /= 10;
			}

			*s++ = ':';
		}

		/* print out the permissions for this ACL	*/
		*s++ = (perm & 04) ? 'r' : '-';
		*s++ = (perm & 02) ? 'w' : '-';
		*s++ = (perm & 01) ? 'x' : '-';
	}

	*s = 0;
	return (buf);
}
