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:
Peter Maydell 2011-01-06 15:04:18 +00:00 committed by Riku Voipio
parent d2ef05bb44
commit 285da2b9a8
4 changed files with 109 additions and 0 deletions

View File

@ -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)))

View File

@ -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__ } },

View File

@ -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 */

View File

@ -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 */