usb.rb 23.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
# Returns a hash that for each preset the running Tails is aware of
# maps the source to the destination.
def get_persistence_presets(skip_links = false)
  # Perl script that prints all persistence presets (one per line) on
  # the form: <mount_point>:<comma-separated-list-of-options>
  script = <<-EOF
  use strict;
  use warnings FATAL => "all";
  use Tails::Persistence::Configuration::Presets;
Tails developers's avatar
Tails developers committed
10
11
  foreach my $preset (Tails::Persistence::Configuration::Presets->new()->all) {
    say $preset->destination, ":", join(",", @{$preset->options});
12
  }
13
14
15
16
EOF
  # VMCommand:s cannot handle newlines, and they're irrelevant in the
  # above perl script any way
  script.delete!("\n")
17
  presets = $vm.execute_successfully("perl -E '#{script}'").stdout.chomp.split("\n")
18
19
20
21
22
23
24
25
26
  assert presets.size >= 10, "Got #{presets.size} persistence presets, " +
                             "which is too few"
  persistence_mapping = Hash.new
  for line in presets
    destination, options_str = line.split(":")
    options = options_str.split(",")
    is_link = options.include? "link"
    next if is_link and skip_links
    source_str = options.find { |option| /^source=/.match option }
27
28
29
30
31
32
33
    # If no source is given as an option, live-boot's persistence
    # feature defaults to the destination minus the initial "/".
    if source_str.nil?
      source = destination.partition("/").last
    else
      source = source_str.split("=")[1]
    end
34
35
36
37
38
39
40
41
42
43
44
    persistence_mapping[source] = destination
  end
  return persistence_mapping
end

def persistent_dirs
  get_persistence_presets
end

def persistent_mounts
  get_persistence_presets(true)
45
46
end

47
def persistent_volumes_mountpoints
48
  $vm.execute("ls -1 -d /live/persistence/*_unlocked/").stdout.chomp.split
49
50
end

51
Given /^I clone USB drive "([^"]+)" to a new USB drive "([^"]+)"$/ do |from, to|
52
  $vm.storage.clone_to_new_disk(from, to)
53
54
55
end

Given /^I unplug USB drive "([^"]+)"$/ do |name|
56
  $vm.unplug_drive(name)
57
58
end

59
Given /^the computer is set to boot from the old Tails DVD$/ do
60
  $vm.set_cdrom_boot(OLD_TAILS_ISO)
61
62
end

63
Given /^the computer is set to boot in UEFI mode$/ do
64
  $vm.set_os_loader('UEFI')
65
66
67
  @os_loader = 'UEFI'
end

anonym's avatar
anonym committed
68
69
70
71
72
73
74
def tails_installer_selected_device
  @installer.child('Target Device:', roleName: 'label').parent
    .child('', roleName: 'combo box', recursive: false).name
end

def tails_installer_is_device_selected?(name)
  device = $vm.disk_dev(name)
75
  tails_installer_selected_device[/#{device}\d*$/]
anonym's avatar
anonym committed
76
77
end

anonym's avatar
anonym committed
78
def tails_installer_match_status(pattern)
anonym's avatar
anonym committed
79
  @installer.child('', roleName: 'text').text[pattern]
anonym's avatar
anonym committed
80
81
end

82
class UpgradeNotSupported < StandardError
83
84
end

85
def usb_install_helper(name)
anonym's avatar
anonym committed
86
  if tails_installer_match_status('It is impossible to upgrade the device')
87
    raise UpgradeNotSupported
88
  end
89
  assert(tails_installer_is_device_selected?(name))
90
  begin
91
92
93
94
    @installer.button('Install Tails').click
    @installer.child('Question', roleName: 'alert').button('Yes').click
    @installer.child('Information', roleName: 'alert')
      .child('Installation complete!', roleName: 'label').wait(30*60)
95
96
97
98
  rescue FindFailed => e
    debug_log("Tails Installer debug log:\n" + $vm.file_content('/tmp/tails-installer-*'))
    raise e
  end
99
100
end

101
When /^I start Tails Installer in "([^"]+)" mode$/ do |mode|
102
103
104
  step 'I run "export DEBUG=1 ; tails-installer-launcher" in GNOME Terminal'
  installer_launcher = Dogtail::Application.new('tails-installer-launcher')
  installer_launcher.wait(10)
105
  installer_launcher.button(mode).click
106
  @installer = Dogtail::Application.new('tails-installer')
