#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <sys/xattr.h>
#include "selinux_internal.h"
#include "policy.h"

static int fsetxattr_wrapper(int fd, const char *name, const void *value,
			     size_t size, int flags)
{
	char buf[40];
	int rc, fd_flag, saved_errno = errno;

	rc = fsetxattr(fd, name, value, size, flags);
	if (rc == 0 || errno != EBADF)
		return rc;

	/* Emulate O_PATH support */
	fd_flag = fcntl(fd, F_GETFL);
	if (fd_flag == -1 || (fd_flag & O_PATH) == 0) {
		errno = EBADF;
		return -1;
	}

	snprintf(buf, sizeof(buf), "/proc/self/fd/%d", fd);
	errno = saved_errno;
	rc = setxattr(buf, name, value, size, flags);
	if (rc < 0 && errno == ENOENT)
		errno = EBADF;
	return rc;
}

int fsetfilecon_raw(int fd, const char *context)
{
	int rc = fsetxattr_wrapper(fd, XATTR_NAME_SELINUX, context,
				   strlen(context) + 1, 0);
	if (rc < 0 && errno == ENOTSUP) {
		char *ccontext = NULL;
		int err = errno;
		if ((fgetfilecon_raw(fd, &ccontext) >= 0) &&
		    (strcmp(context, ccontext) == 0)) {
			rc = 0;
		} else {
			errno = err;
		}
		freecon(ccontext);
	}
	return rc;
}

int fsetfilecon(int fd, const char *context)
{
	int ret;
	char *rcontext;

	if (selinux_trans_to_raw_context(context, &rcontext))
		return -1;

	ret = fsetfilecon_raw(fd, rcontext);

	freecon(rcontext);

	return ret;
}
