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