107
  @installer.child('Tails Installer', roleName: 'frame').wait
108
109
end

110
Then /^Tails Installer detects that a device is too small$/ do
111
  try_for(10) do
112
    tails_installer_match_status(/^The device .* is too small to install Tails/)
113
  end
114
115
end

116
117
118
119
120
121
122
123
124
125
126
127
128
When /^I am told that the destination device cannot be upgraded$/ do
  try_for(10) do
    tails_installer_match_status(/^It is impossible to upgrade the device/)
  end
end

When /^I am suggested to do a "Upgrade by cloning"$/ do
  try_for(10) do
    tails_installer_match_status(
      /You should instead use "Install by cloning" to upgrade Tails/
    )
  end
end
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

Then /^a suitable USB device is (?:still )?not found$/ do
  @installer.child('No device suitable to install Tails could be found',
                   roleName: 'label').wait(30)
end

Then /^(no|the "([^"]+)") USB drive is selected$/ do |mode, name|
  try_for(30) do
    if mode == 'no'
      tails_installer_selected_device == ''
    else
      tails_installer_is_device_selected?(name)
    end
  end
end

145
146
When /^I "([^"]*)" Tails to USB drive "([^"]+)"$/ do |mode, name|
  step "I start Tails Installer in \"#{mode}\" mode"
147
148
149
  usb_install_helper(name)
end

anonym's avatar
anonym committed
150
When /^I fail to "([^"]*)" Tails to USB drive "([^"]+)"$/ do |mode, name|
151
  begin
152
    step "I \"#{mode}\" Tails to USB drive \"#{name}\""
153
  rescue UpgradeNotSupported
154
155
156
157
158
159
    # this is what we expect
  else
    raise "The USB installer should not succeed"
  end
end

160
Given /^I setup a filesystem share containing the Tails ISO$/ do
anonym's avatar
anonym committed
161
162
  shared_iso_dir_on_host = "#{$config["TMPDIR"]}/shared_iso_dir"
  @shared_iso_dir_on_guest = "/tmp/shared_iso_dir"
163
164
165
  FileUtils.mkdir_p(shared_iso_dir_on_host)
  FileUtils.cp(TAILS_ISO, shared_iso_dir_on_host)
  add_after_scenario_hook { FileUtils.rm_r(shared_iso_dir_on_host) }
166
  $vm.add_share(shared_iso_dir_on_host, @shared_iso_dir_on_guest)
167
168
end

169
When /^I do a "Upgrade from ISO" on USB drive "([^"]+)"$/ do |name|
170
  iso_path_on_guest = "#{@shared_iso_dir_on_guest}/#{File.basename(TAILS_ISO)}"
171
  step 'I start Tails Installer in "Upgrade from ISO" mode'
172
173
174
175
  @installer.child('Use existing Live system ISO:', roleName: 'label')
    .parent.button('(None)').click
  file_chooser = @installer.child('Select a File', roleName: 'file chooser')
  file_chooser.wait(10)
176
  @screen.type("l", Sikuli::KeyModifier.CTRL)
177
178
179
  # The only visible text element will be the path entry
  file_chooser.child(roleName: 'text').text = iso_path_on_guest
  file_chooser.button('Open').click
180
181
182
183
184
  usb_install_helper(name)
end

Given /^I enable all persistence presets$/ do
  @screen.wait('PersistenceWizardPresets.png', 20)
185
186
187
188
189
190
  # Select the "Persistent" folder preset, which is checked by default.
  @screen.type(Sikuli::Key.TAB)
  # Check all non-default persistence presets, i.e. all *after* the
  # "Persistent" folder, which are unchecked by default.
  (persistent_dirs.size - 1).times do
    @screen.type(Sikuli::Key.TAB + Sikuli::Key.SPACE)
191
  end
192
  @screen.wait_and_click('PersistenceWizardSave.png', 10)
intrigeri's avatar
intrigeri committed
193
  @screen.wait('PersistenceWizardDone.png', 60)
194
  @screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
195
196
end

197
When /^I disable the first persistence preset$/ do
198
  step 'I start "Configure persistent volume" via the GNOME "Tails" applications menu'
199
200
201
202
203
204
205
  @screen.wait('PersistenceWizardPresets.png', 300)
  @screen.type(Sikuli::Key.SPACE)
  @screen.wait_and_click('PersistenceWizardSave.png', 10)
  @screen.wait('PersistenceWizardDone.png', 30)
  @screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
