Message ID | e0eb210ec0c1cd262e3f642133ee93acdaf60aa0.1395237255.git.riku.voipio@linaro.org |
---|---|
State | Accepted |
Commit | e0eb210ec0c1cd262e3f642133ee93acdaf60aa0 |
Headers | show |
Hello, On Wed, Mar 19, 2014 at 3:03 PM, <riku.voipio@linaro.org> wrote: > From: Peter Maydell <peter.maydell@linaro.org> > > 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> > --- > linux-user/syscall.c | 75 +++++++++++++++++++++++++++++++++++++++++++++-- > linux-user/syscall_defs.h | 11 +++++++ > 2 files changed, 84 insertions(+), 2 deletions(-) > > diff --git a/linux-user/syscall.c b/linux-user/syscall.c > index e404a32..366b695 100644 > --- a/linux-user/syscall.c > +++ b/linux-user/syscall.c > @@ -43,6 +43,7 @@ > #include <sys/resource.h> > #include <sys/mman.h> > #include <sys/swap.h> > +#include <linux/capability.h> > #include <signal.h> > #include <sched.h> > #ifdef __ia64__ > @@ -243,6 +244,10 @@ _syscall3(int, sys_sched_setaffinity, pid_t, pid, unsigned int, len, > unsigned long *, user_mask_ptr); > _syscall4(int, reboot, int, magic1, int, magic2, unsigned int, cmd, > 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[] = { > { 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); > break; > case TARGET_NR_capget: > - goto unimplemented; > 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) { Sorry for spotting this late, but older kernels (in my case 2.6.18-238.el5, CentOS 5.6) don't have _LINUX_CAPABILITY_VERSION_1 defined. I think _LINUX_CAPABILITY_VERSION should be used instead in that case. Thanks, Laurent > + /* 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: > #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_MIPS) || \ > defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_ALPHA) || \ > diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h > index 2a7d1db..fdf9a47 100644 > --- a/linux-user/syscall_defs.h > +++ b/linux-user/syscall_defs.h > @@ -2566,3 +2566,14 @@ struct target_sigevent { > } _sigev_thread; > } _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; > +}; > -- > 1.8.1.2 > >
On 19 March 2014 15:10, Laurent Desnogues <laurent.desnogues@gmail.com> wrote: > Hello, > > On Wed, Mar 19, 2014 at 3:03 PM, <riku.voipio@linaro.org> wrote: >> From: Peter Maydell <peter.maydell@linaro.org> >> >> + if (header.version != _LINUX_CAPABILITY_VERSION_1) { > > Sorry for spotting this late, but older kernels (in my case > 2.6.18-238.el5, CentOS 5.6) don't have > _LINUX_CAPABILITY_VERSION_1 defined. Oops. > I think > _LINUX_CAPABILITY_VERSION should be used instead > in that case. Simplest is to just use the non _1 suffix version always; later kernels define that for source compatibility. thanks -- PMM
diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e404a32..366b695 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -43,6 +43,7 @@ #include <sys/resource.h> #include <sys/mman.h> #include <sys/swap.h> +#include <linux/capability.h> #include <signal.h> #include <sched.h> #ifdef __ia64__ @@ -243,6 +244,10 @@ _syscall3(int, sys_sched_setaffinity, pid_t, pid, unsigned int, len, unsigned long *, user_mask_ptr); _syscall4(int, reboot, int, magic1, int, magic2, unsigned int, cmd, 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[] = { { 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); break; case TARGET_NR_capget: - goto unimplemented; 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: #if defined(TARGET_I386) || defined(TARGET_ARM) || defined(TARGET_MIPS) || \ defined(TARGET_SPARC) || defined(TARGET_PPC) || defined(TARGET_ALPHA) || \ diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 2a7d1db..fdf9a47 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -2566,3 +2566,14 @@ struct target_sigevent { } _sigev_thread; } _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; +};