-
Notifications
You must be signed in to change notification settings - Fork 5
/
atruncate.py
executable file
·86 lines (60 loc) · 1.56 KB
/
atruncate.py
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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# Quick and dirty clone of atruncate
#
import sys
import os
import io
import re
if __name__ != "__main__":
raise Exception("This application is meant to be used stand-alone")
file_to_truncate = sys.argv[1]
BLOCK_SIZE = 2048 * 1024
ANDROID_SPARSE_IMAGE_BLOCK_SIZE = 4096
class ReversedFile(io.FileIO):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# To the end
self.file_end = os.path.getsize(self.name)
self.seek(self.file_end)
self._end_reached = False
def read(self, size=-1):
if self._end_reached:
return b""
if size > -1:
starting_point = self.tell() - size
if starting_point < 0:
self.seek(0)
block = super().read(size + starting_point)
self.seek(0)
self._end_reached = True
else:
self.seek(starting_point)
block = super().read(size)
self.seek(starting_point)
return block[::-1]
else:
return super().read(size)[::-1]
expr = re.compile(b"^\x00*")
with ReversedFile(file_to_truncate, "r+b") as f:
empty_block = b"\x00" * BLOCK_SIZE
end = 0
skipped = 0
while True:
block = f.read(BLOCK_SIZE)
if block == empty_block:
skipped += BLOCK_SIZE
continue
else:
end_result = expr.search(block)
if end is not None:
end = end_result.end()
skipped += end
break
# Account for block size alignment
when_to_truncate = f.file_end - skipped
nearest = when_to_truncate % ANDROID_SPARSE_IMAGE_BLOCK_SIZE
if nearest > 0:
when_to_truncate += (ANDROID_SPARSE_IMAGE_BLOCK_SIZE - nearest)
f.truncate(when_to_truncate)