end

206
Given /^I create a persistent partition$/ do
207
  step 'I start "Configure persistent volume" via the GNOME "Tails" applications menu'
intrigeri's avatar
intrigeri committed
208
  @screen.wait('PersistenceWizardStart.png', 60)
209
  @screen.type(@persistence_password + "\t" + @persistence_password + Sikuli::Key.ENTER)
Tails developers's avatar
Tails developers committed
210
  @screen.wait('PersistenceWizardPresets.png', 300)
211
212
213
  step "I enable all persistence presets"
end

214
def check_disk_integrity(name, dev, scheme)
215
  info = $vm.execute("udisksctl info --block-device '#{dev}'").stdout
216
217
218
219
220
221
222
  info_split = info.split("\n  org\.freedesktop\.UDisks2\.PartitionTable:\n")
  dev_info = info_split[0]
  part_table_info = info_split[1]
  assert(part_table_info.match("^    Type: +#{scheme}$"),
         "Unexpected partition scheme on USB drive '#{name}', '#{dev}'")
end

223
def check_part_integrity(name, dev, usage, fs_type, part_label, part_type = nil)
224
  info = $vm.execute("udisksctl info --block-device '#{dev}'").stdout
225
  info_split = info.split("\n  org\.freedesktop\.UDisks2\.Partition:\n")
226
227
  dev_info = info_split[0]
  part_info = info_split[1]
228
  assert(dev_info.match("^    IdUsage: +#{usage}$"),
229
         "Unexpected device field 'usage' on USB drive '#{name}', '#{dev}'")
230
231
232
  assert(dev_info.match("^    IdType: +#{fs_type}$"),
         "Unexpected device field 'IdType' on USB drive '#{name}', '#{dev}'")
  assert(part_info.match("^    Name: +#{part_label}$"),
233
         "Unexpected partition label on USB drive '#{name}', '#{dev}'")
234
235
236
237
  if part_type
    assert(part_info.match("^    Type: +#{part_type}$"),
           "Unexpected partition type on USB drive '#{name}', '#{dev}'")
  end
238
239
end

240
def tails_is_installed_helper(name, tails_root, loader)
241
  disk_dev = $vm.disk_dev(name)
242
243
  part_dev = disk_dev + "1"
  check_disk_integrity(name, disk_dev, "gpt")
244
245
246
  check_part_integrity(name, part_dev, "filesystem", "vfat", "Tails",
                       # EFI System Partition
                       'c12a7328-f81f-11d2-ba4b-00a0c93ec93b')
247

248
  target_root = "/mnt/new"
249
  $vm.execute("mkdir -p #{target_root}")
250
  $vm.execute("mount #{part_dev} #{target_root}")
251

252
  c = $vm.execute("diff -qr '#{tails_root}/live' '#{target_root}/live'")
253
  assert(c.success?,
254
         "USB drive '#{name}' has differences in /live:\n#{c.stdout}\n#{c.stderr}")
255

256
  syslinux_files = $vm.execute("ls -1 #{target_root}/syslinux").stdout.chomp.split
257
  # We deal with these files separately
258
  ignores = ["syslinux.cfg", "exithelp.cfg", "ldlinux.c32", "ldlinux.sys"]
259
  for f in syslinux_files - ignores do
260
    c = $vm.execute("diff -q '#{tails_root}/#{loader}/#{f}' " +
261
                    "'#{target_root}/syslinux/#{f}'")
262
263
264
265
266
    assert(c.success?, "USB drive '#{name}' has differences in " +
           "'/syslinux/#{f}'")
  end

  # The main .cfg is named differently vs isolinux
267
  c = $vm.execute("diff -q '#{tails_root}/#{loader}/#{loader}.cfg' " +
268
                  "'#{target_root}/syslinux/syslinux.cfg'")
269
270
271
  assert(c.success?, "USB drive '#{name}' has differences in " +
         "'/syslinux/syslinux.cfg'")

272
273
  $vm.execute("umount #{target_root}")
  $vm.execute("sync")
274
275
end

276
277
278
279
280
281
Then /^the running Tails is installed on USB drive "([^"]+)"$/ do |target_name|
  loader = boot_device_type == "usb" ? "syslinux" : "isolinux"
  tails_is_installed_helper(target_name, "/lib/live/mount/medium", loader)
end

