Add the lseek SEEK_DATA/SEEK_HOLE support into xfs_io. The result from the lseek() call will be printed to the output. For example: xfs_io> lseek -h 609k Type Offset hole 630784 Signed-off-by: Mark Tinguely <tinguely@xxxxxxx> --- v1 -> v2 Add "-a" and "-r" options. Simplify the output. v2 -> v3 Refactor for configure.in -> configure.ac change. SEEK_DATA with -1 offset behaves badly on older Linux. Display error message as "ERR <errno>". v3 -> v4 Change command name to "seek" Modified detection of first data/hole. Separate output routine. Single loop to handle each data/hole item. configure.ac | 1 include/builddefs.in | 1 io/Makefile | 5 + io/init.c | 1 io/io.h | 6 + io/seek.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++++++ m4/package_libcdev.m4 | 15 ++++ man/man8/xfs_io.8 | 35 ++++++++++ 8 files changed, 228 insertions(+) Index: seek_xfsprogs/configure.ac =================================================================== --- seek_xfsprogs.orig/configure.ac +++ seek_xfsprogs/configure.ac @@ -110,6 +110,7 @@ AC_HAVE_GETMNTINFO AC_HAVE_FALLOCATE AC_HAVE_FIEMAP AC_HAVE_PREADV +AC_HAVE_SEEK_DATA AC_HAVE_SYNC_FILE_RANGE AC_HAVE_BLKID_TOPO($enable_blkid) Index: seek_xfsprogs/include/builddefs.in =================================================================== --- seek_xfsprogs.orig/include/builddefs.in +++ seek_xfsprogs/include/builddefs.in @@ -102,6 +102,7 @@ HAVE_GETMNTINFO = @have_getmntinfo@ HAVE_FALLOCATE = @have_fallocate@ HAVE_FIEMAP = @have_fiemap@ HAVE_PREADV = @have_preadv@ +HAVE_SEEK_DATA = @have_seek_data@ HAVE_SYNC_FILE_RANGE = @have_sync_file_range@ GCCFLAGS = -funsigned-char -fno-strict-aliasing -Wall Index: seek_xfsprogs/io/Makefile =================================================================== --- seek_xfsprogs.orig/io/Makefile +++ seek_xfsprogs/io/Makefile @@ -80,6 +80,11 @@ ifeq ($(HAVE_PREADV),yes) LCFLAGS += -DHAVE_PREADV -DHAVE_PWRITEV endif +ifeq ($(HAVE_SEEK_DATA),yes) +LCFLAGS += -DHAVE_SEEK_DATA +CFILES += seek.c +endif + default: depend $(LTCOMMAND) include $(BUILDRULES) Index: seek_xfsprogs/io/init.c =================================================================== --- seek_xfsprogs.orig/io/init.c +++ seek_xfsprogs/io/init.c @@ -64,6 +64,7 @@ init_commands(void) help_init(); imap_init(); inject_init(); + seek_init(); madvise_init(); mincore_init(); mmap_init(); Index: seek_xfsprogs/io/io.h =================================================================== --- seek_xfsprogs.orig/io/io.h +++ seek_xfsprogs/io/io.h @@ -108,6 +108,12 @@ extern void quit_init(void); extern void shutdown_init(void); extern void truncate_init(void); +#ifdef HAVE_SEEK_DATA +extern void seek_init(void); +#else +#define seek_init() do { } while (0) +#endif + #ifdef HAVE_FADVISE extern void fadvise_init(void); #else Index: seek_xfsprogs/io/seek.c =================================================================== --- /dev/null +++ seek_xfsprogs/io/seek.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2012 SGI + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it would be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <libxfs.h> +#include <linux/fs.h> + +#include <sys/uio.h> +#include <xfs/xfs.h> +#include <xfs/command.h> +#include <xfs/input.h> +#include <ctype.h> +#include "init.h" +#include "io.h" + +static cmdinfo_t seek_cmd; + +static void +seek_help(void) +{ + printf(_( +"\n" +" returns the next hole and/or data offset at or after the specified offset\n" +"\n" +" Example:\n" +" 'seek -d 512' - offset of data at or following offset 512\n" +" 'seek -a -r 0' - offsets of all data and hole in entire file\n" +"\n" +" Returns the offset of the next data and/or hole. There is an implied hole\n" +" at the end of file. If the specified offset is past end of file, or there\n" +" is no data past the specied offset, EOF is returned.\n" +" -a -- return the next data and hole starting at the specified offset.\n" +" -d -- return the next data starting at the specified offset.\n" +" -h -- return the next hole starting at the specified offset.\n" +" -r -- return all remaining type(s) starting at the specified offset.\n" +"\n")); +} + +#define SEEK_DFLAG (1 << 0) +#define SEEK_HFLAG (1 << 1) +#define SEEK_RFLAG (1 << 2) +#define DATA 0 +#define HOLE 1 + +struct seekinfo { + char *name; + int seektype; + int mask; +} seekinfo[] = { + {"DATA", SEEK_DATA, SEEK_DFLAG}, + {"HOLE", SEEK_HOLE, SEEK_HFLAG} +}; + +void +seek_output( + char *name, + off64_t offset) +{ + if (offset == -1) { + if (errno == ENXIO) + printf("%s EOF\n", name); + else + printf("%s ERR %d\n", name, errno); + } else + printf("%s %ld\n", name, offset); +} + +static int +seek_f( + int argc, + char **argv) +{ + off64_t offset, result; + size_t fsblocksize, fssectsize; + int flag; + int type; /* specify data or hole */ + int c; + + flag = 0; + init_cvtnum(&fsblocksize, &fssectsize); + + while ((c = getopt(argc, argv, "adhr")) != EOF) { + switch (c) { + case 'a': + flag |= (SEEK_HFLAG | SEEK_DFLAG); + break; + case 'd': + flag |= SEEK_DFLAG; + break; + case 'h': + flag |= SEEK_HFLAG; + break; + case 'r': + flag |= SEEK_RFLAG; + break; + default: + return command_usage(&seek_cmd); + } + } + /* must have hole or data specified and an offset */ + if (!(flag & (SEEK_DFLAG | SEEK_HFLAG)) || + optind != argc - 1) + return command_usage(&seek_cmd); + + offset = cvtnum(fsblocksize, fssectsize, argv[optind]); + + if (flag & SEEK_HFLAG) { + result = lseek64(file->fd, offset, SEEK_HOLE); + if ((result == offset) || + !(flag & SEEK_DFLAG)) { + offset = result; /* in case it was EOF */ + type = HOLE; + goto found_hole; + } + } + + /* found data or both hole and data results in a -1 */ + type = DATA; + offset = lseek64(file->fd, offset, SEEK_DATA); + +found_hole: + printf("Type offset\n"); + + /* loop to handle the data / hole entries in assending order. */ + while (flag) { + if (flag & seekinfo[type].mask) + seek_output(seekinfo[type].name, offset); + + /* stop on error or when the EOF has been detected */ + if (offset == -1) + break; + + /* For the case of display only a single data and/or hole + * item, mask off the item just displayed. The loop will + * end when all the requested items have been displayed. + */ + if (!(flag & SEEK_RFLAG)) + flag ^= seekinfo[type].mask; + + type ^= 1; /* alternate between data and hole */ + offset = lseek64(file->fd, offset, seekinfo[type].seektype); + } + return 0; +} + +void +seek_init(void) +{ + seek_cmd.name = "seek"; + seek_cmd.altname = "seek"; + seek_cmd.cfunc = seek_f; + seek_cmd.argmin = 2; + seek_cmd.argmax = 5; + seek_cmd.flags = CMD_NOMAP_OK | CMD_FOREIGN_OK; + seek_cmd.args = _("-a | -d | -h [-r] off"); + seek_cmd.oneline = _("locate the next data and/or hole"); + seek_cmd.help = seek_help; + + add_command(&seek_cmd); +} Index: seek_xfsprogs/m4/package_libcdev.m4 =================================================================== --- seek_xfsprogs.orig/m4/package_libcdev.m4 +++ seek_xfsprogs/m4/package_libcdev.m4 @@ -153,6 +153,21 @@ AC_DEFUN([AC_HAVE_PREADV], AC_SUBST(have_preadv) ]) +# +# Check if we have a working fadvise system call +# +AC_DEFUN([AC_HAVE_SEEK_DATA], + [ AC_MSG_CHECKING([for lseek SEEK_DATA]) + AC_TRY_COMPILE([ +#include <linux/fs.h> + ], [ + lseek(0, 0, SEEK_DATA); + ], have_seek_data=yes + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no)) + AC_SUBST(have_seek_data) + ]) + # # Check if we have a sync_file_range libc call (Linux) # Index: seek_xfsprogs/man/man8/xfs_io.8 =================================================================== --- seek_xfsprogs.orig/man/man8/xfs_io.8 +++ seek_xfsprogs/man/man8/xfs_io.8 @@ -377,6 +377,41 @@ must be specified as another open file .RB ( \-f ) or by path .RB ( \-i ). +.TP +.BI "seek \-a | \-d | \-h [ \-r ] offset" +On platforms that support the +.BR lseek (2) +.B SEEK_DATA +and +.B SEEK_HOLE +options, display the offsets of the specified segments. +.RS 1.0i +.PD 0 +.TP 0.4i +.B \-a +Display both +.B data +and +.B hole +segments starting at the specified +.B offset. +.TP +.B \-d +Display the +.B data +segment starting at the specified +.B offset. +.TP +.B \-h +Display the +.B hole +segment starting at the specified +.B offset. +.TP +.B \-r +Recursively display all the specified segments starting at the specified +.B offset. +.TP .SH MEMORY MAPPED I/O COMMANDS .TP _______________________________________________ xfs mailing list xfs@xxxxxxxxxxx http://oss.sgi.com/mailman/listinfo/xfs