#include <sys/types.h>
#include <sys/acl.h>
#include <sys/stat.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdio.h>
#define ACL_MIN_SIZE_HEURISTIC (sizeof(struct kauth_filesec) + 16 * sizeof(struct kauth_ace))
static int statx_syscall(void *obj, struct stat *sb, void *fsacl, size_t *fsacl_size);
static int fstatx_syscall(void *obj, struct stat *sb, void *fsacl, size_t *fsacl_size);
static int lstatx_syscall(void *obj, struct stat *sb, void *fsacl, size_t *fsacl_size);
static int statx1(void *obj,
int (* stat_syscall)(void *obj, struct stat *sb, void *fsacl, size_t *fsacl_size),
struct stat *sb, filesec_t fsec);
int
statx_np(const char *path, struct stat *sb, filesec_t fsec)
{
if (fsec == NULL)
return(stat(path, sb));
return(statx1((void *)&path, statx_syscall, sb, fsec));
}
int
fstatx_np(int fd, struct stat *sb, filesec_t fsec)
{
if (fsec == NULL)
return(fstat(fd, sb));
return(statx1((void *)&fd, fstatx_syscall, sb, fsec));
}
int
lstatx_np(const char *path, struct stat *sb, filesec_t fsec)
{
if (fsec == NULL)
return(lstat(path, sb));
return(statx1((void *)&path, lstatx_syscall, sb, fsec));
}
static int
statx_syscall(void *obj, struct stat *sb, void *fsacl, size_t *fsacl_size)
{
const char *path = *(const char **)obj;
return(syscall(SYS_stat_extended, path, sb, fsacl, fsacl_size));
}
static int
fstatx_syscall(void *obj, struct stat *sb, void *fsacl, size_t *fsacl_size)
{
int fd = *(int *)obj;
return(syscall(SYS_fstat_extended, fd, sb, fsacl, fsacl_size));
}
static int
lstatx_syscall(void *obj, struct stat *sb, void *fsacl, size_t *fsacl_size)
{
const char *path = *(const char **)obj;
return(syscall(SYS_lstat_extended, path, sb, fsacl, fsacl_size));
}
static int
statx1(void *obj,
int (* stat_syscall)(void *obj, struct stat *sb, void *fsacl, size_t *fsacl_size),
struct stat *sb, filesec_t fsec)
{
struct kauth_filesec *fsacl, *ofsacl;
size_t fsacl_size, buffer_size;
int error;
fsacl = NULL;
error = 0;
if ((fsacl = malloc(ACL_MIN_SIZE_HEURISTIC)) == NULL) {
error = ENOMEM;
goto out;
}
buffer_size = ACL_MIN_SIZE_HEURISTIC;
for (;;) {
fsacl_size = buffer_size;
if ((error = stat_syscall(obj, sb, fsacl, &fsacl_size)) != 0)
goto out;
if (fsacl_size <= buffer_size)
break;
ofsacl = fsacl;
fsacl = realloc(fsacl, fsacl_size + sizeof(struct kauth_ace) * 2);
if (fsacl == NULL) {
fsacl = ofsacl;
errno = ENOMEM;
goto out;
}
buffer_size = fsacl_size;
}
filesec_set_property(fsec, FILESEC_OWNER, &(sb->st_uid));
filesec_set_property(fsec, FILESEC_GROUP, &(sb->st_gid));
filesec_set_property(fsec, FILESEC_MODE, &(sb->st_mode));
if (fsacl_size >= sizeof(struct kauth_filesec)) {
filesec_set_property(fsec, FILESEC_UUID, &fsacl->fsec_owner);
filesec_set_property(fsec, FILESEC_GRPUUID, &fsacl->fsec_group);
if (fsacl->fsec_acl.acl_entrycount != KAUTH_FILESEC_NOACL) {
filesec_set_property(fsec, FILESEC_ACL_ALLOCSIZE, &fsacl_size);
filesec_set_property(fsec, FILESEC_ACL_RAW, &fsacl);
fsacl = NULL;
} else {
filesec_set_property(fsec, FILESEC_ACL_ALLOCSIZE, NULL);
filesec_set_property(fsec, FILESEC_ACL_RAW, NULL);
}
} else {
filesec_set_property(fsec, FILESEC_UUID, NULL);
filesec_set_property(fsec, FILESEC_GRPUUID, NULL);
filesec_set_property(fsec, FILESEC_ACL_ALLOCSIZE, NULL);
filesec_set_property(fsec, FILESEC_ACL_RAW, NULL);
}
out:
if (fsacl != NULL)
free(fsacl);
return(error);
}