Then /^the ISO's Tails is installed on USB drive "([^"]+)"$/ do |target_name|
282
  iso = "#{@shared_iso_dir_on_guest}/#{File.basename(TAILS_ISO)}"
283
  iso_root = "/mnt/iso"
284
285
  $vm.execute("mkdir -p #{iso_root}")
  $vm.execute("mount -o loop #{iso} #{iso_root}")
Tails developers's avatar
Tails developers committed
286
  tails_is_installed_helper(target_name, iso_root, "isolinux")
287
  $vm.execute("umount #{iso_root}")
288
289
end

290
Then /^there is no persistence partition on USB drive "([^"]+)"$/ do |name|
291
292
  data_part_dev = $vm.disk_dev(name) + "2"
  assert(!$vm.execute("test -b #{data_part_dev}").success?,
293
294
295
         "USB drive #{name} has a partition '#{data_part_dev}'")
end

296
Then /^a Tails persistence partition exists on USB drive "([^"]+)"$/ do |name|
297
  dev = $vm.disk_dev(name) + "2"
298
  check_part_integrity(name, dev, "crypto", "crypto_LUKS", "TailsData")
299

300
301
  # The LUKS container may already be opened, e.g. by udisks after
  # we've run tails-persistence-setup.
302
  c = $vm.execute("ls -1 --hide 'control' /dev/mapper/")
303
304
  if c.success?
    for candidate in c.stdout.split("\n")
305
      luks_info = $vm.execute("cryptsetup status '#{candidate}'")
306
307
308
309
310
311
312
      if luks_info.success? and luks_info.stdout.match("^\s+device:\s+#{dev}$")
        luks_dev = "/dev/mapper/#{candidate}"
        break
      end
    end
  end
  if luks_dev.nil?
313
314
    c = $vm.execute("echo #{@persistence_password} | " +
                    "cryptsetup luksOpen #{dev} #{name}")
315
316
317
    assert(c.success?, "Couldn't open LUKS device '#{dev}' on  drive '#{name}'")
    luks_dev = "/dev/mapper/#{name}"
  end
318
319

  # Adapting check_part_integrity() seems like a bad idea so here goes
320
  info = $vm.execute("udisksctl info --block-device '#{luks_dev}'").stdout
321
322
323
324
  assert info.match("^    CryptoBackingDevice: +'/[a-zA-Z0-9_/]+'$")
  assert info.match("^    IdUsage: +filesystem$")
  assert info.match("^    IdType: +ext[34]$")
  assert info.match("^    IdLabel: +TailsData$")
325
326

  mount_dir = "/mnt/#{name}"
327
  $vm.execute("mkdir -p #{mount_dir}")
328
  c = $vm.execute("mount '#{luks_dev}' #{mount_dir}")
329
  assert(c.success?,
Tails developers's avatar
Tails developers committed
330
         "Couldn't mount opened LUKS device '#{dev}' on drive '#{name}'")
331

332
333
334
  $vm.execute("umount #{mount_dir}")
  $vm.execute("sync")
  $vm.execute("cryptsetup luksClose #{name}")
335
336
end

337
Given /^I enable persistence$/ do
338
339
  @screen.wait('TailsGreeterPersistence.png', 10)
  @screen.type(Sikuli::Key.SPACE)
340
341
  @screen.wait('TailsGreeterPersistencePassphrase.png', 10)
  match = @screen.find('TailsGreeterPersistencePassphrase.png')
342
  @screen.click(match.getCenter.offset(match.w*2, match.h/2))
343
  @screen.type(@persistence_password)
344
345
end

346
347
def tails_persistence_enabled?
  persistence_state_file = "/var/lib/live/config/tails.persistence"
348
349
  return $vm.execute("test -e '#{persistence_state_file}'").success? &&
         $vm.execute(". '#{persistence_state_file}' && " +
350
                     'test "$TAILS_PERSISTENCE_ENABLED" = true').success?
351
352
end

353
354
Given /^all persistence presets(| from the old Tails version)(| but the first one) are enabled$/ do |old_tails, except_first|
  assert(old_tails.empty? || except_first.empty?, "Unsupported case.")
355
356
357
  try_for(120, :msg => "Persistence is disabled") do
    tails_persistence_enabled?
  end
358
  unexpected_mounts = Array.new
359
  # Check that all persistent directories are mounted
360
361
  if old_tails.empty?
    expected_mounts = persistent_mounts
