From patchwork Fri Jan 7 20:52:31 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Riku Voipio X-Patchwork-Id: 77915 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id CB253B7121 for ; Sat, 8 Jan 2011 08:00:12 +1100 (EST) Received: from localhost ([127.0.0.1]:53422 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PbJPt-0003ap-Vh for incoming@patchwork.ozlabs.org; Fri, 07 Jan 2011 16:00:10 -0500 Received: from [140.186.70.92] (port=60592 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1PbJIh-0008GL-9g for qemu-devel@nongnu.org; Fri, 07 Jan 2011 15:52:45 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PbJIe-0007Vs-4J for qemu-devel@nongnu.org; Fri, 07 Jan 2011 15:52:43 -0500 Received: from afflict.kos.to ([92.243.29.197]:44853) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PbJId-0007VW-Qa for qemu-devel@nongnu.org; Fri, 07 Jan 2011 15:52:40 -0500 Received: by afflict.kos.to (Postfix, from userid 1000) id E92AD26674; Fri, 7 Jan 2011 20:52:35 +0000 (UTC) From: Riku Voipio To: qemu-devel@nongnu.org Date: Fri, 7 Jan 2011 22:52:31 +0200 Message-Id: <285da2b9a83353703d07e141fdb447e82944146c.1294433287.git.riku.voipio@nokia.com> X-Mailer: git-send-email 1.6.5 In-Reply-To: References: X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) Cc: Peter Maydell , Riku Voipio Subject: [Qemu-devel] [PATCH 3/7] linux-user: Implement FS_IOC_FIEMAP ioctl X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Peter Maydell 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 Signed-off-by: Riku Voipio --- linux-user/ioctls.h | 4 ++ linux-user/syscall.c | 88 ++++++++++++++++++++++++++++++++++++++++++++ linux-user/syscall_defs.h | 1 + linux-user/syscall_types.h | 16 ++++++++ 4 files changed, 109 insertions(+), 0 deletions(-) diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 769e1bc..538e257 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -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))) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 970efe3..f10e17a 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -83,6 +83,7 @@ int __clone2(int (*fn)(void *), void *child_stack_base, #include #include #include +#include #include #include #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__ } }, diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 20c93d0..d02a9bf 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -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 */ diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index 340dbd3..0e67cd8 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -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 */