backupcompatibility.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. #
  2. # The Qubes OS Project, http://www.qubes-os.org
  3. #
  4. # Copyright (C) 2014 Marek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
  5. #
  6. # This library is free software; you can redistribute it and/or
  7. # modify it under the terms of the GNU Lesser General Public
  8. # License as published by the Free Software Foundation; either
  9. # version 2.1 of the License, or (at your option) any later version.
  10. #
  11. # This library is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. # Lesser General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU Lesser General Public
  17. # License along with this library; if not, see <https://www.gnu.org/licenses/>.
  18. #
  19. from multiprocessing import Queue
  20. import os
  21. import shutil
  22. import subprocess
  23. import unittest
  24. import sys
  25. import re
  26. import qubes.tests
  27. import qubes.tests.integ.backup
  28. QUBESXML_R2 = '''
  29. <QubesVmCollection updatevm="3" default_kernel="3.7.6-2" default_netvm="3" default_fw_netvm="2" default_template="1" clockvm="2">
  30. <QubesTemplateVm installed_by_rpm="True" kernel="3.7.6-2" uses_default_kernelopts="True" qid="1" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="fedora-20-x64.conf" label="black" template_qid="none" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{ 'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="fedora-20-x64" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/vm-templates/fedora-20-x64"/>
  31. <QubesNetVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="2" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="netvm.conf" label="red" template_qid="1" kernelopts="iommu=soft swiotlb=4096" memory="200" default_user="user" volatile_img="volatile.img" services="{'ntpd': False, 'meminfo-writer': False}" maxmem="1535" pcidevs="['02:00.0', '03:00.0']" name="netvm" netid="1" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/servicevms/netvm"/>
  32. <QubesProxyVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="3" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="firewallvm.conf" label="green" template_qid="1" kernelopts="" memory="200" default_user="user" netvm_qid="2" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="firewallvm" netid="2" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/servicevms/firewallvm"/>
  33. <QubesAppVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="4" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="True" conf_file="fedora-20-x64-dvm.conf" label="gray" template_qid="1" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{ 'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="fedora-20-x64-dvm" private_img="private.img" vcpus="1" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/fedora-20-x64-dvm"/>
  34. <QubesAppVm backup_content="True" backup_path="appvms/test-work" installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="5" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="test-work.conf" label="green" template_qid="1" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="test-work" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/test-work"/>
  35. <QubesAppVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="6" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="banking.conf" label="green" template_qid="1" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="banking" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/banking"/>
  36. <QubesAppVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="7" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="personal.conf" label="yellow" template_qid="1" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="personal" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/personal"/>
  37. <QubesAppVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="8" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="untrusted.conf" label="red" template_qid="1" kernelopts="" memory="400" default_user="user" netvm_qid="12" uses_default_netvm="False" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="untrusted" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/untrusted"/>
  38. <QubesTemplateVm backup_size="104857600" backup_content="True" backup_path="vm-templates/test-template-clone" installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="9" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="test-template-clone.conf" label="green" template_qid="none" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="test-template-clone" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/vm-templates/test-template-clone"/>
  39. <QubesAppVm backup_size="104857600" backup_content="True" backup_path="appvms/test-custom-template-appvm" installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="10" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="test-custom-template-appvm.conf" label="yellow" template_qid="9" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="test-custom-template-appvm" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/test-custom-template-appvm"/>
  40. <QubesAppVm backup_size="104857600" backup_content="True" backup_path="appvms/test-standalonevm" installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="11" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="test-standalonevm.conf" label="blue" template_qid="none" kernelopts="" memory="400" default_user="user" netvm_qid="3" uses_default_netvm="True" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="test-standalonevm" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/appvms/test-standalonevm"/>
  41. <QubesProxyVm backup_size="104857600" backup_content="True" backup_path="servicevms/test-testproxy" installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="12" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="test-testproxy.conf" label="red" template_qid="1" kernelopts="" memory="200" default_user="user" netvm_qid="3" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="test-testproxy" netid="3" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/servicevms/test-testproxy"/>
  42. <QubesProxyVm installed_by_rpm="False" kernel="3.7.6-2" uses_default_kernelopts="True" qid="13" include_in_backups="True" uses_default_kernel="True" qrexec_timeout="60" internal="False" conf_file="testproxy2.conf" label="red" template_qid="9" kernelopts="" memory="200" default_user="user" netvm_qid="2" volatile_img="volatile.img" services="{'meminfo-writer': True}" maxmem="1535" pcidevs="[]" name="testproxy2" netid="4" private_img="private.img" vcpus="2" root_img="root.img" debug="False" dir_path="/var/lib/qubes/servicevms/testproxy2"/>
  43. <QubesHVm backup_size="104857600" backup_content="True" backup_path="appvms/test-testhvm" installed_by_rpm="False" netvm_qid="none" qid="14" include_in_backups="True" timezone="localtime" qrexec_timeout="60" conf_file="test-testhvm.conf" label="purple" template_qid="none" internal="False" memory="512" uses_default_netvm="True" services="{'meminfo-writer': False}" default_user="user" pcidevs="[]" name="test-testhvm" qrexec_installed="False" private_img="private.img" drive="None" vcpus="2" root_img="root.img" guiagent_installed="False" debug="False" dir_path="/var/lib/qubes/appvms/test-testhvm"/>
  44. <QubesDisposableVm dispid="50" firewall_conf="firewall.xml" label="red" name="disp50" netvm_qid="2" qid="15" template_qid="1"/>
  45. </QubesVmCollection>
  46. '''
  47. MANGLED_SUBDIRS_R2 = {
  48. "test-work": "vm5",
  49. "test-template-clone": "vm9",
  50. "test-custom-template-appvm": "vm10",
  51. "test-standalonevm": "vm11",
  52. "test-testproxy": "vm12",
  53. "test-testhvm": "vm14",
  54. }
  55. APPTEMPLATE_R2B2 = '''
  56. [Desktop Entry]
  57. Name=%VMNAME%: {name}
  58. GenericName=%VMNAME%: {name}
  59. GenericName[ca]=%VMNAME%: Navegador web
  60. GenericName[cs]=%VMNAME%: Webový prohlížeč
  61. GenericName[es]=%VMNAME%: Navegador web
  62. GenericName[fa]=%VMNAME%: مرورر اینترنتی
  63. GenericName[fi]=%VMNAME%: WWW-selain
  64. GenericName[fr]=%VMNAME%: Navigateur Web
  65. GenericName[hu]=%VMNAME%: Webböngésző
  66. GenericName[it]=%VMNAME%: Browser Web
  67. GenericName[ja]=%VMNAME%: ウェブ・ブラウザ
  68. GenericName[ko]=%VMNAME%: 웹 브라우저
  69. GenericName[nb]=%VMNAME%: Nettleser
  70. GenericName[nl]=%VMNAME%: Webbrowser
  71. GenericName[nn]=%VMNAME%: Nettlesar
  72. GenericName[no]=%VMNAME%: Nettleser
  73. GenericName[pl]=%VMNAME%: Przeglądarka WWW
  74. GenericName[pt]=%VMNAME%: Navegador Web
  75. GenericName[pt_BR]=%VMNAME%: Navegador Web
  76. GenericName[sk]=%VMNAME%: Internetový prehliadač
  77. GenericName[sv]=%VMNAME%: Webbläsare
  78. Comment={comment}
  79. Comment[ca]=Navegueu per el web
  80. Comment[cs]=Prohlížení stránek World Wide Webu
  81. Comment[de]=Im Internet surfen
  82. Comment[es]=Navegue por la web
  83. Comment[fa]=صفحات شبه جهانی اینترنت را مرور نمایید
  84. Comment[fi]=Selaa Internetin WWW-sivuja
  85. Comment[fr]=Navigue sur Internet
  86. Comment[hu]=A világháló böngészése
  87. Comment[it]=Esplora il web
  88. Comment[ja]=ウェブを閲覧します
  89. Comment[ko]=웹을 돌아 다닙니다
  90. Comment[nb]=Surf på nettet
  91. Comment[nl]=Verken het internet
  92. Comment[nn]=Surf på nettet
  93. Comment[no]=Surf på nettet
  94. Comment[pl]=Przeglądanie stron WWW
  95. Comment[pt]=Navegue na Internet
  96. Comment[pt_BR]=Navegue na Internet
  97. Comment[sk]=Prehliadanie internetu
  98. Comment[sv]=Surfa på webben
  99. Exec=qvm-run -q --tray -a %VMNAME% '{command} %u'
  100. Categories=Network;WebBrowser;
  101. X-Qubes-VmName=%VMNAME%
  102. Icon=%VMDIR%/icon.png
  103. '''
  104. BACKUP_HEADER_R2 = '''version=3
  105. hmac-algorithm=SHA512
  106. crypto-algorithm=aes-256-cbc
  107. encrypted={encrypted}
  108. compressed={compressed}
  109. compression-filter=gzip
  110. '''
  111. class TC_00_BackupCompatibility(
  112. qubes.tests.integ.backup.BackupTestsMixin, qubes.tests.SystemTestCase):
  113. def tearDown(self):
  114. self.remove_test_vms(prefix="test-")
  115. super(TC_00_BackupCompatibility, self).tearDown()
  116. def create_whitelisted_appmenus(self, filename):
  117. f = open(filename, "w")
  118. f.write("gnome-terminal.desktop\n")
  119. f.write("nautilus.desktop\n")
  120. f.write("firefox.desktop\n")
  121. f.write("mozilla-thunderbird.desktop\n")
  122. f.write("libreoffice-startcenter.desktop\n")
  123. f.close()
  124. def create_appmenus(self, dir, template, list):
  125. for name in list:
  126. f = open(os.path.join(dir, name + ".desktop"), "w")
  127. f.write(template.format(name=name, comment=name, command=name))
  128. f.close()
  129. def create_private_img(self, filename):
  130. self.create_sparse(filename, 2*2**30)
  131. subprocess.check_call(["/usr/sbin/mkfs.ext4", "-q", "-F", filename])
  132. def create_volatile_img(self, filename):
  133. self.create_sparse(filename, 11.5*2**30)
  134. # here used to be sfdisk call with "0,1024,S\n,10240,L\n" input,
  135. # but since sfdisk folks like to change command arguments in
  136. # incompatible way, have an partition table verbatim here
  137. ptable = (
  138. '\x00\x00\x00\x00\x00\x00\x00\x00\xab\x39\xd5\xd4\x00\x00\x20\x00'
  139. '\x00\x21\xaa\x82\x82\x28\x08\x00\x00\x00\x00\x00\x00\x20\xaa\x00'
  140. '\x82\x29\x15\x83\x9c\x79\x08\x00\x00\x20\x00\x00\x01\x40\x00\x00'
  141. '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
  142. '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xaa\x55'
  143. )
  144. with open(filename, 'r+') as f:
  145. f.seek(0x1b0)
  146. f.write(ptable)
  147. # TODO: mkswap
  148. def fullpath(self, name):
  149. return os.path.join(self.backupdir, name)
  150. def create_v1_files(self, r2b2=False):
  151. appmenus_list = [
  152. "firefox", "gnome-terminal", "evince", "evolution",
  153. "mozilla-thunderbird", "libreoffice-startcenter", "nautilus",
  154. "gedit", "gpk-update-viewer", "gpk-application"
  155. ]
  156. os.mkdir(self.fullpath("appvms"))
  157. os.mkdir(self.fullpath("servicevms"))
  158. os.mkdir(self.fullpath("vm-templates"))
  159. # normal AppVM
  160. os.mkdir(self.fullpath("appvms/test-work"))
  161. self.create_whitelisted_appmenus(self.fullpath(
  162. "appvms/test-work/whitelisted-appmenus.list"))
  163. os.symlink("/usr/share/qubes/icons/green.png",
  164. self.fullpath("appvms/test-work/icon.png"))
  165. self.create_private_img(self.fullpath("appvms/test-work/private.img"))
  166. # StandaloneVM
  167. os.mkdir(self.fullpath("appvms/test-standalonevm"))
  168. self.create_whitelisted_appmenus(self.fullpath(
  169. "appvms/test-standalonevm/whitelisted-appmenus.list"))
  170. os.symlink("/usr/share/qubes/icons/blue.png",
  171. self.fullpath("appvms/test-standalonevm/icon.png"))
  172. self.create_private_img(self.fullpath(
  173. "appvms/test-standalonevm/private.img"))
  174. self.create_sparse(
  175. self.fullpath("appvms/test-standalonevm/root.img"), 10*2**30)
  176. self.fill_image(self.fullpath("appvms/test-standalonevm/root.img"),
  177. 100*1024*1024, True)
  178. os.mkdir(self.fullpath("appvms/test-standalonevm/apps.templates"))
  179. self.create_appmenus(self.fullpath("appvms/test-standalonevm/apps"
  180. ".templates"),
  181. APPTEMPLATE_R2B2,
  182. appmenus_list)
  183. os.mkdir(self.fullpath("appvms/test-standalonevm/kernels"))
  184. for k_file in ["initramfs", "vmlinuz", "modules.img"]:
  185. self.fill_image(self.fullpath("appvms/test-standalonevm/kernels/"
  186. + k_file), 10*1024*1024)
  187. # VM based on custom template
  188. subprocess.check_call(
  189. ["/bin/cp", "-a", self.fullpath("appvms/test-work"),
  190. self.fullpath("appvms/test-custom-template-appvm")])
  191. # HVM
  192. if r2b2:
  193. subprocess.check_call(
  194. ["/bin/cp", "-a", self.fullpath("appvms/test-standalonevm"),
  195. self.fullpath("appvms/test-testhvm")])
  196. # ProxyVM
  197. os.mkdir(self.fullpath("servicevms/test-testproxy"))
  198. self.create_whitelisted_appmenus(self.fullpath(
  199. "servicevms/test-testproxy/whitelisted-appmenus.list"))
  200. self.create_private_img(
  201. self.fullpath("servicevms/test-testproxy/private.img"))
  202. # Custom template
  203. os.mkdir(self.fullpath("vm-templates/test-template-clone"))
  204. self.create_private_img(
  205. self.fullpath("vm-templates/test-template-clone/private.img"))
  206. self.create_sparse(self.fullpath(
  207. "vm-templates/test-template-clone/root-cow.img"), 10*2**30)
  208. self.create_sparse(self.fullpath(
  209. "vm-templates/test-template-clone/root.img"), 10*2**30)
  210. self.fill_image(self.fullpath(
  211. "vm-templates/test-template-clone/root.img"), 1*2**30, True)
  212. self.create_volatile_img(self.fullpath(
  213. "vm-templates/test-template-clone/volatile.img"))
  214. subprocess.check_call([
  215. "/bin/tar", "cS",
  216. "-f", self.fullpath(
  217. "vm-templates/test-template-clone/clean-volatile.img.tar"),
  218. "-C", self.fullpath("vm-templates/test-template-clone"),
  219. "volatile.img"])
  220. self.create_whitelisted_appmenus(self.fullpath(
  221. "vm-templates/test-template-clone/whitelisted-appmenus.list"))
  222. self.create_whitelisted_appmenus(self.fullpath(
  223. "vm-templates/test-template-clone/vm-whitelisted-appmenus.list"))
  224. if r2b2:
  225. self.create_whitelisted_appmenus(self.fullpath(
  226. "vm-templates/test-template-clone/netvm-whitelisted-appmenus"
  227. ".list"))
  228. os.symlink("/usr/share/qubes/icons/green.png",
  229. self.fullpath("vm-templates/test-template-clone/icon.png"))
  230. os.mkdir(
  231. self.fullpath("vm-templates/test-template-clone/apps.templates"))
  232. self.create_appmenus(
  233. self.fullpath("vm-templates/test-template-clone/apps.templates"),
  234. APPTEMPLATE_R2B2,
  235. appmenus_list)
  236. os.mkdir(self.fullpath("vm-templates/test-template-clone/apps"))
  237. self.create_appmenus(
  238. self.fullpath("vm-templates/test-template-clone/apps"),
  239. APPTEMPLATE_R2B2.replace("%VMNAME%", "test-template-clone")
  240. .replace("%VMDIR%", self.fullpath(
  241. "vm-templates/test-template-clone")),
  242. appmenus_list)
  243. def calculate_hmac(self, f_name, algorithm="sha512", password="qubes"):
  244. subprocess.check_call(["openssl", "dgst", "-"+algorithm, "-hmac",
  245. password],
  246. stdin=open(self.fullpath(f_name), "r"),
  247. stdout=open(self.fullpath(f_name+".hmac"), "w"))
  248. def append_backup_stream(self, f_name, stream, basedir=None):
  249. if not basedir:
  250. basedir = self.backupdir
  251. subprocess.check_call(["tar", "-cO", "--posix", "-C", basedir,
  252. f_name],
  253. stdout=stream)
  254. def handle_v3_file(self, f_name, subdir, stream, compressed=True,
  255. encrypted=True):
  256. # create inner archive
  257. tar_cmdline = ["tar", "-Pc", '--sparse',
  258. '-C', self.fullpath(os.path.dirname(f_name)),
  259. '--xform', 's:^%s:%s\\0:' % (
  260. os.path.basename(f_name),
  261. subdir),
  262. os.path.basename(f_name)
  263. ]
  264. if compressed:
  265. tar_cmdline.insert(-1, "--use-compress-program=%s" % "gzip")
  266. tar = subprocess.Popen(tar_cmdline, stdout=subprocess.PIPE)
  267. if encrypted:
  268. encryptor = subprocess.Popen(
  269. ["openssl", "enc", "-e", "-aes-256-cbc", "-pass", "pass:qubes"],
  270. stdin=tar.stdout,
  271. stdout=subprocess.PIPE)
  272. data = encryptor.stdout
  273. else:
  274. data = tar.stdout
  275. stage1_dir = self.fullpath(os.path.join("stage1", subdir))
  276. if not os.path.exists(stage1_dir):
  277. os.makedirs(stage1_dir)
  278. subprocess.check_call(["split", "--numeric-suffixes",
  279. "--suffix-length=3",
  280. "--bytes="+str(100*1024*1024), "-",
  281. os.path.join(stage1_dir,
  282. os.path.basename(f_name+"."))],
  283. stdin=data)
  284. for part in sorted(os.listdir(stage1_dir)):
  285. if not re.match(
  286. r"^{}.[0-9][0-9][0-9]$".format(os.path.basename(f_name)),
  287. part):
  288. continue
  289. part_with_dir = os.path.join(subdir, part)
  290. self.calculate_hmac(os.path.join("stage1", part_with_dir))
  291. self.append_backup_stream(part_with_dir, stream,
  292. basedir=self.fullpath("stage1"))
  293. self.append_backup_stream(part_with_dir+".hmac", stream,
  294. basedir=self.fullpath("stage1"))
  295. def create_v3_backup(self, encrypted=True, compressed=True):
  296. """
  297. Create "backup format 3" backup - used in R2 and R3.0
  298. :param encrypt: Should the backup be encrypted
  299. :return:
  300. """
  301. output = open(self.fullpath("backup.bin"), "w")
  302. f = open(self.fullpath("backup-header"), "w")
  303. f.write(BACKUP_HEADER_R2.format(
  304. encrypted=str(encrypted),
  305. compressed=str(compressed)
  306. ))
  307. f.close()
  308. self.calculate_hmac("backup-header")
  309. self.append_backup_stream("backup-header", output)
  310. self.append_backup_stream("backup-header.hmac", output)
  311. f = open(self.fullpath("qubes.xml"), "w")
  312. if encrypted:
  313. qubesxml = QUBESXML_R2
  314. for vmname, subdir in MANGLED_SUBDIRS_R2.items():
  315. qubesxml = re.sub(r"[a-z-]*/{}".format(vmname),
  316. subdir, qubesxml)
  317. f.write(qubesxml)
  318. else:
  319. f.write(QUBESXML_R2)
  320. f.close()
  321. self.handle_v3_file("qubes.xml", "", output, encrypted=encrypted,
  322. compressed=compressed)
  323. self.create_v1_files(r2b2=True)
  324. for vm_type in ["appvms", "servicevms"]:
  325. for vm_name in os.listdir(self.fullpath(vm_type)):
  326. vm_dir = os.path.join(vm_type, vm_name)
  327. for f_name in os.listdir(self.fullpath(vm_dir)):
  328. if encrypted:
  329. subdir = MANGLED_SUBDIRS_R2[vm_name]
  330. else:
  331. subdir = vm_dir
  332. self.handle_v3_file(
  333. os.path.join(vm_dir, f_name),
  334. subdir+'/', output, encrypted=encrypted)
  335. for vm_name in os.listdir(self.fullpath("vm-templates")):
  336. vm_dir = os.path.join("vm-templates", vm_name)
  337. if encrypted:
  338. subdir = MANGLED_SUBDIRS_R2[vm_name]
  339. else:
  340. subdir = vm_dir
  341. self.handle_v3_file(
  342. os.path.join(vm_dir, "."),
  343. subdir+'/', output, encrypted=encrypted)
  344. output.close()
  345. def assertRestored(self, name, **kwargs):
  346. with self.assertNotRaises((KeyError, qubes.exc.QubesException)):
  347. vm = self.app.domains[name]
  348. vm.storage.verify()
  349. for prop, value in kwargs.items():
  350. if prop == 'klass':
  351. self.assertIsInstance(vm, value)
  352. elif prop == 'features':
  353. self.assertEqual(dict(vm.features), value,
  354. 'VM {} - features mismatch'.format(vm.name))
  355. elif value is qubes.property.DEFAULT:
  356. self.assertTrue(vm.property_is_default(prop),
  357. 'VM {} - property {} not default'.format(vm.name, prop))
  358. else:
  359. actual_value = getattr(vm, prop)
  360. if isinstance(actual_value, qubes.vm.BaseVM):
  361. self.assertEqual(value, actual_value.name,
  362. 'VM {} - property {}'.format(vm.name, prop))
  363. elif isinstance(actual_value, qubes.Label):
  364. self.assertEqual(value, actual_value.name,
  365. 'VM {} - property {}'.format(vm.name, prop))
  366. else:
  367. self.assertEqual(value, actual_value,
  368. 'VM {} - property {}'.format(vm.name, prop))
  369. def test_210_r2(self):
  370. self.create_v3_backup(False)
  371. self.restore_backup(self.fullpath("backup.bin"), options={
  372. 'use-default-template': True,
  373. 'use-default-netvm': True,
  374. })
  375. common_props = {
  376. 'installed_by_rpm': False,
  377. 'kernel': qubes.property.DEFAULT,
  378. 'kernelopts': qubes.property.DEFAULT,
  379. 'qrexec_timeout': qubes.property.DEFAULT,
  380. 'netvm': qubes.property.DEFAULT,
  381. 'default_user': qubes.property.DEFAULT,
  382. 'include_in_backups': True,
  383. 'debug': False,
  384. 'maxmem': 1535,
  385. 'memory': 400,
  386. 'features': {
  387. 'service.meminfo-writer': '1',
  388. },
  389. }
  390. template_standalone_props = common_props.copy()
  391. template_standalone_props['features'] = {
  392. 'qrexec': '1',
  393. 'gui': '1',
  394. 'service.meminfo-writer': '1',
  395. }
  396. self.assertRestored("test-template-clone",
  397. klass=qubes.vm.templatevm.TemplateVM,
  398. label='green',
  399. provides_network=False,
  400. **template_standalone_props)
  401. testproxy_props = common_props.copy()
  402. testproxy_props.update(
  403. label='red',
  404. provides_network=True,
  405. memory=200,
  406. template=self.app.default_template.name,
  407. )
  408. self.assertRestored("test-testproxy",
  409. klass=qubes.vm.appvm.AppVM,
  410. **testproxy_props)
  411. self.assertRestored("test-work",
  412. klass=qubes.vm.appvm.AppVM,
  413. template=self.app.default_template.name,
  414. label='green',
  415. **common_props)
  416. self.assertRestored("test-standalonevm",
  417. klass=qubes.vm.standalonevm.StandaloneVM,
  418. label='blue',
  419. **template_standalone_props)
  420. self.assertRestored("test-custom-template-appvm",
  421. klass=qubes.vm.appvm.AppVM,
  422. template='test-template-clone',
  423. label='yellow',
  424. **common_props)
  425. def test_220_r2_encrypted(self):
  426. self.create_v3_backup(True)
  427. self.restore_backup(self.fullpath("backup.bin"), options={
  428. 'use-default-template': True,
  429. 'use-default-netvm': True,
  430. })
  431. common_props = {
  432. 'installed_by_rpm': False,
  433. 'kernel': qubes.property.DEFAULT,
  434. 'kernelopts': qubes.property.DEFAULT,
  435. 'qrexec_timeout': qubes.property.DEFAULT,
  436. 'netvm': qubes.property.DEFAULT,
  437. 'default_user': qubes.property.DEFAULT,
  438. 'include_in_backups': True,
  439. 'debug': False,
  440. 'maxmem': 1535, # 4063 caped by 10*400
  441. 'memory': 400,
  442. 'features': {
  443. 'service.meminfo-writer': '1',
  444. },
  445. }
  446. template_standalone_props = common_props.copy()
  447. template_standalone_props['features'] = {
  448. 'qrexec': '1',
  449. 'gui': '1',
  450. 'service.meminfo-writer': '1',
  451. }
  452. self.assertRestored("test-template-clone",
  453. klass=qubes.vm.templatevm.TemplateVM,
  454. label='green',
  455. provides_network=False,
  456. **template_standalone_props)
  457. testproxy_props = common_props.copy()
  458. testproxy_props.update(
  459. label='red',
  460. provides_network=True,
  461. memory=200,
  462. template=self.app.default_template.name,
  463. )
  464. self.assertRestored("test-testproxy",
  465. klass=qubes.vm.appvm.AppVM,
  466. **testproxy_props)
  467. self.assertRestored("test-work",
  468. klass=qubes.vm.appvm.AppVM,
  469. template=self.app.default_template.name,
  470. label='green',
  471. **common_props)
  472. self.assertRestored("test-standalonevm",
  473. klass=qubes.vm.standalonevm.StandaloneVM,
  474. label='blue',
  475. **template_standalone_props)
  476. self.assertRestored("test-custom-template-appvm",
  477. klass=qubes.vm.appvm.AppVM,
  478. template='test-template-clone',
  479. label='yellow',
  480. **common_props)
  481. class TC_01_BackupCompatibilityIntoLVM(TC_00_BackupCompatibility):
  482. def setUp(self):
  483. super(TC_01_BackupCompatibilityIntoLVM, self).setUp()
  484. self.init_lvm_pool()
  485. def restore_backup(self, source=None, appvm=None, options=None,
  486. **kwargs):
  487. if options is None:
  488. options = {}
  489. options['override_pool'] = self.pool.name
  490. super(TC_01_BackupCompatibilityIntoLVM, self).restore_backup(source,
  491. appvm, options, **kwargs)