20 bool is_nul(
void *buffer,
size_t count) {
22 size_t words = count /
sizeof(long);
24 for (
auto word =
static_cast<long*
>(buffer); words; --words) {
33 template <
typename T>
void printNumber(std::ostream& ost, T number) {
34 ost << std::dec << std::setw(16) << number;
35 ost <<
" (" << std::hex << std::showbase << std::setw(10) << number <<
" )";
39 template <
typename T1,
typename T2>
void printRegion(std::ostream& ost, T1
start, T2 end) {
48 int main(
int argc,
const char *argv[]) {
51 options::single<size_t> defaultBlockSize(
'b',
"blockSize",
"block size to search in (o: take from filesystem)", 0);
54 defaultBlockSize.
fRequire(&findZeroes);
55 auto remainingOptions = parser.
fParse(argc, argv);
59 for (
const auto& file : remainingOptions) {
63 std::cout << file <<
" has " << statbuf.st_size <<
" bytes, " <<
64 statbuf.st_blocks <<
" blocks, i.e. " << statbuf.st_blocks * 512 <<
" bytes allocated, " <<
65 (statbuf.st_blocks * 512.) /
static_cast<double>(statbuf.st_size) <<
" fill factor\n";
67 auto blockSize = statbuf.st_blksize;
68 if (defaultBlockSize != 0) {
69 blockSize = defaultBlockSize;
72 void* blkBuf =
nullptr;
75 PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE,
77 "mmap block buffer with ", blockSize,
" bytes");
81 std::vector<std::pair<int, std::string>> what({{SEEK_HOLE,
"data"}, {SEEK_DATA,
"hole"}});
82 for (decltype(statbuf.st_size) pos = 0, toggle = 1, region = 0;
83 pos < statbuf.st_size;
85 auto next = lseek(fd, pos, what.at(toggle).first);
86 if (next == -1 && errno == ENXIO) {
87 next = statbuf.st_size;
90 "seek ", file,
"for ", what.at(toggle).second,
91 ", from offset ", pos);
95 std::cout << std::setw(6) << region <<
": " << what.at(toggle).second <<
"\t";
98 if (findZeroes && what.at(toggle).first == SEEK_HOLE) {
99 size_t zeroBlocksStart = 0;
100 bool lastBlockWasData =
true;
101 for (
auto offset = pos; offset < next;) {
102 auto bytesRead = pread(fd, blkBuf, blockSize, offset);
103 if (
is_nul(blkBuf, blockSize)) {
104 if (lastBlockWasData) {
105 zeroBlocksStart = offset;
107 lastBlockWasData =
false;
109 if (!lastBlockWasData) {
110 std::cout <<
"\tzeroes\t";
114 lastBlockWasData =
true;
118 if (!lastBlockWasData) {
119 std::cout <<
"\tzeroes";
128 struct timespec times[2];
129 memcpy(×[0], &statbuf.st_atim,
sizeof(
struct timespec));
130 memcpy(×[1], &statbuf.st_mtim,
sizeof(
struct timespec));
131 throwcall::good0(futimens(fd, times),
"set tims stamps on ", file,
" to old values");
135 if (blkBuf !=
nullptr) {
136 throwcall::good0(munmap(blkBuf, blockSize),
"munmap block buffer with ", blockSize,
" bytes");