362
363
364
365
366
367
    if ! except_first.empty?
      first_expected_mount_source      = expected_mounts.keys[0]
      first_expected_mount_destination = expected_mounts[first_expected_mount_source]
      expected_mounts.delete(first_expected_mount_source)
      unexpected_mounts = [first_expected_mount_destination]
    end
368
  else
369
    assert_not_nil($remembered_persistence_mounts)
370
    expected_mounts = $remembered_persistence_mounts
371
  end
372
  mount = $vm.execute("mount").stdout.chomp
373
  for _, dir in expected_mounts do
374
375
376
    assert(mount.include?("on #{dir} "),
           "Persistent directory '#{dir}' is not mounted")
  end
377
378
379
380
  for dir in unexpected_mounts do
    assert(! mount.include?("on #{dir} "),
           "Persistent directory '#{dir}' is mounted")
  end
381
382
end

383
384
385
386
Given /^persistence is disabled$/ do
  assert(!tails_persistence_enabled?, "Persistence is enabled")
end

387
388
Given /^I enable read-only persistence$/ do
  step "I enable persistence"
389
  @screen.wait_and_click('TailsGreeterPersistenceReadOnly.png', 10)
390
391
end

392
def boot_device
393
394
  # Approach borrowed from
  # config/chroot_local_includes/lib/live/config/998-permissions
395
396
  boot_dev_id = $vm.execute("udevadm info --device-id-of-file=/lib/live/mount/medium").stdout.chomp
  boot_dev = $vm.execute("readlink -f /dev/block/'#{boot_dev_id}'").stdout.chomp
397
398
399
  return boot_dev
end

anonym's avatar
anonym committed
400
def device_info(dev)
401
402
  # Approach borrowed from
  # config/chroot_local_includes/lib/live/config/998-permissions
anonym's avatar
anonym committed
403
404
405
406
407
408
  info = $vm.execute("udevadm info --query=property --name='#{dev}'").stdout.chomp
  info.split("\n").map { |e| e.split('=') } .to_h
end

def boot_device_type
  device_info(boot_device)['ID_BUS']
409
410
end

411
412
413
Then /^Tails is running from (.*) drive "([^"]+)"$/ do |bus, name|
  bus = bus.downcase
  case bus
414
  when "sata"
415
416
417
418
419
    expected_bus = "ata"
  else
    expected_bus = bus
  end
  assert_equal(expected_bus, boot_device_type)
420
  actual_dev = boot_device
421
422
423
424
425
426
  # The boot partition differs between an using Tails installer and
  # isohybrids. There's also a strange case isohybrids are thought to
  # be booting from the "raw" device, and not a partition of it
  # (#10504).
  expected_devs = ['', '1', '4'].map { |e| $vm.disk_dev(name) + e }
  assert(expected_devs.include?(actual_dev),
427
         "We are running from device #{actual_dev}, but for #{bus} drive " +
428
         "'#{name}' we expected to run from one of #{expected_devs}")
429
430
431
432
433
end

Then /^the boot device has safe access rights$/ do

  super_boot_dev = boot_device.sub(/[[:digit:]]+$/, "")
434
  devs = $vm.execute("ls -1 #{super_boot_dev}*").stdout.chomp.split
435
  assert(devs.size > 0, "Could not determine boot device")
436
  all_users = $vm.execute("cut -d':' -f1 /etc/passwd").stdout.chomp.split
437
  all_users_with_groups = all_users.collect do |user|
