linux-user: Implement FS_IOC_FIEMAP ioctl
Implement the FS_IOC_FIEMAP ioctl using the new support for custom handling of ioctls; this is needed because the struct that is passed includes a variable-length array. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Riku Voipio <riku.voipio@iki.fi>
This commit is contained in:
parent
d2ef05bb44
commit
285da2b9a8
@ -76,6 +76,10 @@
|
||||
#ifdef FIGETBSZ
|
||||
IOCTL(FIGETBSZ, IOC_R, MK_PTR(TYPE_LONG))
|
||||
#endif
|
||||
#ifdef FS_IOC_FIEMAP
|
||||
IOCTL_SPECIAL(FS_IOC_FIEMAP, IOC_W | IOC_R, do_ioctl_fs_ioc_fiemap,
|
||||
MK_PTR(MK_STRUCT(STRUCT_fiemap)))
|
||||
#endif
|
||||
|
||||
IOCTL(SIOCATMARK, 0, TYPE_NULL)
|
||||
IOCTL(SIOCADDRT, IOC_W, MK_PTR(MK_STRUCT(STRUCT_rtentry)))
|
||||
|
@ -83,6 +83,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base,
|
||||
#include <linux/kd.h>
|
||||
#include <linux/mtio.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/fiemap.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/vt.h>
|
||||
#include "linux_loop.h"
|
||||
@ -2985,6 +2986,93 @@ struct IOCTLEntry {
|
||||
|
||||
#define MAX_STRUCT_SIZE 4096
|
||||
|
||||
/* So fiemap access checks don't overflow on 32 bit systems.
|
||||
* This is very slightly smaller than the limit imposed by
|
||||
* the underlying kernel.
|
||||
*/
|
||||
#define FIEMAP_MAX_EXTENTS ((UINT_MAX - sizeof(struct fiemap)) \
|
||||
/ sizeof(struct fiemap_extent))
|
||||
|
||||
static abi_long do_ioctl_fs_ioc_fiemap(const IOCTLEntry *ie, uint8_t *buf_temp,
|
||||
int fd, abi_long cmd, abi_long arg)
|
||||
{
|
||||
/* The parameter for this ioctl is a struct fiemap followed
|
||||
* by an array of struct fiemap_extent whose size is set
|
||||
* in fiemap->fm_extent_count. The array is filled in by the
|
||||
* ioctl.
|
||||
*/
|
||||
int target_size_in, target_size_out;
|
||||
struct fiemap *fm;
|
||||
const argtype *arg_type = ie->arg_type;
|
||||
const argtype extent_arg_type[] = { MK_STRUCT(STRUCT_fiemap_extent) };
|
||||
void *argptr, *p;
|
||||
abi_long ret;
|
||||
int i, extent_size = thunk_type_size(extent_arg_type, 0);
|
||||
uint32_t outbufsz;
|
||||
int free_fm = 0;
|
||||
|
||||
assert(arg_type[0] == TYPE_PTR);
|
||||
assert(ie->access == IOC_RW);
|
||||
arg_type++;
|
||||
target_size_in = thunk_type_size(arg_type, 0);
|
||||
argptr = lock_user(VERIFY_READ, arg, target_size_in, 1);
|
||||
if (!argptr) {
|
||||
return -TARGET_EFAULT;
|
||||
}
|
||||
thunk_convert(buf_temp, argptr, arg_type, THUNK_HOST);
|
||||
unlock_user(argptr, arg, 0);
|
||||
fm = (struct fiemap *)buf_temp;
|
||||
if (fm->fm_extent_count > FIEMAP_MAX_EXTENTS) {
|
||||
return -TARGET_EINVAL;
|
||||
}
|
||||
|
||||
outbufsz = sizeof (*fm) +
|
||||
(sizeof(struct fiemap_extent) * fm->fm_extent_count);
|
||||
|
||||
if (outbufsz > MAX_STRUCT_SIZE) {
|
||||
/* We can't fit all the extents into the fixed size buffer.
|
||||
* Allocate one that is large enough and use it instead.
|
||||
*/
|
||||
fm = malloc(outbufsz);
|
||||
if (!fm) {
|
||||
return -TARGET_ENOMEM;
|
||||
}
|
||||
memcpy(fm, buf_temp, sizeof(struct fiemap));
|
||||
free_fm = 1;
|
||||
}
|
||||
ret = get_errno(ioctl(fd, ie->host_cmd, fm));
|
||||
if (!is_error(ret)) {
|
||||
target_size_out = target_size_in;
|
||||
/* An extent_count of 0 means we were only counting the extents
|
||||
* so there are no structs to copy
|
||||
*/
|
||||
if (fm->fm_extent_count != 0) {
|
||||
target_size_out += fm->fm_mapped_extents * extent_size;
|
||||
}
|
||||
argptr = lock_user(VERIFY_WRITE, arg, target_size_out, 0);
|
||||
if (!argptr) {
|
||||
ret = -TARGET_EFAULT;
|
||||
} else {
|
||||
/* Convert the struct fiemap */
|
||||
thunk_convert(argptr, fm, arg_type, THUNK_TARGET);
|
||||
if (fm->fm_extent_count != 0) {
|
||||
p = argptr + target_size_in;
|
||||
/* ...and then all the struct fiemap_extents */
|
||||
for (i = 0; i < fm->fm_mapped_extents; i++) {
|
||||
thunk_convert(p, &fm->fm_extents[i], extent_arg_type,
|
||||
THUNK_TARGET);
|
||||
p += extent_size;
|
||||
}
|
||||
}
|
||||
unlock_user(argptr, arg, target_size_out);
|
||||
}
|
||||
}
|
||||
if (free_fm) {
|
||||
free(fm);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static IOCTLEntry ioctl_entries[] = {
|
||||
#define IOCTL(cmd, access, ...) \
|
||||
{ TARGET_ ## cmd, cmd, #cmd, access, 0, { __VA_ARGS__ } },
|
||||
|
@ -783,6 +783,7 @@ struct target_pollfd {
|
||||
#define TARGET_BLKGETSIZE64 TARGET_IOR(0x12,114,sizeof(uint64_t)) /* return device size in bytes (u64 *arg) */
|
||||
#define TARGET_FIBMAP TARGET_IO(0x00,1) /* bmap access */
|
||||
#define TARGET_FIGETBSZ TARGET_IO(0x00,2) /* get the block size used for bmap */
|
||||
#define TARGET_FS_IOC_FIEMAP TARGET_IOWR('f',11,struct fiemap)
|
||||
|
||||
/* cdrom commands */
|
||||
#define TARGET_CDROMPAUSE 0x5301 /* Pause Audio Operation */
|
||||
|
@ -165,3 +165,19 @@ STRUCT(vt_stat,
|
||||
TYPE_SHORT, /* v_active */
|
||||
TYPE_SHORT, /* v_signal */
|
||||
TYPE_SHORT) /* v_state */
|
||||
|
||||
STRUCT(fiemap_extent,
|
||||
TYPE_ULONGLONG, /* fe_logical */
|
||||
TYPE_ULONGLONG, /* fe_physical */
|
||||
TYPE_ULONGLONG, /* fe_length */
|
||||
MK_ARRAY(TYPE_ULONGLONG, 2), /* fe_reserved64[2] */
|
||||
TYPE_INT, /* fe_flags */
|
||||
MK_ARRAY(TYPE_INT, 3)) /* fe_reserved[3] */
|
||||
|
||||
STRUCT(fiemap,
|
||||
TYPE_ULONGLONG, /* fm_start */
|
||||
TYPE_ULONGLONG, /* fm_length */
|
||||
TYPE_INT, /* fm_flags */
|
||||
TYPE_INT, /* fm_mapped_extents */
|
||||
TYPE_INT, /* fm_extent_count */
|
||||
TYPE_INT) /* fm_reserved */
|
||||
|
Loading…
Reference in New Issue
Block a user