1*8b418c83SDag-Erling Smørgrav /*-
2*8b418c83SDag-Erling Smørgrav * Copyright (c) 2023 Klara, Inc.
3*8b418c83SDag-Erling Smørgrav *
4*8b418c83SDag-Erling Smørgrav * SPDX-License-Identifier: BSD-2-Clause
5*8b418c83SDag-Erling Smørgrav */
6*8b418c83SDag-Erling Smørgrav
7*8b418c83SDag-Erling Smørgrav #include <err.h>
8*8b418c83SDag-Erling Smørgrav #include <fcntl.h>
9*8b418c83SDag-Erling Smørgrav #include <stdbool.h>
10*8b418c83SDag-Erling Smørgrav #include <stdio.h>
11*8b418c83SDag-Erling Smørgrav #include <stdlib.h>
12*8b418c83SDag-Erling Smørgrav #include <sysexits.h>
13*8b418c83SDag-Erling Smørgrav #include <unistd.h>
14*8b418c83SDag-Erling Smørgrav
15*8b418c83SDag-Erling Smørgrav static bool verbose;
16*8b418c83SDag-Erling Smørgrav
17*8b418c83SDag-Erling Smørgrav /*
18*8b418c83SDag-Erling Smørgrav * Returns true if the file named by its argument is sparse, i.e. if
19*8b418c83SDag-Erling Smørgrav * seeking to SEEK_HOLE returns a different value than seeking to
20*8b418c83SDag-Erling Smørgrav * SEEK_END.
21*8b418c83SDag-Erling Smørgrav */
22*8b418c83SDag-Erling Smørgrav static bool
sparse(const char * filename)23*8b418c83SDag-Erling Smørgrav sparse(const char *filename)
24*8b418c83SDag-Erling Smørgrav {
25*8b418c83SDag-Erling Smørgrav off_t hole, end;
26*8b418c83SDag-Erling Smørgrav int fd;
27*8b418c83SDag-Erling Smørgrav
28*8b418c83SDag-Erling Smørgrav if ((fd = open(filename, O_RDONLY)) < 0 ||
29*8b418c83SDag-Erling Smørgrav (hole = lseek(fd, 0, SEEK_HOLE)) < 0 ||
30*8b418c83SDag-Erling Smørgrav (end = lseek(fd, 0, SEEK_END)) < 0)
31*8b418c83SDag-Erling Smørgrav err(1, "%s", filename);
32*8b418c83SDag-Erling Smørgrav close(fd);
33*8b418c83SDag-Erling Smørgrav if (end > hole) {
34*8b418c83SDag-Erling Smørgrav if (verbose)
35*8b418c83SDag-Erling Smørgrav printf("%s: hole at %zu\n", filename, (size_t)hole);
36*8b418c83SDag-Erling Smørgrav return (true);
37*8b418c83SDag-Erling Smørgrav }
38*8b418c83SDag-Erling Smørgrav return (false);
39*8b418c83SDag-Erling Smørgrav }
40*8b418c83SDag-Erling Smørgrav
41*8b418c83SDag-Erling Smørgrav static void
usage(void)42*8b418c83SDag-Erling Smørgrav usage(void)
43*8b418c83SDag-Erling Smørgrav {
44*8b418c83SDag-Erling Smørgrav
45*8b418c83SDag-Erling Smørgrav fprintf(stderr, "usage: sparse [-v] file [...]\n");
46*8b418c83SDag-Erling Smørgrav exit(EX_USAGE);
47*8b418c83SDag-Erling Smørgrav }
48*8b418c83SDag-Erling Smørgrav
49*8b418c83SDag-Erling Smørgrav int
main(int argc,char * argv[])50*8b418c83SDag-Erling Smørgrav main(int argc, char *argv[])
51*8b418c83SDag-Erling Smørgrav {
52*8b418c83SDag-Erling Smørgrav int opt, rv;
53*8b418c83SDag-Erling Smørgrav
54*8b418c83SDag-Erling Smørgrav while ((opt = getopt(argc, argv, "v")) != -1) {
55*8b418c83SDag-Erling Smørgrav switch (opt) {
56*8b418c83SDag-Erling Smørgrav case 'v':
57*8b418c83SDag-Erling Smørgrav verbose = true;
58*8b418c83SDag-Erling Smørgrav break;
59*8b418c83SDag-Erling Smørgrav default:
60*8b418c83SDag-Erling Smørgrav usage();
61*8b418c83SDag-Erling Smørgrav break;
62*8b418c83SDag-Erling Smørgrav }
63*8b418c83SDag-Erling Smørgrav }
64*8b418c83SDag-Erling Smørgrav argc -= optind;
65*8b418c83SDag-Erling Smørgrav argv += optind;
66*8b418c83SDag-Erling Smørgrav if (argc == 0)
67*8b418c83SDag-Erling Smørgrav usage();
68*8b418c83SDag-Erling Smørgrav rv = EXIT_SUCCESS;
69*8b418c83SDag-Erling Smørgrav while (argc-- > 0)
70*8b418c83SDag-Erling Smørgrav if (!sparse(*argv++))
71*8b418c83SDag-Erling Smørgrav rv = EXIT_FAILURE;
72*8b418c83SDag-Erling Smørgrav exit(rv);
73*8b418c83SDag-Erling Smørgrav }
74