438
    groups = $vm.execute("groups #{user}").stdout.chomp.sub(/^#{user} : /, "").split(" ")
439
440
441
    [user, groups]
  end
  for dev in devs do
442
443
444
    dev_owner = $vm.execute("stat -c %U #{dev}").stdout.chomp
    dev_group = $vm.execute("stat -c %G #{dev}").stdout.chomp
    dev_perms = $vm.execute("stat -c %a #{dev}").stdout.chomp
445
    assert_equal("root", dev_owner)
446
447
    assert(dev_group == "disk" || dev_group == "root",
           "Boot device '#{dev}' owned by group '#{dev_group}', expected " +
448
           "'disk' or 'root'.")
449
    assert_equal("660", dev_perms)
450
451
452
453
454
455
456
    for user, groups in all_users_with_groups do
      next if user == "root"
      assert(!(groups.include?(dev_group)),
             "Unprivileged user '#{user}' is in group '#{dev_group}' which " +
             "owns boot device '#{dev}'")
    end
  end
457

458
  info = $vm.execute("udisksctl info --block-device '#{super_boot_dev}'").stdout
intrigeri's avatar
intrigeri committed
459
  assert(info.match("^    HintSystem: +true$"),
460
         "Boot device '#{super_boot_dev}' is not system internal for udisks")
461
462
end

463
Then /^all persistent filesystems have safe access rights$/ do
464
  persistent_volumes_mountpoints.each do |mountpoint|
465
466
467
    fs_owner = $vm.execute("stat -c %U #{mountpoint}").stdout.chomp
    fs_group = $vm.execute("stat -c %G #{mountpoint}").stdout.chomp
    fs_perms = $vm.execute("stat -c %a #{mountpoint}").stdout.chomp
468
469
470
    assert_equal("root", fs_owner)
    assert_equal("root", fs_group)
    assert_equal('775', fs_perms)
471
472
473
  end
end

474
Then /^all persistence configuration files have safe access rights$/ do
475
  persistent_volumes_mountpoints.each do |mountpoint|
476
    assert($vm.execute("test -e #{mountpoint}/persistence.conf").success?,
477
           "#{mountpoint}/persistence.conf does not exist, while it should")
478
    assert($vm.execute("test ! -e #{mountpoint}/live-persistence.conf").success?,
479
           "#{mountpoint}/live-persistence.conf does exist, while it should not")
480
    $vm.execute(
481
482
      "ls -1 #{mountpoint}/persistence.conf #{mountpoint}/live-*.conf"
    ).stdout.chomp.split.each do |f|
483
484
485
      file_owner = $vm.execute("stat -c %U '#{f}'").stdout.chomp
      file_group = $vm.execute("stat -c %G '#{f}'").stdout.chomp
      file_perms = $vm.execute("stat -c %a '#{f}'").stdout.chomp
486
487
488
      assert_equal("tails-persistence-setup", file_owner)
      assert_equal("tails-persistence-setup", file_group)
      assert_equal("600", file_perms)
489
    end
Tails developers's avatar
Tails developers committed
490
  end
491
492
end

493
Then /^all persistent directories(| from the old Tails version) have safe access rights$/ do |old_tails|
494
495
496
  if old_tails.empty?
    expected_dirs = persistent_dirs
  else
497
    assert_not_nil($remembered_persistence_dirs)
498
    expected_dirs = $remembered_persistence_dirs
499
  end
500
  persistent_volumes_mountpoints.each do |mountpoint|
501
    expected_dirs.each do |src, dest|
Tails developers's avatar
Tails developers committed
502
      full_src = "#{mountpoint}/#{src}"
503
504
505
      assert_vmcommand_success $vm.execute("test -d #{full_src}")
      dir_perms = $vm.execute_successfully("stat -c %a '#{full_src}'").stdout.chomp
      dir_owner = $vm.execute_successfully("stat -c %U '#{full_src}'").stdout.chomp
506
      if dest.start_with?("/home/#{LIVE_USER}")
507
        expected_perms = "700"
508
        expected_owner = LIVE_USER
509
510
511
512
513
514
515
516
517
518
      else
        expected_perms = "755"
        expected_owner = "root"
      end
      assert_equal(expected_perms, dir_perms,
                   "Persistent source #{full_src} has permission " \
                   "#{dir_perms}, expected #{expected_perms}")
      assert_equal(expected_owner, dir_owner,
                   "Persistent source #{full_src} has owner " \
                   "#{dir_owner}, expected #{expected_owner}")
519
520
521
522
    end
  end
end

523
When /^I write some files expected to persist$/ do
524
  persistent_mounts.each do |_, dir|
525
    owner = $vm.execute("stat -c %U #{dir}").stdout.chomp
526
    assert($vm.execute("touch #{dir}/XXX_persist", :user => owner).success?,
527
           "Could not create file in persistent directory #{dir}")
528
529
530
  end
end

531
532
533
534
535
536
When /^I write some dotfile expected to persist$/ do
  assert($vm.execute("touch /live/persistence/TailsData_unlocked/dotfiles/.XXX_persist",
                     :user => LIVE_USER).success?,
         "Could not create a file in the dotfiles persistence.")
end

537
When /^I remove some files expected to persist$/ do
538
  persistent_mounts.each do |_, dir|
539
    owner = $vm.execute("stat -c %U #{dir}").stdout.chomp
540
    assert($vm.execute("rm #{dir}/XXX_persist", :user => owner).success?,
541
           "Could not remove file in persistent directory #{dir}")
542
543
544
545
  end
end

When /^I write some files not expected to persist$/ do
546
  persistent_mounts.each do |_, dir|
547
    owner = $vm.execute("stat -c %U #{dir}").stdout.chomp
548
    assert($vm.execute("touch #{dir}/XXX_gone", :user => owner).success?,
549
           "Could not create file in persistent directory #{dir}")
550
551
552
  end
end

553
When /^I take note of which persistence presets are available$/ do
554
555
  $remembered_persistence_mounts = persistent_mounts
  $remembered_persistence_dirs = persistent_dirs
556
557
558
559
560
561
end

Then /^the expected persistent files(| created with the old Tails version) are present in the filesystem$/ do |old_tails|
  if old_tails.empty?
    expected_mounts = persistent_mounts
  else
562
    assert_not_nil($remembered_persistence_mounts)
563
    expected_mounts = $remembered_persistence_mounts
564
565
  end
  expected_mounts.each do |_, dir|
566
    assert($vm.execute("test -e #{dir}/XXX_persist").success?,
567
           "Could not find expected file in persistent directory #{dir}")
568
    assert(!$vm.execute("test -e #{dir}/XXX_gone").success?,
569
570
571
572
           "Found file that should not have persisted in persistent directory #{dir}")
  end
end

573
574
575
576
577
578
579
580
Then /^the expected persistent dotfile is present in the filesystem$/ do
  expected_dirs = persistent_dirs
  assert($vm.execute("test -L #{expected_dirs['dotfiles']}/.XXX_persist").success?,
         "Could not find expected persistent dotfile link.")
  assert($vm.execute("test -e $(readlink -f #{expected_dirs['dotfiles']}/.XXX_persist)").success?,
           "Could not find expected persistent dotfile link target.")
end

581
Then /^only the expected files are present on the persistence partition on USB drive "([^"]+)"$/ do |name|
582
  assert(!$vm.is_running?)
