-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlsholes.c
150 lines (118 loc) · 2.88 KB
/
lsholes.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#define _GNU_SOURCE
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#define ARRSZE(X) (sizeof(X) / sizeof(*(X)))
struct verbose_data {
off_t prev;
const char* fmt;
};
static void* verbose_init(void) {
struct verbose_data* ret = calloc(1, sizeof(struct verbose_data));
ret->fmt = "%s: %ld-%ld\n";
return ret;
};
static void* hex_init(void) {
struct verbose_data* ret = calloc(1, sizeof(struct verbose_data));
ret->fmt = "%s: 0x%016lx-0x%016lx\n";
return ret;
};
static void verbose_teardown(void* data) {
free(data);
};
static void verbose_loop(void* data, int whence, off_t offset) {
struct verbose_data* d = data;
if (offset == d->prev)
return;
const char* whence_str;
if (whence == SEEK_HOLE)
whence_str = "data";
else if (whence == SEEK_DATA)
whence_str = "hole";
else
errx(1, "unsupported \"whence\"");
printf(d->fmt, whence_str, d->prev, offset);
d->prev = offset;
}
struct count_data {
size_t count_holes, count_data;
off_t prev;
};
static void* count_init(void) {
return calloc(1, sizeof(struct count_data));
};
static void count_teardown(void* data) {
struct count_data* d = data;
printf("hole: %zu\n", d->count_holes);
printf("data: %zu\n", d->count_data);
free(data);
};
static void count_loop(void* data, int whence, off_t offset) {
struct count_data* d = data;
size_t *count_ptr = NULL;
if (whence == SEEK_HOLE)
count_ptr = &(d->count_holes);
else if (whence == SEEK_DATA)
count_ptr = &(d->count_data);
else
errx(1, "unsupported \"whence\"");
*count_ptr += offset - d->prev;
d->prev = offset;
}
struct {
void* (*init)(void);
void (*teardown)(void*);
void (*loop)(void*, int, off_t);
} modes[] = {
[0] = { verbose_init, verbose_teardown, verbose_loop },
['c'] = { count_init, count_teardown, count_loop },
['x'] = { hex_init, verbose_teardown, verbose_loop },
};;
static int do_file(const char* path, unsigned int mode) {
if (mode >= ARRSZE(modes) || !modes[mode].loop)
errx(1, "unsupported mode");
int fd = open(path, O_RDONLY);
if (fd < 0)
err(1, "open");
struct stat st;
if (fstat(fd, &st) < 0)
err(1, "fstat");
const int whences[2] = { SEEK_DATA, SEEK_HOLE };
void* data = modes[mode].init();
off_t cur = 0;
while (cur < st.st_size) {
for (size_t i = 0; i < ARRSZE(whences); i++) {
cur = lseek(fd, cur, whences[i]);
if (cur < 0) {
if (errno != ENXIO)
err(1, "lseek");
cur = st.st_size;
}
modes[mode].loop(data, whences[i], cur);
}
}
modes[mode].teardown(data);
if (close(fd) < 0)
err(1, "close");
return 0;
}
int main(int argc, char** argv) {
if (argc < 2)
errx(1, "Not enough arguments");
int opt;
unsigned int mode = 0;
while ((opt = getopt(argc, argv, "cx")) != -1) {
switch (opt) {
case 'c':
case 'x':
mode = opt;
break;
}
}
for (int i = optind; i < argc; i++)
do_file(argv[i], mode);
}