[patch 3/3] Fix XFS_IOC_FSBULKSTAT{,_SINGLE} and XFS_IOC_FSINUMBERS in compat mode

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



* 32bit struct xfs_fsop_bulkreq has different size and layout of
  members, no matter the alignment. Move the code out of the #else
  branch (why was it there in the first place?). Define _32 variants of
  the ioctl constants.
* 32bit struct xfs_bstat is different on 32bit (because of time_t and on
  i386 becaus of different padding). Create a new function
  xfs_ioctl32_bulkstat_wrap(), which allocates extra ->ubuffer and
  converts the elements to the 32bit format after the original ioctl
  returns. Same for i386 struct xfs_inogrp.

Signed-off-by: Michal Marek <[email protected]>
---
 fs/xfs/linux-2.6/xfs_ioctl32.c |  262 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 238 insertions(+), 24 deletions(-)

--- linux-2.6.orig/fs/xfs/linux-2.6/xfs_ioctl32.c
+++ linux-2.6/fs/xfs/linux-2.6/xfs_ioctl32.c
@@ -109,35 +109,249 @@ STATIC unsigned long xfs_ioctl32_geom_v1
 	return (unsigned long)p;
 }
 
-#else
+typedef struct xfs_inogrp32 {
+	__u64		xi_startino;	/* starting inode number	*/
+	__s32		xi_alloccount;	/* # bits set in allocmask	*/
+	__u64		xi_allocmask;	/* mask of allocated inodes	*/
+} __attribute__((packed)) xfs_inogrp32_t;
+
+STATIC int xfs_inogrp_store_compat(
+	xfs_inogrp32_t __user  *p32,
+	xfs_inogrp_t __user *p)
+{
+#define copy(memb) copy_in_user(&p32->memb, &p->memb, sizeof(p32->memb))
+	if (copy(xi_startino) ||
+	    copy(xi_alloccount) ||
+	    copy(xi_allocmask))
+		return -EFAULT;
+	return 0;
+#undef copy
+}
+
+#endif
+
+/* XFS_IOC_FSBULKSTAT and friends */
+
+typedef struct xfs_bstime32 {
+	__s32		tv_sec;		/* seconds		*/
+	__s32		tv_nsec;	/* and nanoseconds	*/
+} xfs_bstime32_t;
+
+static int xfs_bstime_store_compat(
+	xfs_bstime32_t __user *p32,
+	xfs_bstime_t __user *p)
+{
+	time_t sec;
+	__s32 sec32;
+
+	if (get_user(sec, &p->tv_sec))
+		return -EFAULT;
+	sec32 = sec;
+	if (put_user(sec32, &p32->tv_sec) ||
+	    copy_in_user(&p32->tv_nsec, &p->tv_nsec, sizeof(__s32)))
+		return -EFAULT;
+	return 0;
+}
+
+typedef struct xfs_bstat32 {
+	__u64		bs_ino;		/* inode number			*/
+	__u16		bs_mode;	/* type and mode		*/
+	__u16		bs_nlink;	/* number of links		*/
+	__u32		bs_uid;		/* user id			*/
+	__u32		bs_gid;		/* group id			*/
+	__u32		bs_rdev;	/* device value			*/
+	__s32		bs_blksize;	/* block size			*/
+	__s64		bs_size;	/* file size			*/
+	xfs_bstime32_t	bs_atime;	/* access time			*/
+	xfs_bstime32_t	bs_mtime;	/* modify time			*/
+	xfs_bstime32_t	bs_ctime;	/* inode change time		*/
+	int64_t		bs_blocks;	/* number of blocks		*/
+	__u32		bs_xflags;	/* extended flags		*/
+	__s32		bs_extsize;	/* extent size			*/
+	__s32		bs_extents;	/* number of extents		*/
+	__u32		bs_gen;		/* generation count		*/
+	__u16		bs_projid;	/* project id			*/
+	unsigned char	bs_pad[14];	/* pad space, unused		*/
+	__u32		bs_dmevmask;	/* DMIG event mask		*/
+	__u16		bs_dmstate;	/* DMIG state info		*/
+	__u16		bs_aextents;	/* attribute number of extents	*/
+}
+#ifdef BROKEN_X86_ALIGNMENT
+	__attribute__((packed))
+#endif
+	xfs_bstat32_t;
+
+static int xfs_bstat_store_compat(
+	xfs_bstat32_t __user *p32,
+	xfs_bstat_t __user *p)
+{
+#define copy(memb) copy_in_user(&p32->memb, &p->memb, sizeof(p32->memb))
+	if (copy(bs_ino) ||
+	    copy(bs_mode) ||
+	    copy(bs_nlink) ||
+	    copy(bs_uid) ||
+	    copy(bs_gid) ||
+	    copy(bs_rdev) ||
+	    copy(bs_blksize) ||
+	    copy(bs_size) ||
+	    xfs_bstime_store_compat(&p32->bs_atime, &p->bs_atime) ||
+	    xfs_bstime_store_compat(&p32->bs_mtime, &p->bs_mtime) ||
+	    xfs_bstime_store_compat(&p32->bs_ctime, &p->bs_ctime) ||
+	    copy(bs_blocks) ||
+	    copy(bs_xflags) ||
+	    copy(bs_extsize) ||
+	    copy(bs_extents) ||
+	    copy(bs_gen) ||
+	    copy(bs_projid) ||
+	    copy(bs_pad[14]) ||
+	    copy(bs_dmevmask) ||
+	    copy(bs_dmstate) ||
+	    copy(bs_aextents))
+		return -EFAULT;
+	return 0;
+#undef copy
+}
 
 typedef struct xfs_fsop_bulkreq32 {
 	compat_uptr_t	lastip;		/* last inode # pointer		*/
 	__s32		icount;		/* count of entries in buffer	*/
 	compat_uptr_t	ubuffer;	/* user buffer for inode desc.	*/
-	__s32		ocount;		/* output count pointer		*/
+	compat_uptr_t	ocount;		/* output count pointer		*/
 } xfs_fsop_bulkreq32_t;
