linux-user: Implement capget, capset
Implement the capget and capset syscalls. This is useful because simple programs like 'ls' try to use it in AArch64, and otherwise we emit a lot of noise about it being unimplemented. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Riku Voipio <riku.voipio@linaro.org>
This commit is contained in:
parent
a7ec0f98e3
commit
e0eb210ec0
|
@ -43,6 +43,7 @@
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <sys/swap.h>
|
#include <sys/swap.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <sched.h>
|
#include <sched.h>
|
||||||
#ifdef __ia64__
|
#ifdef __ia64__
|
||||||
|
@ -243,6 +244,10 @@ _syscall3(int, sys_sched_setaffinity, pid_t, pid, unsigned int, len,
|
||||||
unsigned long *, user_mask_ptr);
|
unsigned long *, user_mask_ptr);
|
||||||
_syscall4(int, reboot, int, magic1, int, magic2, unsigned int, cmd,
|
_syscall4(int, reboot, int, magic1, int, magic2, unsigned int, cmd,
|
||||||
void *, arg);
|
void *, arg);
|
||||||
|
_syscall2(int, capget, struct __user_cap_header_struct *, header,
|
||||||
|
struct __user_cap_data_struct *, data);
|
||||||
|
_syscall2(int, capset, struct __user_cap_header_struct *, header,
|
||||||
|
struct __user_cap_data_struct *, data);
|
||||||
|
|
||||||
static bitmask_transtbl fcntl_flags_tbl[] = {
|
static bitmask_transtbl fcntl_flags_tbl[] = {
|
||||||
{ TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, },
|
{ TARGET_O_ACCMODE, TARGET_O_WRONLY, O_ACCMODE, O_WRONLY, },
|
||||||
|
@ -7677,9 +7682,75 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1,
|
||||||
unlock_user(p, arg1, ret);
|
unlock_user(p, arg1, ret);
|
||||||
break;
|
break;
|
||||||
case TARGET_NR_capget:
|
case TARGET_NR_capget:
|
||||||
goto unimplemented;
|
|
||||||
case TARGET_NR_capset:
|
case TARGET_NR_capset:
|
||||||
goto unimplemented;
|
{
|
||||||
|
struct target_user_cap_header *target_header;
|
||||||
|
struct target_user_cap_data *target_data = NULL;
|
||||||
|
struct __user_cap_header_struct header;
|
||||||
|
struct __user_cap_data_struct data[2];
|
||||||
|
struct __user_cap_data_struct *dataptr = NULL;
|
||||||
|
int i, target_datalen;
|
||||||
|
int data_items = 1;
|
||||||
|
|
||||||
|
if (!lock_user_struct(VERIFY_WRITE, target_header, arg1, 1)) {
|
||||||
|
goto efault;
|
||||||
|
}
|
||||||
|
header.version = tswap32(target_header->version);
|
||||||
|
header.pid = tswap32(target_header->pid);
|
||||||
|
|
||||||
|
if (header.version != _LINUX_CAPABILITY_VERSION_1) {
|
||||||
|
/* Version 2 and up takes pointer to two user_data structs */
|
||||||
|
data_items = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_datalen = sizeof(*target_data) * data_items;
|
||||||
|
|
||||||
|
if (arg2) {
|
||||||
|
if (num == TARGET_NR_capget) {
|
||||||
|
target_data = lock_user(VERIFY_WRITE, arg2, target_datalen, 0);
|
||||||
|
} else {
|
||||||
|
target_data = lock_user(VERIFY_READ, arg2, target_datalen, 1);
|
||||||
|
}
|
||||||
|
if (!target_data) {
|
||||||
|
unlock_user_struct(target_header, arg1, 0);
|
||||||
|
goto efault;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num == TARGET_NR_capset) {
|
||||||
|
for (i = 0; i < data_items; i++) {
|
||||||
|
data[i].effective = tswap32(target_data[i].effective);
|
||||||
|
data[i].permitted = tswap32(target_data[i].permitted);
|
||||||
|
data[i].inheritable = tswap32(target_data[i].inheritable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataptr = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num == TARGET_NR_capget) {
|
||||||
|
ret = get_errno(capget(&header, dataptr));
|
||||||
|
} else {
|
||||||
|
ret = get_errno(capset(&header, dataptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The kernel always updates version for both capget and capset */
|
||||||
|
target_header->version = tswap32(header.version);
|
||||||
|
unlock_user_struct(target_header, arg1, 1);
|
||||||
|
|
||||||
|
if (arg2) {
|
||||||
|
if (num == TARGET_NR_capget) {
|
||||||
|
for (i = 0; i < data_items; i++) {
|
||||||
|
target_data[i].effective = tswap32(data[i].effective);
|
||||||
|
target_data[i].permitted = tswap32(data[i].permitted);
|
||||||
|
target_data[i].inheritable = tswap32(data[i].inheritable);
|
||||||
|
}
|
||||||
|
unlock_user(target_data, arg2, target_datalen);
|
||||||
|
} else {
|
||||||
|
unlock_user(target_data, arg2, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case TARGET_NR_sigaltstack:
|
case TARGET_NR_sigaltstack:
|
||||||
#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_MIPS) || \
|
#if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_MIPS) || \
|
||||||
defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_ALPHA) || \
|
defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_ALPHA) || \
|
||||||
|
|
|
@ -2566,3 +2566,14 @@ struct target_sigevent {
|
||||||
} _sigev_thread;
|
} _sigev_thread;
|
||||||
} _sigev_un;
|
} _sigev_un;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct target_user_cap_header {
|
||||||
|
uint32_t version;
|
||||||
|
int pid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct target_user_cap_data {
|
||||||
|
uint32_t effective;
|
||||||
|
uint32_t permitted;
|
||||||
|
uint32_t inheritable;
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in a new issue