-
Notifications
You must be signed in to change notification settings - Fork 203
/
Copy pathdistro.py
267 lines (238 loc) · 11.2 KB
/
distro.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
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Name: distro.py
# Purpose: Module to detect if distro types supported by multibootusb (by extracting specific files)
# Authors: Sundar
# Licence: This file is a part of multibootusb package. You can redistribute it or modify
# under the terms of GNU General Public License, v.2 or above
from functools import partial
import os
import platform
import re
from . import _7zip
from .gen import *
from . import iso
from .isodump3 import ISO9660
def distro(iso_cfg_ext_dir, iso_link, expose_exception=False):
"""
Detect if distro is supported by multibootusb.
:param iso_cfg_ext_dir: Directory where *.cfg files are extracted.
:return: Detected distro name as string.
"""
# iso9660fs = ISO9660(iso_link)
# iso_file_list = iso9660fs.readDir("/")
distro = None # tenatively set to None
iso_file_list = _7zip.list_iso(
iso_link, expose_exception=expose_exception)
iso_file_list_lower = [f.lower() for f in iso_file_list]
v_isolinux_bin_exists = iso.isolinux_bin_exist(iso_link)
# Let's have less costly checks first.
# We'll have to make these checks as strictive as possible
# so that keyword based tests will not be skipped because
# of a false positive.
if iso_file_list:
distro = perform_strict_detections(iso_cfg_ext_dir, iso_file_list)
if distro:
return distro
distro = detect_iso_from_file_list(iso_file_list)
if distro:
return distro
else:
iso_file_list = []
def run_contains(keywords, filename, file_content, iso_flielist,
isolinux_bin_exists):
return any(k in file_content for k in keywords.split('|'))
def contains(keywords):
return partial(run_contains, keywords.lower())
def run_file_exists(filename_sought, filename, file_content, iso_filelist,
isolinux_bin_exists):
return filename_sought in iso_filelist
def file_exists(filename_sought):
return partial(run_file_exists, filename_sought)
def run_isolinux_bin_exists(exist_or_not, filename, file_content,
iso_filelist, isolinux_bin_exists):
return exist_or_not is isolinux_bin_exists
def isolinux_bin_exists(exist_or_not):
return partial(run_isolinux_bin_exists, exist_or_not)
def run_not(predicate, filename, file_content,
iso_filelist, isolinux_bin_exists):
return not predicate(filename, file_content, iso_filelist,
isolinux_bin_exists)
def not_(predicate):
return partial(run_not, predicate)
# contains(X) predicates that X is contained in an examined text file.
# Multiple keywords can be concatenated by '|'. Predicates gets aaserted
# if anyone of the keywords is found in the text file.
# Sorry you can't include | in a keyword for now.
test_vector = [
('ubcd', contains('ubcd')),
('hbcd', contains('hbcd')),
('systemrescuecd', contains('systemrescuecd')),
('parted-magic', [contains('pmagic|partedmagic'),
isolinux_bin_exists(True)]),
# mounting fat filesystem hard coded in to initrd.
# Can be modified only under linux.
('mageialive', contains('mgalive')),
('arch', contains('archisolabel|misolabel|parabolaisolabel')),
('chakra', contains('chakraisolabel')),
('kaos', contains('kdeosisolabel')),
('debian', [contains('boot=live'), isolinux_bin_exists(True)]),
('grml', [contains('grml'), contains('live-media-path')]),
('debian-install', [contains('debian-installer'),
not_(file_exists('casper'))]),
('solydx', contains('solydx')),
('knoppix', contains('knoppix')),
('centos', contains('root=live:CDLABEL=CentOS')),
('fedora', contains('root=live:CDLABEL=|root=live:LABEL=')),
('fedora', contains('redcore')),
('redhat', contains('redhat')),
('slitaz', contains('slitaz|dban |ophcrack|tinycore|rescue.cpi'
'|xpud|untangle|4mlinux|partition wizard'
'|android-x86.png|riplinux|lebel dummy'
'|http://pogostick.net/~pnh/ntpasswd/'
'|AVG Rescue CD|AntivirusLiveCD'
'|lkrn|Nanolinux|OSForensics|PING')),
('slitaz', contains('minimal Slackware|Slackware-HOWTO')),
#('suse', contains('suse')),
('opensuse-install', contains('class opensuse')),
('ubuntu', contains('boot=casper')),
('wifislax', contains('wifislax')),
('slax', contains('slax')),
('sms', [contains('sms.jpg|vector |autoexec'),
isolinux_bin_exists(True)]),
('antix', contains('antix')),
('porteus', contains('porteus')),
('pclinuxos', contains('livecd=livecd|PCLinuxOS')),
('gentoo', contains('looptype=squashfs|http://dee.su/liberte')),
('finnix', contains('finnix')),
('wifiway', contains('wifiway')),
('puppy', contains('puppy|quirky|fatdog|slacko|xenialpup')),
('ipcop', contains('ipcop')),
('ipfire', contains('ipfire')),
('salix-live', [contains('zenwalk|slack|salix'),
contains('live')]),
('zenwalk', contains('zenwalk|slack|salix')),
('ubuntu-server', contains('ubuntu server')),
('centos-install', contains('Install CentOS')),
('centos', contains('centos')),
('trinity-rescue', contains('Trinity Rescue Kit')),
('alpine', contains('alpine')),
('kaspersky', contains('http://support.kaspersky.com')),
('alt-linux', contains('ALT Linux')),
('Windows', contains('Sergei Strelec')),
('ReactOS', contains('ReactOS')),
('fsecure', contains('fsecure')),
('pc-unlocker', contains('default rwp')),
('pc-tool', contains('/system/stage1')),
('grub2only', contains('vba32rescue')),
('rising-av', contains('BOOT_IMAGE=rising')),
('Avira-RS', contains('Avira Rescue System')),
('insert', contains('BOOT_IMAGE=insert')),
('sgrubd2', contains('Super Grub Disk')),
]
# I'm not sure if this platform check is necessary but I will
# avoid removal to not alter the behaviour.
if platform.system() == "Linux" or platform.system() == "Windows":
for path, subdirs, files in os.walk(iso_cfg_ext_dir):
for name in files:
name_lower = name.lower()
if not name_lower.endswith(('.cfg', '.txt', '.lst')):
continue
if name_lower=='i18n.cfg':
# i18n.cfg in salitaz-rolling cause misdetection
# of centos by the following line.
# MENU LABEL English US (acentos)
continue
try:
# errors='ignore' is required as some files also
# contain non utf character
string = open(os.path.join(path, name),
errors='ignore').read()
except IOError:
log("Read Error on %s." % name)
continue
for distro_, predicate in test_vector:
predicates = [predicate] if callable(predicate) \
else predicate
if all( p(name_lower, string.lower(), iso_file_list_lower,
v_isolinux_bin_exists) for p in predicates ):
return distro_
if distro:
return distro
# FIXME: See the below comments.
# else:
# # FIXME: The idea of detecting as generic is to work like a unetbootin if other methods fails.
# # This simply extracts distro to root of the USB and install syslinux on isolinux.bin directory.
# # All works fine but unable to boot the distro successfully. Also, see the generic section from
# # syslinux, update_cfg and install_distro modules.
# if self.isolinux_bin_exist():
# return "generic"
elif str(iso_link).lower().endswith('.iso'):
return 'memdisk_iso'
elif str(iso_link).lower().endswith('.img'):
return 'memdisk_img'
else:
return None
def detect_iso_from_file_list(iso_file_list):
"""
Fallback detection script from the content of an ISO.
:return: supported distro as string
"""
keys_to_distro = [
(['f4ubcd'], 'f4ubcd'),
(['alpine-release'], 'alpine'),
(['sources', 'boot.wim'], 'Windows'),
(['config.isoclient'], 'opensuse'),
(['dban'], 'slitaz'),
(['memtest.img'], 'memtest'),
(['mt86.png', 'isolinux'], 'raw_iso'),
(['menu.lst'], 'grub4dos'),
(['bootwiz.cfg', 'bootmenu_logo.png'], 'grub4dos_iso') ]
filenames = [f.lower() for f in iso_file_list]
for keys, distro in keys_to_distro:
match = True
for k in keys:
if all(k not in fn for fn in filenames):
match = False
break
if match is True:
return distro
#log("Examined %d %s in the iso but could not determine the distro."
# % (len(filenames), len(filenames)==1 and 'filename' or 'filenames'))
return None
def perform_strict_detections(iso_cfg_ext_dir, iso_file_list):
def run_contains(filepath, keyword, cfg_dir=iso_cfg_ext_dir):
fullpath = os.path.join(cfg_dir, filepath.replace('/', os.sep))
if not os.path.exists(fullpath):
return False
try:
with open(fullpath, 'rb') as f:
data = f.read().lower()
return keyword in data
except (IOError, OSError):
log("Failed to open %s" % fullpath)
return False
def contains(relataive_filepath, keyword):
return partial(run_contains, relataive_filepath,
bytes(keyword.lower(),'us-ascii'))
# contains(P, K) predicates that file P contains the specified
# string K. The predicate get never asserted if P has not been
# extracted into the staging area (iso_cfg_ext_dir).
test_vector = [
('wifislax', contains('boot/syslinux/menu/vesamenu.cfg',
'menu label Wifislax64 Live')),
('salix-live', contains('boot/menus/mainmenu.cfg',
'MENU LABEL SALIX LIVE')),
('grml', contains('boot/isolinux/vesamenu.cfg',
'menu title Grml - Live Linux'))
]
for distro, predicate in test_vector:
predicates = [predicate] if callable(predicate) else predicate
if all(p() for p in predicates):
return distro
return None
if __name__ == '__main__':
iso_cfg_ext_dir = os.path.join(multibootusb_host_dir(), "iso_cfg_ext_dir")
iso_link = 'Downloads/clonezilla-live-2.4.2-32-amd64.iso'
iso_extract_file(iso_link, iso_cfg_ext_dir, 'cfg')
log(distro(iso_cfg_ext_dir))