xref: /freebsd/bin/cp/tests/sparse.c (revision ebacd8013fe5f7fdf9f6a5b286f6680dd2891036)
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
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
42 usage(void)
43 {
44 
45 	fprintf(stderr, "usage: sparse [-v] file [...]\n");
46 	exit(EX_USAGE);
47 }
48 
49 int
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