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 15static 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 */ 22static bool 23sparse(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 41static void 42usage(void) 43{ 44 45 fprintf(stderr, "usage: sparse [-v] file [...]\n"); 46 exit(EX_USAGE); 47} 48 49int 50main(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