|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Copyright (c) 2013 SGI | 
|  | * All Rights Reserved. | 
|  | */ | 
|  |  | 
|  | #include "command.h" | 
|  | #include "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")); | 
|  | } | 
|  |  | 
|  | /* 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 | 
|  |  | 
|  | static 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 */ | 
|  | static void | 
|  | seek_output( | 
|  | int	startflag, | 
|  | char	*type, | 
|  | off_t	start, | 
|  | off_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) | 
|  | { | 
|  | off_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: | 
|  | exitcode = 1; | 
|  | return command_usage(&seek_cmd); | 
|  | } | 
|  | } | 
|  | if (!(flag & (SEEK_DFLAG | SEEK_HFLAG)) || optind != argc - 1) { | 
|  | exitcode = 1; | 
|  | return command_usage(&seek_cmd); | 
|  | } | 
|  |  | 
|  | start = offset = cvtnum(fsblocksize, fssectsize, argv[optind]); | 
|  | if (offset < 0) { | 
|  | exitcode = 1; | 
|  | 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) { | 
|  | current = HOLE; | 
|  | offset = lseek(file->fd, start, SEEK_HOLE); | 
|  | if (offset != -1 && offset < start) | 
|  | goto bad_result; | 
|  | 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. | 
|  | */ | 
|  | goto found_hole; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* The offset is not a hole, or we are looking just for data */ | 
|  | current = DATA; | 
|  | offset = lseek(file->fd, start, SEEK_DATA); | 
|  | if (offset != -1 && offset < start) | 
|  | goto bad_result; | 
|  |  | 
|  | 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 = lseek(file->fd, start, seekinfo[current].seektype); | 
|  | if (offset != -1 && offset <= start) | 
|  | goto bad_result; | 
|  | } | 
|  | return 0; | 
|  |  | 
|  | bad_result: | 
|  | fprintf(stderr, "Invalid seek result: lseek(<fd>, %lld, SEEK_%s) = %lld\n", | 
|  | (long long)start, seekinfo[current].name, (long long)offset); | 
|  | 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); | 
|  | } |