public inbox for linux-xfs@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2 v2] hook xfs to fiemap ioctl
@ 2008-10-25 13:26 Eric Sandeen
  2008-10-25 13:33 ` [PATCH 1/2 v2] convert xfs_getbmap to take formatter functions Eric Sandeen
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Eric Sandeen @ 2008-10-25 13:26 UTC (permalink / raw)
  To: xfs-oss

Update & resend, including some checkpatch fixes, a bugfix, and 
incorporating some of Christoph's and Dave's comments.

Also, here's a very hacky (don't laugh!) test app.  I'll clean it
up at some point :)

-Eric

------------

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <ext2fs/ext2_types.h>

/*************************************************/
/* All this should come from fiemap.h eventually */
struct fiemap_extent {
        __u64 fe_logical;  /* logical offset in bytes for the start of
                            * the extent from the beginning of the file */
        __u64 fe_physical; /* physical offset in bytes for the start
                            * of the extent from the beginning of the disk */
        __u64 fe_length;   /* length in bytes for this extent */
        __u64 fe_reserved64[2];
        __u32 fe_flags;    /* FIEMAP_EXTENT_* flags for this extent */
        __u32 fe_reserved[3];
};

struct fiemap {
        __u64 fm_start;         /* logical offset (inclusive) at
                                 * which to start mapping (in) */
        __u64 fm_length;        /* logical length of mapping which
                                 * userspace wants (in) */
        __u32 fm_flags;         /* FIEMAP_FLAG_* flags for request (in/out) */
        __u32 fm_mapped_extents;/* number of extents that were mapped (out) */
        __u32 fm_extent_count;  /* size of fm_extents array (in) */
        __u32 fm_reserved;
        struct fiemap_extent fm_extents[0]; /* array of mapped extents (out) */
};

#define FIEMAP_MAX_OFFSET       (~0ULL)

#define FIEMAP_FLAG_SYNC        0x00000001 /* sync file data before map */
#define FIEMAP_FLAG_XATTR       0x00000002 /* map extended attribute tree */

#define FIEMAP_FLAGS_COMPAT     (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_XATTR)

#define FIEMAP_EXTENT_LAST              0x00000001 /* Last extent in file. */
#define FIEMAP_EXTENT_UNKNOWN           0x00000002 /* Data location unknown. */
#define FIEMAP_EXTENT_DELALLOC          0x00000004 /* Location still pending.
                                                    * Sets EXTENT_UNKNOWN. */
#define FIEMAP_EXTENT_ENCODED           0x00000008 /* Data can not be read
                                                    * while fs is unmounted */
#define FIEMAP_EXTENT_DATA_ENCRYPTED    0x00000080 /* Data is encrypted by fs.
                                                    * Sets EXTENT_NO_BYPASS. */
#define FIEMAP_EXTENT_NOT_ALIGNED       0x00000100 /* Extent offsets may not be
                                                    * block aligned. */
#define FIEMAP_EXTENT_DATA_INLINE       0x00000200 /* Data mixed with metadata.
                                                    * Sets EXTENT_NOT_ALIGNED.*/
#define FIEMAP_EXTENT_DATA_TAIL         0x00000400 /* Multiple files in block.
                                                    * Sets EXTENT_NOT_ALIGNED.*/
#define FIEMAP_EXTENT_UNWRITTEN         0x00000800 /* Space allocated, but
                                                    * no data (i.e. zero). */
#define FIEMAP_EXTENT_MERGED            0x00001000 /* File does not natively
                                                    * support extents. Result
                                                    * merged for efficiency. */

#define FIGETBSZ	_IO(0x00, 2)    /* get the block size used for bmap */
#define FS_IOC_FIEMAP			_IOWR('f', 11, struct fiemap)
// #define FS_IOC_FIECOUNT			_IOWR('f', 12, struct fiecount)

/* End of what should be coming from fiemap.h */
/**********************************************/


void usage(void)
{
	printf("Usage: fiemap [-vrSCL] [-s start] [-l length] [-c buf count] [-m max] filename\n");
	printf("  -v        : verbose mode\n");
	printf("  -r        : raw output: print raw ioctl structure values\n");
	printf("  -S        : set FIEMAP_FLAG_SYNC to sync before mapping\n");
	printf("  -C        : set FIEMAP_FLAG_NUM_EXTENTS to only get extent count, not mapping\n");
	printf("  -X        : set FIEMAP_FLAG_XATTR to report xattr mapping\n");
	printf("  -s start  : start of mapping in bytes (default 0)\n");
	printf("  -l length : length of mapping in bytes (default to end of file)\n");
	printf("  -c count  : count of extents in ioctl input structure (default 32)\n");
	printf("  -m max    : max nr of ioctls to call before exit (default 512)\n");
	exit(EXIT_FAILURE);
}

#define EXABYTES(x)     ((long long)(x) << 60)
#define PETABYTES(x)    ((long long)(x) << 50)
#define TERABYTES(x)    ((long long)(x) << 40)
#define GIGABYTES(x)    ((long long)(x) << 30)
#define MEGABYTES(x)    ((long long)(x) << 20)
#define KILOBYTES(x)    ((long long)(x) << 10)

long long
cvtnum(char *s)
{
	long long	i;
	char		*sp;
	int		c;

	i = strtoll(s, &sp, 0);
	if (i == 0 && sp == s)
		return -1LL;
	if (*sp == '\0')
		return i;
	if (sp[1] != '\0')
		return -1LL;

	c = tolower(*sp);
	switch (c) {
	case 'k':
		return KILOBYTES(i);
	case 'm':
		return MEGABYTES(i);
	case 'g':
		return GIGABYTES(i);
	case 't':
		return TERABYTES(i);
	case 'p':
		return PETABYTES(i);
	case 'e':
		return  EXABYTES(i);
	}

	return -1LL;
}

void show_extents_table(struct fiemap *fiemap, int blocksize, int start_extent, int *is_last)
{
	unsigned int	i;

	for (i = 0; i < fiemap->fm_mapped_extents; i++) {
		__u64	logical = fiemap->fm_extents[i].fe_logical;
		__u64	phys = fiemap->fm_extents[i].fe_physical;
		__u64	length = fiemap->fm_extents[i].fe_length;
		int	flags = fiemap->fm_extents[i].fe_flags;
		__u64	last_logical = 0;

		if (i) {
			last_logical =
				fiemap->fm_extents[i-1].fe_logical +
				fiemap->fm_extents[i-1].fe_length - 1;
		}

		printf("ext: %3u logical: [%8llu..%8llu] phys: %8llu..%8llu flags: 0x%03X tot: %llu\n",
			i + start_extent,
			logical / blocksize,
			(logical + length - 1) / blocksize,
			phys / blocksize,
			(phys + length - 1) / blocksize,
			flags,
			(length / blocksize));

		if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) {
			*is_last = 1;
			break; /* XXX should we?  or should look for exents filled in past last? */
		}
	}

	if (fiemap->fm_mapped_extents < fiemap->fm_extent_count)
		*is_last = 1;
}

void show_extents_raw(struct fiemap *fiemap, int start_extent, int *is_last)
{
	unsigned int	i;

	for (i = 0; i < fiemap->fm_mapped_extents; i++) {
		printf("Extent %3u: logical: %10lld physical: %10lld length: %10lld flags 0x%03X\n",
			i + start_extent,
			fiemap->fm_extents[i].fe_logical,
			fiemap->fm_extents[i].fe_physical,
			fiemap->fm_extents[i].fe_length,
			fiemap->fm_extents[i].fe_flags);

		if (fiemap->fm_extents[i].fe_flags & FIEMAP_EXTENT_LAST) {
			*is_last = 1;
			break;
			/* XXX should we?  or should look for extents wrongly filled in past last? */
		}
	}

	if (fiemap->fm_mapped_extents < fiemap->fm_extent_count)
		*is_last = 1;
}

int main(int argc, char**argv)
{
	int	blocksize = 0;	/* filesystem blocksize */
	uint	count = 32;	/* extent count */
	int	fd;		/* file descriptor */
	int	last = 0;	/* last extent found */
	int	xattr = 0;	/* return xattr mapping */
	int	maxioctls = 512; /* max ioctls to try */
	int	opt;
	int	rc;
	int	raw = 0;	/* raw output format */
	int	sync = 0;	/* sync file before mapping */
	int	verbose = 0;	/* verbose output */
	char	*fname;		/* filename to map */
	char	*fiebuf;	/* fiemap buffer / ioctl argument */
	__u64	lstart = 0;	/* logical input mapping start */
	__u64	llength = ~0ULL;/* logical input mapping length */
	uint	start_ext = 0;	/* starting extent nr. for this batch */
	__u32	flags = 0;
	struct fiemap *fiemap;


	/* XXX er, can llength be ~0ULL if start > 0? */
	while ((opt = getopt(argc, argv, "s:l:c:m:rSCLXv")) != -1) {
		switch(opt) {
		/* logical mapping offset */
		case 's':
			lstart = cvtnum(optarg);
			break;
		/* logical mapping length */
		case 'l':
			llength = cvtnum(optarg);
			break;
		/* count of extent buffers to send */
		case 'c':
			count = atoi(optarg);
			break;
		/* max nr. of ioctls to try (safety net) */
		case 'm':
			maxioctls = atoi(optarg);
			break;
		/* raw format output */
		case 'r':
			raw++;
			break;
		/* sync file before mapping */
		case 'S':
			sync++;
			break;
		/* return extents for xattrs */
		case 'X':
			xattr++;
			break;
		/* be verbose */
		case 'v':
			verbose++;
			break;
		default:
			usage();
		}
	}

	fname = argv[optind++];
	if (!fname)
		usage();

	/* Set the flags for the header */
	if (sync)
		flags |= FIEMAP_FLAG_SYNC;
	if (xattr)
		flags |= FIEMAP_FLAG_XATTR;

	/* The whole buffer, extent maps and all */
	fiebuf = malloc(sizeof (struct fiemap) + (count * sizeof(struct fiemap_extent)));
	if (!fiebuf) {
		perror("Could not allocate fiemap buffers");
		exit(1);
	}

	/* set up the header */
	fiemap = (struct fiemap *)fiebuf;

	fiemap->fm_start = lstart;
	fiemap->fm_length = llength;
	fiemap->fm_flags = flags;
	fiemap->fm_extent_count = count;
	fiemap->fm_mapped_extents = 0;	/* output only */

	fd = open(fname, O_RDONLY);
	if (fd < 0) {
		perror("Can't open file");
		exit(1);
	}

	if (ioctl(fd, FIGETBSZ, &blocksize) < 0) {
		perror("Can't get block size");
		close(fd);
		return;
	}

	do {
		if (verbose)
			printf("Input:  start %llu length %llu flags 0x%X count %u\n",
				fiemap->fm_start, fiemap->fm_length,
				fiemap->fm_flags, fiemap->fm_extent_count);

		rc = ioctl(fd, FS_IOC_FIEMAP, (unsigned long)fiemap);
		if (rc < 0) {
			perror("FIEMAP ioctl failed");
			printf("flags 0x%x\n", fiemap->fm_flags);
			close(fd);
			exit(1);
		}
		if (verbose)
			printf("Output: start %llu length %llu flags 0x%X count %u mapped %u\n",
				fiemap->fm_start, fiemap->fm_length,
				fiemap->fm_flags, fiemap->fm_extent_count,
				fiemap->fm_mapped_extents);

		if (raw)
			show_extents_raw(fiemap, start_ext, &last);
		else
			show_extents_table(fiemap, blocksize, start_ext, &last);

		if (!last) {
			int last_map = fiemap->fm_mapped_extents - 1;
			__u64 foo;	/* XXX Ummmm */

			foo = fiemap->fm_extents[last_map].fe_logical +
				fiemap->fm_extents[last_map].fe_length;
			/* Set up the next call arguments */ /* move to top */
			fiemap->fm_start = foo;
			fiemap->fm_length = lstart + llength - foo;
			fiemap->fm_extent_count = count;
		}

		/* keeps track of extent nrs. we've printed; rather, pass by value! */
		start_ext += fiemap->fm_mapped_extents;

		/* XXX need to stop if we've reached what we asked for */
		maxioctls--;

	} while (!last && maxioctls > 0);


	close(fd);

	return 0;
}

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/2 v2] convert xfs_getbmap to take formatter functions
  2008-10-25 13:26 [PATCH 0/2 v2] hook xfs to fiemap ioctl Eric Sandeen
@ 2008-10-25 13:33 ` Eric Sandeen
  2008-10-25 20:25   ` Christoph Hellwig
  2008-10-25 20:27   ` Christoph Hellwig
  2008-10-25 13:34 ` [PATCH 2/2 v2] hook up fiemap & associated formatter Eric Sandeen
  2008-10-25 20:20 ` [PATCH 0/2 v2] hook xfs to fiemap ioctl Christoph Hellwig
  2 siblings, 2 replies; 9+ messages in thread
