--- /dev/null
+/*
+ * Copyright (c) 2013 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 <xfs/xfs.h>
+#include <xfs/command.h>
+#include <xfs/input.h>
+#include <sys/types.h>
+#include <unistd.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 requested 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 specified 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"
+" -s -- also print the starting offset.\n"
+"\n"));
+}
+
+#ifndef HAVE_SEEK_DATA
+#define SEEK_DATA 3 /* seek to the next data */
+#define SEEK_HOLE 4 /* seek to the next hole */
+#endif
+
+/* values for flag variable */
+#define SEEK_DFLAG (1 << 0)
+#define SEEK_HFLAG (1 << 1)
+#define SEEK_RFLAG (1 << 2)
+
+/* indexes into the seekinfo array */
+#define DATA 0
+#define HOLE 1
+
+struct seekinfo {
+ char *name; /* display item name */
+ int seektype; /* data or hole */
+ int mask; /* compared for print and looping */
+} seekinfo[] = {
+ {"DATA", SEEK_DATA, SEEK_DFLAG},
+ {"HOLE", SEEK_HOLE, SEEK_HFLAG}
+};
+
+/* print item type and offset. catch special cases of eof and error */
+void
+seek_output(
+ int startflag,
+ char *type,
+ off64_t start,
+ off64_t offset)
+{
+ if (offset == -1) {
+ if (errno == ENXIO) {
+ if (startflag)
+ printf("%s %lld EOF\n", type,
+ (long long)start);
+ else
+ printf("%s EOF\n", type);
+ } else {
+ printf("ERR %lld ", (long long)start);
+ fflush(stdout); /* so the printf preceded the perror */
+ perror("");
+ }
+ } else {
+ if (startflag)
+ printf("%s %lld %lld\n", type,
+ (long long)start, (long long)offset);
+ else
+ printf("%s %lld\n", type, (long long)offset);
+ }
+}
+
+static int
+seek_f(
+ int argc,
+ char **argv)
+{
+ off64_t offset, start;
+ size_t fsblocksize, fssectsize;
+ int c;
+ int current; /* specify data or hole */
+ int flag;
+ int startflag;
+
+ flag = startflag = 0;
+ init_cvtnum(&fsblocksize, &fssectsize);
+
+ while ((c = getopt(argc, argv, "adhrs")) != 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;
+ case 's':
+ startflag = 1;
+ break;
+ default:
+ return command_usage(&seek_cmd);
+ }
+ }
+ if (!(flag & (SEEK_DFLAG | SEEK_HFLAG)) || optind != argc - 1)
+ return command_usage(&seek_cmd);
+
+ start = offset = cvtnum(fsblocksize, fssectsize, argv[optind]);
+ if (offset < 0)
+ return command_usage(&seek_cmd);
+
+ /*
+ * check to see if the offset is a data or hole entry and
+ * decide if we want to display that type of entry.
+ */
+ if (flag & SEEK_HFLAG) {
+ offset = lseek64(file->fd, start, SEEK_HOLE);
+ if ((start == offset) || !(flag & SEEK_DFLAG)) {
+ /*
+ * this offset is a hole or are only displaying holes.
+ * if this offset is for data and we are displaying
+ * data, then we will fall through below to
+ * initialize the data search.
+ */
+ current = HOLE;
+ goto found_hole;
+ }
+ }
+
+ /* The offset is not a hole, or we are looking just for data */
+ current = DATA;
+ offset = lseek64(file->fd, start, SEEK_DATA);
+
+found_hole:
+ /*
+ * At this point we know which type and the offset of the starting
+ * item. "current" alternates between data / hole entries in
+ * assending order - this alternation is needed even if only one
+ * type is to be displayed.
+ *
+ * An error or EOF will terminate the display, otherwise "flag"
+ * determines if there are more items to be displayed.
+ */
+ if (startflag)
+ printf("Whence Start Result\n");
+ else
+ printf("Whence Result\n");
+
+ for (c = 0; flag; c++) {
+ if (offset == -1) {
+ /* print error or eof if the only entry */
+ if (errno != ENXIO || c == 0 )
+ seek_output(startflag, seekinfo[current].name,
+ start, offset);
+ return 0; /* stop on error or EOF */
+ }
+
+ if (flag & seekinfo[current].mask)
+ seek_output(startflag, seekinfo[current].name, start,
+ offset);
+
+ /*
+ * When displaying only a single data and/or hole item, mask
+ * off the item as it is displayed. The loop will end when all
+ * requested items have been displayed.
+ */
+ if (!(flag & SEEK_RFLAG))
+ flag &= ~seekinfo[current].mask;
+
+ current ^= 1; /* alternate between data and hole */
+ start = offset;
+ offset = lseek64(file->fd, start, seekinfo[current].seektype);
+ }
+ return 0;
+}
+
+void
+seek_init(void)
+{
+ seek_cmd.name = "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);
+}