583
  disk = {
584
    :path => $vm.storage.disk_path(name),
585
    :opts => {
586
      :format => $vm.storage.disk_format(name),
587
588
589
      :readonly => true
    }
  }
590
  $vm.storage.guestfs_disk_helper(disk) do |g, disk_handle|
591
592
593
594
595
596
597
598
599
    partitions = g.part_list(disk_handle).map do |part_desc|
      disk_handle + part_desc["part_num"].to_s
    end
    partition = partitions.find do |part|
      g.blkid(part)["PART_ENTRY_NAME"] == "TailsData"
    end
    assert_not_nil(partition, "Could not find the 'TailsData' partition " \
                              "on disk '#{disk_handle}'")
    luks_mapping = File.basename(partition) + "_unlocked"
600
    g.luks_open(partition, @persistence_password, luks_mapping)
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
    luks_dev = "/dev/mapper/#{luks_mapping}"
    mount_point = "/"
    g.mount(luks_dev, mount_point)
    assert_not_nil($remembered_persistence_mounts)
    $remembered_persistence_mounts.each do |dir, _|
      # Guestfs::exists may have a bug; if the file exists, 1 is
      # returned, but if it doesn't exist false is returned. It seems
      # the translation of C types into Ruby types is glitchy.
      assert(g.exists("/#{dir}/XXX_persist") == 1,
             "Could not find expected file in persistent directory #{dir}")
      assert(g.exists("/#{dir}/XXX_gone") != 1,
             "Found file that should not have persisted in persistent directory #{dir}")
    end
    g.umount(mount_point)
    g.luks_close(luks_dev)
  end
617
end
618
619

When /^I delete the persistent partition$/ do
620
  step 'I start "Delete persistent volume" via the GNOME "Tails" applications menu'
intrigeri's avatar
intrigeri committed
621
  @screen.wait("PersistenceWizardDeletionStart.png", 120)
622
623
624
  @screen.type(" ")
  @screen.wait("PersistenceWizardDone.png", 120)
end
625
626

Then /^Tails has started in UEFI mode$/ do
627
  assert($vm.execute("test -d /sys/firmware/efi").success?,
628
629
         "/sys/firmware/efi does not exist")
 end
630
631

Given /^I create a ([[:alpha:]]+) label on disk "([^"]+)"$/ do |type, name|
632
  $vm.storage.disk_mklabel(name, type)
633
end