From: Eric Sandeen @ 2008-10-25 13:33 UTC (permalink / raw)
  To: xfs-oss

Preliminary work to hook up fiemap, this allows us to pass in an
arbitrary formatter to copy extent data back to userspace.

The formatter takes info for 1 extent, a pointer to the user "thing*"
and a pointer to a "filled" variable to indicate whether a userspace
buffer did get filled in (for fiemap, hole "extents" are skipped).

I'm just using the getbmapx struct as a "common denominator" because
as far as I can see, it holds all info that any formatters will care
about.

("*thing" because fiemap doesn't pass the user pointer around, but rather
has a pointer to a fiemap info structure, and helpers associated with it)

Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
---

Index: linux-2.6-xfs/fs/xfs/xfs_bmap.c
===================================================================
--- linux-2.6-xfs.orig/fs/xfs/xfs_bmap.c
+++ linux-2.6-xfs/fs/xfs/xfs_bmap.c
@@ -5805,9 +5805,9 @@ error0:
 STATIC int
 xfs_getbmapx_fix_eof_hole(
 	xfs_inode_t		*ip,		/* xfs incore inode pointer */
-	struct getbmap		*out,		/* output structure */
+	struct getbmapx		*out,		/* output structure */
 	int			prealloced,	/* this is a file with
-						* preallocated data space */
+						 * preallocated data space */
 	__int64_t		end,		/* last block requested */
 	xfs_fsblock_t		startblock)
 {
@@ -5832,15 +5832,38 @@ xfs_getbmapx_fix_eof_hole(
 	return 1;
 }
 
+int xfs_getbmapx_format(void __user **ap, struct getbmapx *bmv, int *filled)
+{
+	*filled = 0;
+	if (copy_to_user(*ap, bmv, sizeof(struct getbmapx)))
+		return XFS_ERROR(EFAULT);
+
+	*ap += sizeof(struct getbmapx);
+	*filled = 1;
+	return 0;
+}
+
+int xfs_getbmap_format(void __user **ap, struct getbmapx *bmv, int *filled)
+{
+	*filled = 0;
+	/* copy only getbmap portion (not getbmapx) */
+	if (copy_to_user(*ap, bmv, sizeof(struct getbmap)))
+		return XFS_ERROR(EFAULT);
+
+	*ap += sizeof(struct getbmap);
+	*filled = 1;
+	return 0;
+}
+
 /*
- * Fcntl interface to xfs_bmapi.
+ * Get inode's extents as described in bmv, and format for output.
  */
 int						/* error code */
 xfs_getbmap(
 	xfs_inode_t		*ip,
-	struct getbmap		*bmv,		/* user bmap structure */
-	void			__user *ap,	/* pointer to user's array */
-	int			interface)	/* interface flags */
+	struct getbmapx		*bmv,		/* user bmap structure */
+	xfs_bmap_format_t	formatter,	/* format to user */
+	void			*arg)		/* formatter arg */
 {
 	__int64_t		bmvend;		/* last block requested */
 	int			error;		/* return value */
@@ -5853,19 +5876,20 @@ xfs_getbmap(
 	int			nexleft;	/* # of user extents left */
 	int			subnex;		/* # of bmapi's can do */
 	int			nmap;		/* number of map entries */
-	struct getbmap		out;		/* output structure */
+	struct getbmapx		out;		/* output structure */
 	int			whichfork;	/* data or attr fork */
 	int			prealloced;	/* this is a file with
 						 * preallocated data space */
 	int			sh_unwritten;	/* true, if unwritten */
 						/* extents listed separately */
+	int			iflags;		/* interface flags */
 	int			bmapi_flags;	/* flags for xfs_bmapi */
-	__int32_t		oflags;		/* getbmapx bmv_oflags field */
 
 	mp = ip->i_mount;
+	iflags = bmv->bmv_iflags;
 
-	whichfork = interface & BMV_IF_ATTRFORK ? XFS_ATTR_FORK : XFS_DATA_FORK;
-	sh_unwritten = (interface & BMV_IF_PREALLOC) != 0;
+	whichfork = iflags & BMV_IF_ATTRFORK ? XFS_ATTR_FORK : XFS_DATA_FORK;
+	sh_unwritten = (iflags & BMV_IF_PREALLOC) != 0;
 
 	/*	If the BMV_IF_NO_DMAPI_READ interface bit specified, do not
 	 *	generate a DMAPI read event.  Otherwise, if the DM_EVENT_READ
@@ -5880,7 +5904,7 @@ xfs_getbmap(
 	 *	could misinterpret holes in a DMAPI file as true holes,
 	 *	when in fact they may represent offline user data.
 	 */
-	if ((interface & BMV_IF_NO_DMAPI_READ) == 0 &&
+	if ((iflags & BMV_IF_NO_DMAPI_READ) == 0 &&
 	    DM_EVENT_ENABLED(ip, DM_EVENT_READ) &&
 	    whichfork == XFS_DATA_FORK) {
 		error = XFS_SEND_DATA(mp, DM_EVENT_READ, ip, 0, 0, 0, NULL);
@@ -5987,52 +6011,35 @@ xfs_getbmap(
 		ASSERT(nmap <= subnex);
 
 		for (i = 0; i < nmap && nexleft && bmv->bmv_length; i++) {
-			nexleft--;
-			oflags = (map[i].br_state == XFS_EXT_UNWRITTEN) ?
+			out.bmv_oflags = (map[i].br_state == XFS_EXT_UNWRITTEN) ?
 					BMV_OF_PREALLOC : 0;
 			out.bmv_offset = XFS_FSB_TO_BB(mp, map[i].br_startoff);
 			out.bmv_length = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
+			out.bmv_unused1 = out.bmv_unused2 = 0;
 			ASSERT(map[i].br_startblock != DELAYSTARTBLOCK);
                         if (map[i].br_startblock == HOLESTARTBLOCK &&
 			    whichfork == XFS_ATTR_FORK) {
 				/* came to the end of attribute fork */
 				goto unlock_and_return;
 			} else {
+				int filled;	/* nr extents filled */
+
 				if (!xfs_getbmapx_fix_eof_hole(ip, &out,
 							prealloced, bmvend,
 							map[i].br_startblock)) {
 					goto unlock_and_return;
 				}
 
-				/* return either getbmap/getbmapx structure. */
-				if (interface & BMV_IF_EXTENDED) {
-					struct	getbmapx	outx;
-
-					GETBMAP_CONVERT(out,outx);
-					outx.bmv_oflags = oflags;
-					outx.bmv_unused1 = outx.bmv_unused2 = 0;
-					if (copy_to_user(ap, &outx,
-							sizeof(outx))) {
-						error = XFS_ERROR(EFAULT);
-						goto unlock_and_return;
-					}
-				} else {
-					if (copy_to_user(ap, &out,
-							sizeof(out))) {
-						error = XFS_ERROR(EFAULT);
-						goto unlock_and_return;
-					}
-				}
+				/* format results & advance arg */
+				error = formatter(&arg, &out, &filled);
+				if (error)
+					goto unlock_and_return;
+				nexleft -= filled;
 				bmv->bmv_offset =
 					out.bmv_offset + out.bmv_length;
 				bmv->bmv_length = MAX((__int64_t)0,
 					(__int64_t)(bmvend - bmv->bmv_offset));
 				bmv->bmv_entries++;
-				ap = (interface & BMV_IF_EXTENDED) ?
-						(void __user *)
-					((struct getbmapx __user *)ap + 1) :
-						(void __user *)
-					((struct getbmap __user *)ap + 1);
 			}
 		}
 	} while (nmap && nexleft && bmv->bmv_length);
Index: linux-2.6-xfs/fs/xfs/xfs_bmap.h
===================================================================
--- linux-2.6-xfs.orig/fs/xfs/xfs_bmap.h
+++ linux-2.6-xfs/fs/xfs/xfs_bmap.h
@@ -356,15 +356,20 @@ xfs_bmap_finish(
 	xfs_bmap_free_t		*flist,		/* i/o: list extents to free */
 	int			*committed);	/* xact committed or not */
 
+/* bmap to userspace formatters - copy to user & advance pointer */
+typedef int (*xfs_bmap_format_t)(void __user **, struct getbmapx *, int *);
+int xfs_getbmapx_format(void __user **ap, struct getbmapx *bmv, int *filled);
+int xfs_getbmap_format(void __user **ap, struct getbmapx *bmv, int *filled);
+
 /*
- * Fcntl interface to xfs_bmapi.
+ * Get inode's extents as described in bmv, and format for output.
  */
 int						/* error code */
 xfs_getbmap(
 	xfs_inode_t		*ip,
-	struct getbmap		*bmv,		/* user bmap structure */
-	void			__user *ap,	/* pointer to user's array */
-	int			iflags);	/* interface flags */
+	struct getbmapx		*bmv,		/* user bmap structure */
+	xfs_bmap_format_t	formatter,	/* format to user */
+	void			*arg);		/* formatter arg */
 
 /*
  * Check if the endoff is outside the last extent. If so the caller will grow
Index: linux-2.6-xfs/fs/xfs/linux-2.6/xfs_ioctl.c
===================================================================
--- linux-2.6-xfs.orig/fs/xfs/linux-2.6/xfs_ioctl.c
+++ linux-2.6-xfs/fs/xfs/linux-2.6/xfs_ioctl.c
@@ -1262,25 +1262,26 @@ xfs_ioc_getbmap(
 	unsigned int		cmd,
 	void			__user *arg)
 {
-	struct getbmap		bm;
-	int			iflags;
+	struct getbmapx		bmx;
 	int			error;
 
-	if (copy_from_user(&bm, arg, sizeof(bm)))
+	if (copy_from_user(&bmx, arg, sizeof(struct getbmapx)))
 		return -XFS_ERROR(EFAULT);
 
-	if (bm.bmv_count < 2)
+	if (bmx.bmv_count < 2)
 		return -XFS_ERROR(EINVAL);
 
-	iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
+	bmx.bmv_iflags = (cmd == XFS_IOC_GETBMAPA ? BMV_IF_ATTRFORK : 0);
 	if (ioflags & IO_INVIS)
-		iflags |= BMV_IF_NO_DMAPI_READ;
+		bmx.bmv_iflags |= BMV_IF_NO_DMAPI_READ;
 
-	error = xfs_getbmap(ip, &bm, (struct getbmap __user *)arg+1, iflags);
+	error = xfs_getbmap(ip, &bmx, xfs_getbmap_format,
+			    (struct getbmap __user *)arg+1);
 	if (error)
 		return -error;
 
-	if (copy_to_user(arg, &bm, sizeof(bm)))
+	/* copy back header - only size of getbmap */
+	if (copy_to_user(arg, &bmx, sizeof(struct getbmap)))
 		return -XFS_ERROR(EFAULT);
 	return 0;
 }
@@ -1291,8 +1292,6 @@ xfs_ioc_getbmapx(
 	void			__user *arg)
 {
 	struct getbmapx		bmx;
-	struct getbmap		bm;
-	int			iflags;
 	int			error;
 
 	if (copy_from_user(&bmx, arg, sizeof(bmx)))
@@ -1301,26 +1300,16 @@ xfs_ioc_getbmapx(
 	if (bmx.bmv_count < 2)
 		return -XFS_ERROR(EINVAL);
 
-	/*
-	 * Map input getbmapx structure to a getbmap
-	 * structure for xfs_getbmap.
-	 */
-	GETBMAP_CONVERT(bmx, bm);
-
-	iflags = bmx.bmv_iflags;
-
-	if (iflags & (~BMV_IF_VALID))
+	if (bmx.bmv_iflags & (~BMV_IF_VALID))
 		return -XFS_ERROR(EINVAL);
 
-	iflags |= BMV_IF_EXTENDED;
-
-	error = xfs_getbmap(ip, &bm, (struct getbmapx __user *)arg+1, iflags);
+	error = xfs_getbmap(ip, &bmx, xfs_getbmapx_format,
+			    (struct getbmapx __user *)arg+1);
 	if (error)
 		return -error;
 
-	GETBMAP_CONVERT(bm, bmx);
-
-	if (copy_to_user(arg, &bmx, sizeof(bmx)))
+	/* copy back header */
+	if (copy_to_user(arg, &bmx, sizeof(struct getbmapx)))
 		return -XFS_ERROR(EFAULT);
 
 	return 0;
Index: linux-2.6-xfs/fs/xfs/xfs_fs.h
===================================================================
--- linux-2.6-xfs.orig/fs/xfs/xfs_fs.h
+++ linux-2.6-xfs/fs/xfs/xfs_fs.h
@@ -114,22 +114,10 @@ struct getbmapx {
 #define BMV_IF_NO_DMAPI_READ	0x2	/* Do not generate DMAPI read event  */
 #define BMV_IF_PREALLOC		0x4	/* rtn status BMV_OF_PREALLOC if req */
 #define BMV_IF_VALID	(BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC)
-#ifdef __KERNEL__
-#define BMV_IF_EXTENDED 0x40000000	/* getpmapx if set */
-#endif
 
 /*	bmv_oflags values - returned for for each non-header segment */
 #define BMV_OF_PREALLOC		0x1	/* segment = unwritten pre-allocation */
 
-/*	Convert getbmap <-> getbmapx - move fields from p1 to p2. */
-#define GETBMAP_CONVERT(p1,p2) {	\
-	p2.bmv_offset = p1.bmv_offset;	\
-	p2.bmv_block = p1.bmv_block;	\
-	p2.bmv_length = p1.bmv_length;	\
-	p2.bmv_count = p1.bmv_count;	\
-	p2.bmv_entries = p1.bmv_entries;  }
-
-
 /*
  * Structure for XFS_IOC_FSSETDM.
  * For use by backup and restore programs to set the XFS on-disk inode

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 2/2 v2] hook up fiemap & associated formatter
  2008-10-25 13:26 [PATCH 0/2 v2] hook xfs to fiemap ioctl Eric Sandeen
  2008-10-25 13:33 ` [PATCH 1/2 v2] convert xfs_getbmap to take formatter functions Eric Sandeen
@ 2008-10-25 13:34 ` Eric Sandeen
  2008-10-25 20:26   ` Christoph Hellwig
  2008-10-25 20:20 ` [PATCH 0/2 v2] hook xfs to fiemap ioctl Christoph Hellwig
  2 siblings, 1 reply; 9+ messages in thread
From: Eric Sandeen @ 2008-10-25 13:34 UTC (permalink / raw)
  To: xfs-oss

Hook up the fiemap ioctl.

This adds the fiemap inode_operation, which for us converts the 
fiemap values & flags into a getbmapx structure which can be sent
to xfs_getbmap.  The formatter then copies the bmv array back into the
user's fiemap buffer via the fiemap helpers.

This adds a new output flag, BMV_OF_LAST to indicate if we've hit
the last extent in the inode.  This potentially saves an extra call
from userspace to see when the whole mapping is done.

It also adds BMV_IF_DELALLOC and BMV_OF_DELALLOC to request, and
indicate, delayed-allocation extents.

If we wanted to be more clever, we could also return mapping data for 
in-inode attributes, but I'm not terribly motivated to do that just yet.

Signed-off-by: Eric Sandeen <sandeen@sandeen.net>
---

Index: linux-2.6-xfs/fs/xfs/linux-2.6/xfs_iops.c
===================================================================
--- linux-2.6-xfs.orig/fs/xfs/linux-2.6/xfs_iops.c
+++ linux-2.6-xfs/fs/xfs/linux-2.6/xfs_iops.c
@@ -53,6 +53,7 @@
 #include <linux/namei.h>
 #include <linux/security.h>
 #include <linux/falloc.h>
+#include <linux/fiemap.h>
 
 /*
  * Bring the atime in the XFS inode uptodate.
@@ -661,6 +662,80 @@ out_error:
 	return error;
 }
 
+#define XFS_FIEMAP_FLAGS	(FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)
+
+STATIC int xfs_fiemap_format(
+	void			**arg,
+	struct getbmapx		*bmv,
+	int			*filled)
+{
+	int			error = 0;
+	struct fiemap_extent_info *fieinfo = *arg;
+	u32			fiemap_flags = 0;
+	u64			logical, physical, length;
+
+	*filled = 0;
+	/* Do nothing for a hole */
+	if (bmv->bmv_block == -1LL)
+		return 0;
+
+	logical = BBTOB(bmv->bmv_offset);
+	physical = BBTOB(bmv->bmv_block);
+	length = BBTOB(bmv->bmv_length);
+
+	if (bmv->bmv_oflags & BMV_OF_PREALLOC)
+		fiemap_flags |= FIEMAP_EXTENT_UNWRITTEN;
+	else if (bmv->bmv_oflags & BMV_OF_DELALLOC) {
+		fiemap_flags |= FIEMAP_EXTENT_DELALLOC;
+		physical = 0;   /* no block yet */
+	}
+	if (bmv->bmv_oflags & BMV_OF_LAST)
+		fiemap_flags |= FIEMAP_EXTENT_LAST;
+
+	error = fiemap_fill_next_extent(fieinfo, logical, physical,
+					length, fiemap_flags);
+	if (error < 0)
+		return -error;
+	*filled = 1;
+	return 0;
+}
+
+STATIC int
+xfs_vn_fiemap(
+	struct inode		*inode,
+	struct fiemap_extent_info *fieinfo,
+	u64			start,
+	u64			length)
+{
+	xfs_inode_t		*ip = XFS_I(inode);
+	struct getbmapx		bm;
+	int			error;
+
+	if (fiemap_check_flags(fieinfo, XFS_FIEMAP_FLAGS))
+		return -EBADR;
+
+	/* Set up bmap header for xfs internal routine */
+	bm.bmv_offset = BTOBB(start);
+	/* Special case for whole file */
+	if (length == FIEMAP_MAX_OFFSET)
+		bm.bmv_length = -1LL;
+	else
+		bm.bmv_length = BTOBB(length);
+	/* xfs_getbmap takes count as header + array */
+	bm.bmv_count = fieinfo->fi_extents_max + 1;
+	bm.bmv_iflags = BMV_IF_PREALLOC;
+	if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR)
+		bm.bmv_iflags |= BMV_IF_ATTRFORK;
+	if (!(fieinfo->fi_flags & FIEMAP_FLAG_SYNC))
+		bm.bmv_iflags |= BMV_IF_DELALLOC;
+
+	error = xfs_getbmap(ip, &bm, xfs_fiemap_format, fieinfo);
+	if (error)
+		return -XFS_ERROR(error);
+
+	return 0;
+}
+
 static const struct inode_operations xfs_inode_operations = {
 	.permission		= xfs_vn_permission,
 	.truncate		= xfs_vn_truncate,
@@ -671,6 +746,7 @@ static const struct inode_operations xfs
 	.removexattr		= generic_removexattr,
 	.listxattr		= xfs_vn_listxattr,
 	.fallocate		= xfs_vn_fallocate,
+	.fiemap			= xfs_vn_fiemap,
 };
 
 static const struct inode_operations xfs_dir_inode_operations = {
Index: linux-2.6-xfs/fs/xfs/xfs_bmap.c
===================================================================
--- linux-2.6-xfs.orig/fs/xfs/xfs_bmap.c
+++ linux-2.6-xfs/fs/xfs/xfs_bmap.c
@@ -5813,6 +5813,9 @@ xfs_getbmapx_fix_eof_hole(
 {
 	__int64_t		fixlen;
 	xfs_mount_t		*mp;		/* file system mount point */
+	xfs_ifork_t		*ifp;		/* inode fork pointer */
+	xfs_extnum_t		lastx;		/* last extent pointer */
+	xfs_fileoff_t		fileblock;
 
 	if (startblock == HOLESTARTBLOCK) {
 		mp = ip->i_mount;
@@ -5827,6 +5830,11 @@ xfs_getbmapx_fix_eof_hole(
 		}
 	} else {
 		out->bmv_block = XFS_FSB_TO_DB(ip, startblock);
+		fileblock = XFS_BB_TO_FSB(ip->i_mount, out->bmv_offset);
+		ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK);
+		if (xfs_iext_bno_to_ext(ifp, fileblock, &lastx) &&
+		   (lastx == (ifp->if_bytes / (uint)sizeof(xfs_bmbt_rec_t))-1))
+			out->bmv_oflags |= BMV_OF_LAST;
 	}
 
 	return 1;
@@ -5880,8 +5888,6 @@ xfs_getbmap(
 	int			whichfork;	/* data or attr fork */
 	int			prealloced;	/* this is a file with
 						 * preallocated data space */
-	int			sh_unwritten;	/* true, if unwritten */
-						/* extents listed separately */
 	int			iflags;		/* interface flags */
 	int			bmapi_flags;	/* flags for xfs_bmapi */
 
@@ -5889,7 +5895,6 @@ xfs_getbmap(
 	iflags = bmv->bmv_iflags;
 
 	whichfork = iflags & BMV_IF_ATTRFORK ? XFS_ATTR_FORK : XFS_DATA_FORK;
-	sh_unwritten = (iflags & BMV_IF_PREALLOC) != 0;
 
 	/*	If the BMV_IF_NO_DMAPI_READ interface bit specified, do not
 	 *	generate a DMAPI read event.  Otherwise, if the DM_EVENT_READ
@@ -5960,8 +5965,9 @@ xfs_getbmap(
 
 	xfs_ilock(ip, XFS_IOLOCK_SHARED);
 
-	if (whichfork == XFS_DATA_FORK &&
-		(ip->i_delayed_blks || ip->i_size > ip->i_d.di_size)) {
+	if (((iflags & BMV_IF_DELALLOC) == 0) &&
+	    (whichfork == XFS_DATA_FORK) &&
+	    (ip->i_delayed_blks || ip->i_size > ip->i_d.di_size)) {
 		/* xfs_fsize_t last_byte = xfs_file_last_byte(ip); */
 		error = xfs_flush_pages(ip, (xfs_off_t)0,
 					       -1, 0, FI_REMAPF);
@@ -5971,7 +5977,8 @@ xfs_getbmap(
 		}
 	}
 
-	ASSERT(whichfork == XFS_ATTR_FORK || ip->i_delayed_blks == 0);
+	ASSERT(whichfork == XFS_ATTR_FORK || (iflags & BMV_IF_DELALLOC) ||
+	       ip->i_delayed_blks == 0);
 
 	lock = xfs_ilock_map_shared(ip);
 
@@ -5983,7 +5990,7 @@ xfs_getbmap(
 		nex = XFS_IFORK_NEXTENTS(ip, whichfork) * 2 + 1;
 
 	bmapi_flags = XFS_BMAPI_AFLAG(whichfork) |
-			((sh_unwritten) ? 0 : XFS_BMAPI_IGSTATE);
+			((iflags & BMV_IF_PREALLOC) ? 0 : XFS_BMAPI_IGSTATE);
 
 	/*
 	 * Allocate enough space to handle "subnex" maps at a time.
@@ -5993,9 +6000,12 @@ xfs_getbmap(
 
 	bmv->bmv_entries = 0;
 
-	if (XFS_IFORK_NEXTENTS(ip, whichfork) == 0) {
-		error = 0;
-		goto unlock_and_return;
+	if ((XFS_IFORK_NEXTENTS(ip, whichfork) == 0)) {
+		if (((iflags & BMV_IF_DELALLOC) == 0) ||
+		    whichfork == XFS_ATTR_FORK) {
+			error = 0;
+			goto unlock_and_return;
+		}
 	}
 
 	nexleft = nex;
@@ -6011,15 +6021,20 @@ xfs_getbmap(
 		ASSERT(nmap <= subnex);
 
 		for (i = 0; i < nmap && nexleft && bmv->bmv_length; i++) {
-			out.bmv_oflags = (map[i].br_state == XFS_EXT_UNWRITTEN) ?
-					BMV_OF_PREALLOC : 0;
+			out.bmv_oflags = 0;
+			if (map[i].br_state == XFS_EXT_UNWRITTEN)
+				out.bmv_oflags |= BMV_OF_PREALLOC;
+			else if (map[i].br_startblock == DELAYSTARTBLOCK)
+				out.bmv_oflags |= BMV_OF_DELALLOC;
 			out.bmv_offset = XFS_FSB_TO_BB(mp, map[i].br_startoff);
 			out.bmv_length = XFS_FSB_TO_BB(mp, map[i].br_blockcount);
 			out.bmv_unused1 = out.bmv_unused2 = 0;
-			ASSERT(map[i].br_startblock != DELAYSTARTBLOCK);
+			ASSERT(((iflags & BMV_IF_DELALLOC) != 0) ||
+			      (map[i].br_startblock != DELAYSTARTBLOCK));
                         if (map[i].br_startblock == HOLESTARTBLOCK &&
 			    whichfork == XFS_ATTR_FORK) {
 				/* came to the end of attribute fork */
+				out.bmv_oflags |= BMV_OF_LAST;
 				goto unlock_and_return;
 			} else {
 				int filled;	/* nr extents filled */
Index: linux-2.6-xfs/fs/xfs/xfs_fs.h
===================================================================
--- linux-2.6-xfs.orig/fs/xfs/xfs_fs.h
+++ linux-2.6-xfs/fs/xfs/xfs_fs.h
@@ -113,10 +113,14 @@ struct getbmapx {
 #define BMV_IF_ATTRFORK		0x1	/* return attr fork rather than data */
 #define BMV_IF_NO_DMAPI_READ	0x2	/* Do not generate DMAPI read event  */
 #define BMV_IF_PREALLOC		0x4	/* rtn status BMV_OF_PREALLOC if req */
-#define BMV_IF_VALID	(BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC)
+#define BMV_IF_DELALLOC		0x8	/* rtn status BMV_OF_DELALLOC if req */
+#define BMV_IF_VALID	\
+	(BMV_IF_ATTRFORK|BMV_IF_NO_DMAPI_READ|BMV_IF_PREALLOC|BMV_IF_DELALLOC)
 
 /*	bmv_oflags values - returned for for each non-header segment */
 #define BMV_OF_PREALLOC		0x1	/* segment = unwritten pre-allocation */
+#define BMV_OF_DELALLOC		0x2	/* segment = delayed allocation */
+#define BMV_OF_LAST		0x4	/* segment is the last in the file */
 
 /*
  * Structure for XFS_IOC_FSSETDM.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 0/2 v2] hook xfs to fiemap ioctl
  2008-10-25 13:26 [PATCH 0/2 v2] hook xfs to fiemap ioctl Eric Sandeen
  2008-10-25 13:33 ` [PATCH 1/2 v2] convert xfs_getbmap to take formatter functions Eric Sandeen
  2008-10-25 13:34 ` [PATCH 2/2 v2] hook up fiemap & associated formatter Eric Sandeen
@ 2008-10-25 20:20 ` Christoph Hellwig
  2 siblings, 0 replies; 9+ messages in thread
From: Christoph Hellwig @ 2008-10-25 20:20 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: xfs-oss

On Sat, Oct 25, 2008 at 08:26:25AM -0500, Eric Sandeen wrote:
> Update & resend, including some checkpatch fixes, a bugfix, and 
> incorporating some of Christoph's and Dave's comments.
> 
> Also, here's a very hacky (don't laugh!) test app.  I'll clean it
> up at some point :)

Would be best if you could add it to xftests and write a little script
to hook it up to xfsqa.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2 v2] convert xfs_getbmap to take formatter functions
  2008-10-25 13:33 ` [PATCH 1/2 v2] convert xfs_getbmap to take formatter functions Eric Sandeen
@ 2008-10-25 20:25   ` Christoph Hellwig
  2008-10-25 20:27   ` Christoph Hellwig
  1 sibling, 0 replies; 9+ messages in thread
From: Christoph Hellwig @ 2008-10-25 20:25 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: xfs-oss

On Sat, Oct 25, 2008 at 08:33:11AM -0500, Eric Sandeen wrote:
> ("*thing" because fiemap doesn't pass the user pointer around, but rather
> has a pointer to a fiemap info structure, and helpers associated with it)

Which means sparse won't like it.  You declare the first argument to the
filler as __user pointer here, but without it in the fiemap version.  I
think the better interface would be to pass a void *buffer, which is
casted to a

	struct {
		struct getbmap(x) __user	*base,
		unsigned			nr;
	}

for the XFS ioctl case and something different for fiemap.  And please
run sparse over it :)

Except for that the patch looks good to me.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2 v2] hook up fiemap & associated formatter
  2008-10-25 13:34 ` [PATCH 2/2 v2] hook up fiemap & associated formatter Eric Sandeen
@ 2008-10-25 20:26   ` Christoph Hellwig
  2008-10-26  4:38     ` Eric Sandeen
  0 siblings, 1 reply; 9+ messages in thread
From: Christoph Hellwig @ 2008-10-25 20:26 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: xfs-oss

> +#define XFS_FIEMAP_FLAGS	(FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)

Why do we need FIEMAP_FLAG_SYNC in here?  That flag is implemented in
the VFS and should be handled there.  (Probably more a question for
the generic implementation than your patch)

> +
> +STATIC int xfs_fiemap_format(

STATIC int
xfs_fiemap_format(



Buit the meat of the patch looks good to me.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2 v2] convert xfs_getbmap to take formatter functions
  2008-10-25 13:33 ` [PATCH 1/2 v2] convert xfs_getbmap to take formatter functions Eric Sandeen
  2008-10-25 20:25   ` Christoph Hellwig
@ 2008-10-25 20:27   ` Christoph Hellwig
  2008-10-25 20:28     ` Eric Sandeen
  1 sibling, 1 reply; 9+ messages in thread
From: Christoph Hellwig @ 2008-10-25 20:27 UTC (permalink / raw)
  To: Eric Sandeen; +Cc: xfs-oss

> +int xfs_getbmapx_format(void __user **ap, struct getbmapx *bmv, int *filled)
> +{
> +	*filled = 0;
> +	if (copy_to_user(*ap, bmv, sizeof(struct getbmapx)))
> +		return XFS_ERROR(EFAULT);
> +
> +	*ap += sizeof(struct getbmapx);
> +	*filled = 1;
> +	return 0;
> +}
> +
> +int xfs_getbmap_format(void __user **ap, struct getbmapx *bmv, int *filled)
> +{
> +	*filled = 0;
> +	/* copy only getbmap portion (not getbmapx) */
> +	if (copy_to_user(*ap, bmv, sizeof(struct getbmap)))
> +		return XFS_ERROR(EFAULT);
> +
> +	*ap += sizeof(struct getbmap);
> +	*filled = 1;
> +	return 0;
> +}

Probably makes sense to have these two helpers in xfs_ioctl.c, where
they are used, similar to how fiemap is implemented.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/2 v2] convert xfs_getbmap to take formatter functions
  2008-10-25 20:27   ` Christoph Hellwig
@ 2008-10-25 20:28     ` Eric Sandeen
  0 siblings, 0 replies; 9+ messages in thread
From: Eric Sandeen @ 2008-10-25 20:28 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: xfs-oss

Christoph Hellwig wrote:
>> +int xfs_getbmapx_format(void __user **ap, struct getbmapx *bmv, int *filled)
>> +{
>> +	*filled = 0;
>> +	if (copy_to_user(*ap, bmv, sizeof(struct getbmapx)))
>> +		return XFS_ERROR(EFAULT);
>> +
>> +	*ap += sizeof(struct getbmapx);
>> +	*filled = 1;
>> +	return 0;
>> +}
>> +
>> +int xfs_getbmap_format(void __user **ap, struct getbmapx *bmv, int *filled)
>> +{
>> +	*filled = 0;
>> +	/* copy only getbmap portion (not getbmapx) */
>> +	if (copy_to_user(*ap, bmv, sizeof(struct getbmap)))
>> +		return XFS_ERROR(EFAULT);
>> +
>> +	*ap += sizeof(struct getbmap);
>> +	*filled = 1;
>> +	return 0;
>> +}
> 
> Probably makes sense to have these two helpers in xfs_ioctl.c, where
> they are used, similar to how fiemap is implemented.
> 

Ok, that makes sense.  Will change that & the other suggestions.

(I did run sparse once, but made changes after that and forgot to re-run...)

-Eric

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/2 v2] hook up fiemap & associated formatter
  2008-10-25 20:26   ` Christoph Hellwig
@ 2008-10-26  4:38     ` Eric Sandeen
  0 siblings, 0 replies; 9+ messages in thread
From: Eric Sandeen @ 2008-10-26  4:38 UTC (permalink / raw)
  To: Christoph Hellwig; +Cc: xfs-oss

Christoph Hellwig wrote:
>> +#define XFS_FIEMAP_FLAGS	(FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR)
> 
> Why do we need FIEMAP_FLAG_SYNC in here?  That flag is implemented in
> the VFS and should be handled there.  (Probably more a question for
> the generic implementation than your patch)

well, sorta, it calls filemap_write_and_wait but no lock is held to keep
that consistent when the filesystem gets to it... IMHO it *should*
probably be up to the fs to handle the sync flag.  But anyway, for it
all to hang together I think we need to tell it that we understand this
flag.

-Eric

>> +
>> +STATIC int xfs_fiemap_format(
> 
> STATIC int
> xfs_fiemap_format(
> 
> 
> 
> Buit the meat of the patch looks good to me.
> 
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2008-10-26  4:38 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-25 13:26 [PATCH 0/2 v2] hook xfs to fiemap ioctl Eric Sandeen
2008-10-25 13:33 ` [PATCH 1/2 v2] convert xfs_getbmap to take formatter functions Eric Sandeen
2008-10-25 20:25   ` Christoph Hellwig
2008-10-25 20:27   ` Christoph Hellwig
2008-10-25 20:28     ` Eric Sandeen
2008-10-25 13:34 ` [PATCH 2/2 v2] hook up fiemap & associated formatter Eric Sandeen
2008-10-25 20:26   ` Christoph Hellwig
2008-10-26  4:38     ` Eric Sandeen
2008-10-25 20:20 ` [PATCH 0/2 v2] hook xfs to fiemap ioctl Christoph Hellwig

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox