1*52244c09SJohn Wren Kennedy /* 2*52244c09SJohn Wren Kennedy * This file and its contents are supplied under the terms of the 3*52244c09SJohn Wren Kennedy * Common Development and Distribution License ("CDDL"), version 1.0. 4*52244c09SJohn Wren Kennedy * You may only use this file in accordance with the terms of version 5*52244c09SJohn Wren Kennedy * 1.0 of the CDDL. 6*52244c09SJohn Wren Kennedy * 7*52244c09SJohn Wren Kennedy * A full copy of the text of the CDDL should have accompanied this 8*52244c09SJohn Wren Kennedy * source. A copy of the CDDL is also available via the Internet at 9*52244c09SJohn Wren Kennedy * http://www.illumos.org/license/CDDL. 10*52244c09SJohn Wren Kennedy */ 11*52244c09SJohn Wren Kennedy 12*52244c09SJohn Wren Kennedy /* 13*52244c09SJohn Wren Kennedy * Copyright (c) 2014 by Delphix. All rights reserved. 14*52244c09SJohn Wren Kennedy */ 15*52244c09SJohn Wren Kennedy 16*52244c09SJohn Wren Kennedy #include <stdio.h> 17*52244c09SJohn Wren Kennedy #include <fcntl.h> 18*52244c09SJohn Wren Kennedy #include <unistd.h> 19*52244c09SJohn Wren Kennedy #include <libzfs.h> 20*52244c09SJohn Wren Kennedy #include <umem.h> 21*52244c09SJohn Wren Kennedy #include <stdlib.h> 22*52244c09SJohn Wren Kennedy #include <stddef.h> 23*52244c09SJohn Wren Kennedy #include <sys/types.h> 24*52244c09SJohn Wren Kennedy #include <sys/list.h> 25*52244c09SJohn Wren Kennedy #include <sys/stat.h> 26*52244c09SJohn Wren Kennedy #include <sys/errno.h> 27*52244c09SJohn Wren Kennedy 28*52244c09SJohn Wren Kennedy #define PRINT_HOLE 0x1 29*52244c09SJohn Wren Kennedy #define PRINT_DATA 0x2 30*52244c09SJohn Wren Kennedy #define PRINT_VERBOSE 0x4 31*52244c09SJohn Wren Kennedy 32*52244c09SJohn Wren Kennedy extern int errno; 33*52244c09SJohn Wren Kennedy 34*52244c09SJohn Wren Kennedy static void 35*52244c09SJohn Wren Kennedy usage(char *msg, int exit_value) 36*52244c09SJohn Wren Kennedy { 37*52244c09SJohn Wren Kennedy (void) fprintf(stderr, "getholes [-dhv] filename\n"); 38*52244c09SJohn Wren Kennedy (void) fprintf(stderr, "%s\n", msg); 39*52244c09SJohn Wren Kennedy exit(exit_value); 40*52244c09SJohn Wren Kennedy } 41*52244c09SJohn Wren Kennedy 42*52244c09SJohn Wren Kennedy typedef struct segment { 43*52244c09SJohn Wren Kennedy list_node_t seg_node; 44*52244c09SJohn Wren Kennedy int seg_type; 45*52244c09SJohn Wren Kennedy off_t seg_offset; 46*52244c09SJohn Wren Kennedy off_t seg_len; 47*52244c09SJohn Wren Kennedy } seg_t; 48*52244c09SJohn Wren Kennedy 49*52244c09SJohn Wren Kennedy /* 50*52244c09SJohn Wren Kennedy * Return an appropriate whence value, depending on whether the file begins 51*52244c09SJohn Wren Kennedy * with a holes or data. 52*52244c09SJohn Wren Kennedy */ 53*52244c09SJohn Wren Kennedy static int 54*52244c09SJohn Wren Kennedy starts_with_hole(int fd) 55*52244c09SJohn Wren Kennedy { 56*52244c09SJohn Wren Kennedy off_t off; 57*52244c09SJohn Wren Kennedy 58*52244c09SJohn Wren Kennedy if ((off = lseek(fd, 0, SEEK_HOLE)) == -1) { 59*52244c09SJohn Wren Kennedy /* ENXIO means no holes were found */ 60*52244c09SJohn Wren Kennedy if (errno == ENXIO) 61*52244c09SJohn Wren Kennedy return (SEEK_DATA); 62*52244c09SJohn Wren Kennedy perror("lseek failed"); 63*52244c09SJohn Wren Kennedy exit(1); 64*52244c09SJohn Wren Kennedy } 65*52244c09SJohn Wren Kennedy 66*52244c09SJohn Wren Kennedy return (off == 0 ? SEEK_HOLE : SEEK_DATA); 67*52244c09SJohn Wren Kennedy } 68*52244c09SJohn Wren Kennedy 69*52244c09SJohn Wren Kennedy static void 70*52244c09SJohn Wren Kennedy print_list(list_t *seg_list, char *fname, int options) 71*52244c09SJohn Wren Kennedy { 72*52244c09SJohn Wren Kennedy uint64_t lz_holes, bs = 0; 73*52244c09SJohn Wren Kennedy uint64_t hole_blks_seen = 0, data_blks_seen = 0; 74*52244c09SJohn Wren Kennedy seg_t *seg; 75*52244c09SJohn Wren Kennedy 76*52244c09SJohn Wren Kennedy if (0 == bs) 77*52244c09SJohn Wren Kennedy if (zfs_get_hole_count(fname, &lz_holes, &bs) != 0) { 78*52244c09SJohn Wren Kennedy perror("zfs_get_hole_count"); 79*52244c09SJohn Wren Kennedy exit(1); 80*52244c09SJohn Wren Kennedy } 81*52244c09SJohn Wren Kennedy 82*52244c09SJohn Wren Kennedy while ((seg = list_remove_head(seg_list)) != NULL) { 83*52244c09SJohn Wren Kennedy if (options & PRINT_VERBOSE) 84*52244c09SJohn Wren Kennedy (void) fprintf(stdout, "%c %llu:%llu\n", 85*52244c09SJohn Wren Kennedy seg->seg_type == SEEK_HOLE ? 'h' : 'd', 86*52244c09SJohn Wren Kennedy seg->seg_offset, seg->seg_len); 87*52244c09SJohn Wren Kennedy 88*52244c09SJohn Wren Kennedy if (seg->seg_type == SEEK_HOLE) { 89*52244c09SJohn Wren Kennedy hole_blks_seen += seg->seg_len / bs; 90*52244c09SJohn Wren Kennedy } else { 91*52244c09SJohn Wren Kennedy data_blks_seen += seg->seg_len / bs; 92*52244c09SJohn Wren Kennedy } 93*52244c09SJohn Wren Kennedy umem_free(seg, sizeof (seg_t)); 94*52244c09SJohn Wren Kennedy } 95*52244c09SJohn Wren Kennedy 96*52244c09SJohn Wren Kennedy /* Verify libzfs sees the same number of hole blocks found manually. */ 97*52244c09SJohn Wren Kennedy if (lz_holes != hole_blks_seen) { 98*52244c09SJohn Wren Kennedy (void) fprintf(stderr, "Counted %llu holes, but libzfs found " 99*52244c09SJohn Wren Kennedy "%llu\n", hole_blks_seen, lz_holes); 100*52244c09SJohn Wren Kennedy exit(1); 101*52244c09SJohn Wren Kennedy } 102*52244c09SJohn Wren Kennedy 103*52244c09SJohn Wren Kennedy if (options & PRINT_HOLE && options & PRINT_DATA) { 104*52244c09SJohn Wren Kennedy (void) fprintf(stdout, "datablks: %llu\n", data_blks_seen); 105*52244c09SJohn Wren Kennedy (void) fprintf(stdout, "holeblks: %llu\n", hole_blks_seen); 106*52244c09SJohn Wren Kennedy return; 107*52244c09SJohn Wren Kennedy } 108*52244c09SJohn Wren Kennedy 109*52244c09SJohn Wren Kennedy if (options & PRINT_DATA) 110*52244c09SJohn Wren Kennedy (void) fprintf(stdout, "%llu\n", data_blks_seen); 111*52244c09SJohn Wren Kennedy if (options & PRINT_HOLE) 112*52244c09SJohn Wren Kennedy (void) fprintf(stdout, "%llu\n", hole_blks_seen); 113*52244c09SJohn Wren Kennedy } 114*52244c09SJohn Wren Kennedy 115*52244c09SJohn Wren Kennedy int 116*52244c09SJohn Wren Kennedy main(int argc, char *argv[]) 117*52244c09SJohn Wren Kennedy { 118*52244c09SJohn Wren Kennedy off_t len, off = 0; 119*52244c09SJohn Wren Kennedy int c, fd, options = 0, whence = SEEK_DATA; 120*52244c09SJohn Wren Kennedy struct stat statbuf; 121*52244c09SJohn Wren Kennedy char *fname; 122*52244c09SJohn Wren Kennedy list_t seg_list; 123*52244c09SJohn Wren Kennedy seg_t *seg = NULL; 124*52244c09SJohn Wren Kennedy 125*52244c09SJohn Wren Kennedy list_create(&seg_list, sizeof (seg_t), offsetof(seg_t, seg_node)); 126*52244c09SJohn Wren Kennedy 127*52244c09SJohn Wren Kennedy while ((c = getopt(argc, argv, "dhv")) != -1) { 128*52244c09SJohn Wren Kennedy switch (c) { 129*52244c09SJohn Wren Kennedy case 'd': 130*52244c09SJohn Wren Kennedy options |= PRINT_DATA; 131*52244c09SJohn Wren Kennedy break; 132*52244c09SJohn Wren Kennedy case 'h': 133*52244c09SJohn Wren Kennedy options |= PRINT_HOLE; 134*52244c09SJohn Wren Kennedy break; 135*52244c09SJohn Wren Kennedy case 'v': 136*52244c09SJohn Wren Kennedy options |= PRINT_VERBOSE; 137*52244c09SJohn Wren Kennedy break; 138*52244c09SJohn Wren Kennedy } 139*52244c09SJohn Wren Kennedy } 140*52244c09SJohn Wren Kennedy argc -= optind; 141*52244c09SJohn Wren Kennedy argv += optind; 142*52244c09SJohn Wren Kennedy 143*52244c09SJohn Wren Kennedy if (argc != 1) 144*52244c09SJohn Wren Kennedy usage("Incorrect number of arguments.", 1); 145*52244c09SJohn Wren Kennedy 146*52244c09SJohn Wren Kennedy if ((fname = argv[0]) == NULL) 147*52244c09SJohn Wren Kennedy usage("No filename provided.", 1); 148*52244c09SJohn Wren Kennedy 149*52244c09SJohn Wren Kennedy if ((fd = open(fname, O_LARGEFILE | O_RDONLY)) < 0) { 150*52244c09SJohn Wren Kennedy perror("open failed"); 151*52244c09SJohn Wren Kennedy exit(1); 152*52244c09SJohn Wren Kennedy } 153*52244c09SJohn Wren Kennedy 154*52244c09SJohn Wren Kennedy if (fstat(fd, &statbuf) != 0) { 155*52244c09SJohn Wren Kennedy perror("fstat failed"); 156*52244c09SJohn Wren Kennedy exit(1); 157*52244c09SJohn Wren Kennedy } 158*52244c09SJohn Wren Kennedy len = statbuf.st_size; 159*52244c09SJohn Wren Kennedy 160*52244c09SJohn Wren Kennedy whence = starts_with_hole(fd); 161*52244c09SJohn Wren Kennedy while ((off = lseek(fd, off, whence)) != -1) { 162*52244c09SJohn Wren Kennedy seg_t *s; 163*52244c09SJohn Wren Kennedy 164*52244c09SJohn Wren Kennedy seg = umem_alloc(sizeof (seg_t), UMEM_DEFAULT); 165*52244c09SJohn Wren Kennedy seg->seg_type = whence; 166*52244c09SJohn Wren Kennedy seg->seg_offset = off; 167*52244c09SJohn Wren Kennedy 168*52244c09SJohn Wren Kennedy list_insert_tail(&seg_list, seg); 169*52244c09SJohn Wren Kennedy if ((s = list_prev(&seg_list, seg)) != NULL) 170*52244c09SJohn Wren Kennedy s->seg_len = seg->seg_offset - s->seg_offset; 171*52244c09SJohn Wren Kennedy 172*52244c09SJohn Wren Kennedy whence = whence == SEEK_HOLE ? SEEK_DATA : SEEK_HOLE; 173*52244c09SJohn Wren Kennedy } 174*52244c09SJohn Wren Kennedy if (errno != ENXIO) { 175*52244c09SJohn Wren Kennedy perror("lseek failed"); 176*52244c09SJohn Wren Kennedy exit(1); 177*52244c09SJohn Wren Kennedy } 178*52244c09SJohn Wren Kennedy (void) close(fd); 179*52244c09SJohn Wren Kennedy 180*52244c09SJohn Wren Kennedy /* 181*52244c09SJohn Wren Kennedy * If this file ends with a hole block, then populate the length of 182*52244c09SJohn Wren Kennedy * the last segment, otherwise this is the end of the file, so 183*52244c09SJohn Wren Kennedy * discard the remaining zero length segment. 184*52244c09SJohn Wren Kennedy */ 185*52244c09SJohn Wren Kennedy if (seg && seg->seg_offset != len) { 186*52244c09SJohn Wren Kennedy seg->seg_len = len - seg->seg_offset; 187*52244c09SJohn Wren Kennedy } else { 188*52244c09SJohn Wren Kennedy (void) list_remove_tail(&seg_list); 189*52244c09SJohn Wren Kennedy } 190*52244c09SJohn Wren Kennedy 191*52244c09SJohn Wren Kennedy print_list(&seg_list, fname, options); 192*52244c09SJohn Wren Kennedy list_destroy(&seg_list); 193*52244c09SJohn Wren Kennedy return (0); 194*52244c09SJohn Wren Kennedy } 195