-
-STATIC unsigned long
-xfs_ioctl32_bulkstat(
-	unsigned long		arg)
+#define XFS_IOC_FSBULKSTAT_32	     _IOWR('X', 101, struct xfs_fsop_bulkreq32)
+#define XFS_IOC_FSBULKSTAT_SINGLE_32 _IOWR('X', 102, struct xfs_fsop_bulkreq32)
+#define XFS_IOC_FSINUMBERS_32	     _IOWR('X', 103, struct xfs_fsop_bulkreq32)
+
+#define MAX_BSTAT_LEN \
+	((__s32)((64*1024 - sizeof(xfs_fsop_bulkreq_t)) / sizeof(xfs_bstat_t)))
+#define MAX_INOGRP_LEN \
+	((__s32)((64*1024 - sizeof(xfs_fsop_bulkreq_t)) / sizeof(xfs_inogrp_t)))
+
+STATIC int
+xfs_ioctl32_bulkstat_wrap(
+	bhv_vnode_t	*vp,
+	struct inode    *inode,
+	struct file     *file,
+	int             mode,
+	unsigned        cmd,
+	unsigned long   arg)
 {
-	xfs_fsop_bulkreq32_t	__user *p32 = (void __user *)arg;
-	xfs_fsop_bulkreq_t	__user *p = compat_alloc_user_space(sizeof(*p));
-	u32			addr;
-
-	if (get_user(addr, &p32->lastip) ||
-	    put_user(compat_ptr(addr), &p->lastip) ||
-	    copy_in_user(&p->icount, &p32->icount, sizeof(s32)) ||
-	    get_user(addr, &p32->ubuffer) ||
-	    put_user(compat_ptr(addr), &p->ubuffer) ||
-	    get_user(addr, &p32->ocount) ||
-	    put_user(compat_ptr(addr), &p->ocount))
+	xfs_fsop_bulkreq32_t __user *p32 = (void __user *)arg;
+	xfs_fsop_bulkreq_t tmp;
+	u32 addr;
+	void *buf32;
+	int err;
+
+	if (get_user(addr, &p32->lastip))
+		return 0;
+	tmp.lastip = compat_ptr(addr);
+	if (get_user(tmp.icount, &p32->icount) ||
+	    get_user(addr, &p32->ubuffer))
 		return -EFAULT;
+	buf32 = compat_ptr(addr);
+	if (get_user(addr, &p32->ocount))
+		return -EFAULT;
+	tmp.ocount = compat_ptr(addr);
 
-	return (unsigned long)p;
-}
+	if (tmp.icount <= 0)
+		return -EINVAL;
+
+	if (cmd == XFS_IOC_FSBULKSTAT_32)
+		cmd = XFS_IOC_FSBULKSTAT;
+	if (cmd == XFS_IOC_FSBULKSTAT_SINGLE_32)
+		cmd = XFS_IOC_FSBULKSTAT_SINGLE;
+	if (cmd == XFS_IOC_FSINUMBERS_32)
+		cmd = XFS_IOC_FSINUMBERS;
+
+	if (cmd == XFS_IOC_FSBULKSTAT || cmd == XFS_IOC_FSBULKSTAT_SINGLE) {
+		xfs_fsop_bulkreq_t __user *p;
+		xfs_bstat_t __user *bs;
+		xfs_bstat32_t __user *bs32 = buf32;
+		__s32 total_ocount = 0;
+		p = compat_alloc_user_space(sizeof(*p) +
+			sizeof(xfs_bstat_t) * min(MAX_BSTAT_LEN, tmp.icount));
+		bs = (xfs_bstat_t __user *)(p + 1);
+		tmp.ubuffer = bs;
+		if (copy_to_user(p, &tmp, sizeof(*p)))
+			return -EFAULT;
+		while (tmp.icount) {
+			__s32 icount = min(MAX_BSTAT_LEN, tmp.icount);
+			__s32 ocount;
+			int i;
+
+			if (put_user(icount, &p->icount))
+				return -EFAULT;
+			err = bhv_vop_ioctl(vp, inode, file, mode, cmd,
+					(void __user *)p);
+			if (err)
+				return err;
+			if (get_user(ocount, p->ocount))
+				return -EFAULT;
+			for (i = 0; i < ocount; i++) {
+				if (xfs_bstat_store_compat(bs32 +
+						total_ocount + i, bs + i))
+					return -EFAULT;
+			}
+			total_ocount += ocount;
+			tmp.icount -= icount;
+		}
+		if (put_user(total_ocount, p->ocount))
+			return -EFAULT;
+		return 0;
+	}
+	if (cmd == XFS_IOC_FSINUMBERS) {
+#ifdef BROKEN_X86_ALIGNMENT
+		xfs_fsop_bulkreq_t __user *p;
+		xfs_inogrp_t __user *ig;
+		xfs_inogrp32_t __user *ig32 = buf32;
+		__s32 total_ocount = 0;
+		p = compat_alloc_user_space(sizeof(*p) +
+			sizeof(xfs_inogrp_t) * min(MAX_INOGRP_LEN, tmp.icount));
+		ig = (xfs_inogrp_t __user *)(p + 1);
+		tmp.ubuffer = ig;
+		if (copy_to_user(p, &tmp, sizeof(*p)))
+			return -EFAULT;
+
+		while (tmp.icount) {
+			__s32 icount = min(MAX_INOGRP_LEN, tmp.icount);
+			__s32 ocount;
+			int i;
+
+			if (put_user(icount, &p->icount))
+				return -EFAULT;
+			err = bhv_vop_ioctl(vp, inode, file, mode, cmd,
+					(void __user *)p);
+			if (err)
+				return err;
+			if (get_user(ocount, p->ocount))
+				return -EFAULT;
+			for (i = 0; i < ocount; i++) {
+				if (xfs_inogrp_store_compat(ig32 +
+						total_ocount + i, ig + i))
+					return -EFAULT;
+			}
+			tmp.icount -= icount;
+			total_ocount += ocount;
+		}
+		if (put_user(total_ocount, p->ocount))
+			return -EFAULT;
+#else
+		xfs_fsop_bulkreq_t __user *p;
+		p = compat_alloc_user_space(sizeof(*p));
+		tmp.ubuffer = buf32;
+		if (copy_to_user(p, &tmp, sizeof(*p)))
+			return -EFAULT;
+
+		err = bhv_vop_ioctl(vp, inode, file, mode, cmd,
+				(void __user *)p);
+		if (err)
+			return err;
 #endif
+		return 0;
+	}
+	return -ENOSYS;
+}
+
 
 typedef struct xfs_fsop_handlereq32 {
 	__u32		fd;		/* fd for FD_TO_HANDLE		*/
@@ -253,12 +467,12 @@ xfs_compat_ioctl(
 	case XFS_IOC_SWAPEXT:
 		break;
 
-	case XFS_IOC_FSBULKSTAT_SINGLE:
-	case XFS_IOC_FSBULKSTAT:
-	case XFS_IOC_FSINUMBERS:
-		arg = xfs_ioctl32_bulkstat(arg);
-		break;
 #endif
+	case XFS_IOC_FSBULKSTAT_32:
+	case XFS_IOC_FSBULKSTAT_SINGLE_32:
+	case XFS_IOC_FSINUMBERS_32:
+		return xfs_ioctl32_bulkstat_wrap(vp, inode, file, mode, cmd,
+				arg);
 	case XFS_IOC_FD_TO_HANDLE_32:
 		arg = xfs_ioctl32_fshandle(arg);
 		cmd = XFS_IOC_FD_TO_HANDLE;

-- 
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux