forked from ish-app/ish
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfakefsify.py
executable file
·100 lines (91 loc) · 2.84 KB
/
fakefsify.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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#!/usr/bin/env python3
import sys
from pathlib import Path
import struct
import urllib.request
import tarfile
import sqlite3
SCHEMA = """
create table meta (id integer unique default 0, db_inode integer);
insert into meta (db_inode) values (0);
create table paths (path blob primary key, inode integer);
create table stats (inode integer primary key, stat blob);
"""
# no index is needed on stats, because the rows are ordered by the primary key
def extract_member(archive, db, member):
path = data/(member.name)
major = member.devmajor
minor = member.devminor
rdev = ((minor & 0xfff00) << 12) | (major << 8) | (minor & 0xff)
mode = member.mode
if member.isfile():
mode |= 0o100000
elif member.isdir():
mode |= 0o040000
elif member.issym():
mode |= 0o120000
elif member.ischr():
mode |= 0o020000
elif member.isblk():
mode |= 0o060000
elif member.isfifo():
mode |= 0o010000
elif member.islnk():
pass
else:
raise ValueError('unrecognized tar entry type')
if member.isdir():
path.mkdir(parents=True, exist_ok=True)
elif member.issym():
path.write_text(member.linkname)
elif member.isfile():
archive.extract(member, data)
else:
path.touch()
cursor = db.cursor()
if member.islnk():
# a hard link shares its target's inode
target_path = data/(member.linkname)
inode = target_path.stat().st_ino
else:
statblob = memoryview(struct.pack(
'=iiii',
mode,
member.uid,
member.gid,
rdev,
))
cursor.execute('insert into stats (stat) values (?)', (statblob,))
inode = cursor.lastrowid
meta_path = path.relative_to(data)
meta_path = b'/' + bytes(meta_path) if meta_path.parts else b''
cursor.execute('insert into paths values (?, ?)', (meta_path, inode))
def extract_archive(archive, db):
for member in archive.getmembers():
# hack
if member.name == './etc/securetty':
continue
extract_member(archive, db, member)
devtty = tarfile.TarInfo('./dev/tty')
devtty.mode = 0o666
devtty.type = b'3'
devtty.devmajor = 5
devtty.devminor = 0
extract_member(archive, db, devtty)
try:
_, archive_path, fs = sys.argv
except ValueError:
print('wrong number of arguments')
print("Usage: fakefsify.py <rootfs archive> <destination dir>")
sys.exit(1)
fs = Path(fs)
fs.mkdir(parents=True, exist_ok=True)
data = fs/'data'
db_path = fs/'meta.db'
with open(archive_path, 'rb') as archive:
with tarfile.open(fileobj=archive) as archive:
db = sqlite3.connect(str(db_path))
db.executescript(SCHEMA)
extract_archive(archive, db)
db.execute('update meta set db_inode = ?', (db_path.stat().st_ino,))
db.commit()