usb.rb 25.9 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
52
53
54
55
56
57
58
def recover_from_upgrader_failure
    $vm.execute('killall tails-upgrade-frontend tails-upgrade-frontend-wrapper zenity')
    # Remove unnecessary sleep for retry
    $vm.execute_successfully('sed -i "/^sleep 30$/d" ' +
                             '/usr/local/bin/tails-upgrade-frontend-wrapper')
    $vm.spawn('tails-upgrade-frontend-wrapper', user: LIVE_USER)
end

59
Given /^I clone USB drive "([^"]+)" to a new USB drive "([^"]+)"$/ do |from, to|
60
  $vm.storage.clone_to_new_disk(from, to)
61
62
63
end

Given /^I unplug USB drive "([^"]+)"$/ do |name|
64
  $vm.unplug_drive(name)
65
66
end

67
Given /^the computer is set to boot from the old Tails DVD$/ do
68
  $vm.set_cdrom_boot(OLD_TAILS_ISO)
69
70
end

71
Given /^the computer is set to boot in UEFI mode$/ do
72
  $vm.set_os_loader('UEFI')
73
74
75
  @os_loader = 'UEFI'
end

anonym's avatar
anonym committed
76
def tails_installer_selected_device
77
  @installer.child('Target USB stick:', roleName: 'label').parent
anonym's avatar
anonym committed
78
79
80
81
82
    .child('', roleName: 'combo box', recursive: false).name
end

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

anonym's avatar
anonym committed
86
def tails_installer_match_status(pattern)
anonym's avatar
anonym committed
87
  @installer.child('', roleName: 'text').text[pattern]
anonym's avatar
anonym committed
88
89
end

90
When /^I start Tails Installer$/ do
91
92
  step 'I run "export DEBUG=1 ; /usr/bin/tails-installer" in GNOME Terminal'
  @installer = Dogtail::Application.new('tails-installer')
93
  @installer.child('Tails Installer', roleName: 'frame')
94
95
96
97
98
  # Sometimes Dogtail will find the Installer and click its window
  # before it is shown (searchShowingOnly is not perfect) which
  # generally means clicking somewhere on the Terminal => the click is
  # lost *and* the installer does not go to the foreground. So let's
  # wait a bit extra.
99
100
  sleep 3
  $vm.focus_window('Tails Installer')
101
102
end

103
When /^I am told that the destination device (.*)$/ do |status|
104
  try_for(10) do
105
    tails_installer_match_status(status)
106
107
108
  end
end

109
Then /^a suitable USB device is (?:still )?not found$/ do
110
111
112
  @installer.child(
    'No device suitable to install Tails could be found', roleName: 'label'
  )
113
114
115
116
117
118
119
120
121
122
123
124
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

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
When /^I (install|upgrade) Tails (?:to|on) USB drive "([^"]+)" (by cloning|from an ISO)$/ do |action, name, source|
  step "I start Tails Installer"
  assert(tails_installer_is_device_selected?(name))
  if source == 'from an ISO'
    iso_radio = @installer.child('Use a downloaded Tails ISO image',
                                 roleName: 'radio button')
    iso_radio.click
    iso_radio.parent.button('(None)').click
    file_chooser = @installer.child('Select a File', roleName: 'file chooser')
    @screen.type("l", Sikuli::KeyModifier.CTRL)
    # The only visible text element will be the path entry
    file_chooser.child(roleName: 'text').typeText(@iso_path + '\n')
    file_chooser.button('Open').click
  end
  begin
    @installer.button(action.capitalize).click
    @installer.child('Question', roleName: 'alert').button('Yes').click
    try_for(30*60) do
      @installer
        .child('Information', roleName: 'alert')
        .child('Installation complete!', roleName: 'label')
      true
    end
  rescue Exception => e
    path = $vm.execute_successfully('ls -1 /tmp/tails-installer-*').stdout.chomp
    debug_log("Tails Installer debug log:\n" + $vm.file_content(path))
    raise e
  end
153
154
end

155
156
157
158
159
Given /^I plug and mount a USB drive containing the Tails ISO$/ do
  iso_dir = share_host_files(TAILS_ISO)
  @iso_path = "#{iso_dir}/#{File.basename(TAILS_ISO)}"
end

160
161
Given /^I enable all persistence presets$/ do
  @screen.wait('PersistenceWizardPresets.png', 20)
162
163
164
165
166
167
  # 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)
168
  end
169
  @screen.wait_and_click('PersistenceWizardSave.png', 10)
intrigeri's avatar
intrigeri committed
170
  @screen.wait('PersistenceWizardDone.png', 60)
171
  @screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
172
173
end

174
When /^I disable the first persistence preset$/ do
anonym's avatar
anonym committed
175
  step 'I start "Configure persistent volume" via GNOME Activities Overview'
176
177
178
179
180
181
182
  @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

183
Given /^I create a persistent partition$/ do
anonym's avatar
anonym committed
184
  step 'I start "Configure persistent volume" via GNOME Activities Overview'
intrigeri's avatar
intrigeri committed
185
  @screen.wait('PersistenceWizardStart.png', 60)
186
  @screen.type(@persistence_password + "\t" + @persistence_password + Sikuli::Key.ENTER)
Tails developers's avatar
Tails developers committed
187
  @screen.wait('PersistenceWizardPresets.png', 300)
188
189
190
  step "I enable all persistence presets"
end

191
def check_disk_integrity(name, dev, scheme)
192
  info = $vm.execute("udisksctl info --block-device '#{dev}'").stdout
193
194
195
196
197
198
199
  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

200
def check_part_integrity(name, dev, usage, fs_type, part_label, part_type = nil)
201
  info = $vm.execute("udisksctl info --block-device '#{dev}'").stdout
202
  info_split = info.split("\n  org\.freedesktop\.UDisks2\.Partition:\n")
203
204
  dev_info = info_split[0]
  part_info = info_split[1]
205
  assert(dev_info.match("^    IdUsage: +#{usage}$"),
206
         "Unexpected device field 'usage' on USB drive '#{name}', '#{dev}'")
207
208
209
  assert(dev_info.match("^    IdType: +#{fs_type}$"),
         "Unexpected device field 'IdType' on USB drive '#{name}', '#{dev}'")
  assert(part_info.match("^    Name: +#{part_label}$"),
210
         "Unexpected partition label on USB drive '#{name}', '#{dev}'")
211
212
213
214
  if part_type
    assert(part_info.match("^    Type: +#{part_type}$"),
           "Unexpected partition type on USB drive '#{name}', '#{dev}'")
  end
215
216
end

217
def tails_is_installed_helper(name, tails_root, loader)
218
  disk_dev = $vm.disk_dev(name)
219
220
  part_dev = disk_dev + "1"
  check_disk_integrity(name, disk_dev, "gpt")
221
222
223
  check_part_integrity(name, part_dev, "filesystem", "vfat", "Tails",
                       # EFI System Partition
                       'c12a7328-f81f-11d2-ba4b-00a0c93ec93b')
224

225
  target_root = "/mnt/new"
226
  $vm.execute("mkdir -p #{target_root}")
227
  $vm.execute("mount #{part_dev} #{target_root}")
228

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

233
  syslinux_files = $vm.execute("ls -1 #{target_root}/syslinux").stdout.chomp.split
234
  # We deal with these files separately
235
  ignores = ["syslinux.cfg", "exithelp.cfg", "ldlinux.c32", "ldlinux.sys"]
236
  for f in syslinux_files - ignores do
237
    c = $vm.execute("diff -q '#{tails_root}/#{loader}/#{f}' " +
238
                    "'#{target_root}/syslinux/#{f}'")
239
240
241
242
243
    assert(c.success?, "USB drive '#{name}' has differences in " +
           "'/syslinux/#{f}'")
  end

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

249
250
  $vm.execute("umount #{target_root}")
  $vm.execute("sync")
251
252
end

253
254
255
256
257
258
259
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|
  iso_root = "/mnt/iso"
260
  $vm.execute("mkdir -p #{iso_root}")
261
  $vm.execute("mount -o loop #{@iso_path} #{iso_root}")
Tails developers's avatar
Tails developers committed
262
  tails_is_installed_helper(target_name, iso_root, "isolinux")
263
  $vm.execute("umount #{iso_root}")
264
265
end

266
Then /^there is no persistence partition on USB drive "([^"]+)"$/ do |name|
267
268
  data_part_dev = $vm.disk_dev(name) + "2"
  assert(!$vm.execute("test -b #{data_part_dev}").success?,
269
270
271
         "USB drive #{name} has a partition '#{data_part_dev}'")
end

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

276
277
  # The LUKS container may already be opened, e.g. by udisks after
  # we've run tails-persistence-setup.
278
  c = $vm.execute("ls -1 --hide 'control' /dev/mapper/")
279
280
  if c.success?
    for candidate in c.stdout.split("\n")
281
      luks_info = $vm.execute("cryptsetup status '#{candidate}'")
282
283
284
285
286
287
288
      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?
289
290
    c = $vm.execute("echo #{@persistence_password} | " +
                    "cryptsetup luksOpen #{dev} #{name}")
291
292
293
    assert(c.success?, "Couldn't open LUKS device '#{dev}' on  drive '#{name}'")
    luks_dev = "/dev/mapper/#{name}"
  end
294
295

  # Adapting check_part_integrity() seems like a bad idea so here goes
296
  info = $vm.execute("udisksctl info --block-device '#{luks_dev}'").stdout
297
298
299
300
  assert info.match("^    CryptoBackingDevice: +'/[a-zA-Z0-9_/]+'$")
  assert info.match("^    IdUsage: +filesystem$")
  assert info.match("^    IdType: +ext[34]$")
  assert info.match("^    IdLabel: +TailsData$")
301
302

  mount_dir = "/mnt/#{name}"
303
  $vm.execute("mkdir -p #{mount_dir}")
304
  c = $vm.execute("mount '#{luks_dev}' #{mount_dir}")
305
  assert(c.success?,
Tails developers's avatar
Tails developers committed
306
         "Couldn't mount opened LUKS device '#{dev}' on drive '#{name}'")
307

308
309
310
  $vm.execute("umount #{mount_dir}")
  $vm.execute("sync")
  $vm.execute("cryptsetup luksClose #{name}")
311
312
end

313
Given /^I enable persistence$/ do
314
315
316
  @screen.wait_and_click('TailsGreeterPersistencePassphrase.png', 10)
  @screen.type(@persistence_password + Sikuli::Key.ENTER)
  @screen.wait('TailsGreeterPersistenceUnlocked.png', 30)
317
318
end

319
320
def tails_persistence_enabled?
  persistence_state_file = "/var/lib/live/config/tails.persistence"
321
322
  return $vm.execute("test -e '#{persistence_state_file}'").success? &&
         $vm.execute(". '#{persistence_state_file}' && " +
323
                     'test "$TAILS_PERSISTENCE_ENABLED" = true').success?
324
325
end

326
327
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.")
328
329
330
  try_for(120, :msg => "Persistence is disabled") do
    tails_persistence_enabled?
  end
331
  unexpected_mounts = Array.new
332
  # Check that all persistent directories are mounted
333
334
  if old_tails.empty?
    expected_mounts = persistent_mounts
335
336
337
338
339
340
    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
341
  else
342
    assert_not_nil($remembered_persistence_mounts)
343
    expected_mounts = $remembered_persistence_mounts
344
  end
345
  mount = $vm.execute("mount").stdout.chomp
346
  for _, dir in expected_mounts do
347
348
349
    assert(mount.include?("on #{dir} "),
           "Persistent directory '#{dir}' is not mounted")
  end
350
351
352
353
  for dir in unexpected_mounts do
    assert(! mount.include?("on #{dir} "),
           "Persistent directory '#{dir}' is mounted")
  end
354
355
end

356
357
358
359
Given /^persistence is disabled$/ do
  assert(!tails_persistence_enabled?, "Persistence is enabled")
end

360
def boot_device
361
362
  # Approach borrowed from
  # config/chroot_local_includes/lib/live/config/998-permissions
363
364
  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
365
366
367
  return boot_dev
end

anonym's avatar
anonym committed
368
def device_info(dev)
369
370
  # Approach borrowed from
  # config/chroot_local_includes/lib/live/config/998-permissions
anonym's avatar
anonym committed
371
372
373
374
375
376
  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']
377
378
end

379
380
381
Then /^Tails is running from (.*) drive "([^"]+)"$/ do |bus, name|
  bus = bus.downcase
  case bus
382
  when "sata"
383
384
385
386
387
    expected_bus = "ata"
  else
    expected_bus = bus
  end
  assert_equal(expected_bus, boot_device_type)
388
  actual_dev = boot_device
389
390
391
392
393
394
  # 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),
395
         "We are running from device #{actual_dev}, but for #{bus} drive " +
396
         "'#{name}' we expected to run from one of #{expected_devs}")
397
398
399
400
401
end

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

  super_boot_dev = boot_device.sub(/[[:digit:]]+$/, "")
402
  devs = $vm.execute("ls -1 #{super_boot_dev}*").stdout.chomp.split
403
  assert(devs.size > 0, "Could not determine boot device")
404
  all_users = $vm.execute("cut -d':' -f1 /etc/passwd").stdout.chomp.split
405
  all_users_with_groups = all_users.collect do |user|
406
    groups = $vm.execute("groups #{user}").stdout.chomp.sub(/^#{user} : /, "").split(" ")
407
408
409
    [user, groups]
  end
  for dev in devs do
410
411
412
    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
413
    assert_equal("root", dev_owner)
414
415
    assert(dev_group == "disk" || dev_group == "root",
           "Boot device '#{dev}' owned by group '#{dev_group}', expected " +
416
           "'disk' or 'root'.")
417
    assert_equal("660", dev_perms)
418
419
420
421
422
423
424
    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
425

426
  info = $vm.execute("udisksctl info --block-device '#{super_boot_dev}'").stdout
intrigeri's avatar
intrigeri committed
427
  assert(info.match("^    HintSystem: +true$"),
428
         "Boot device '#{super_boot_dev}' is not system internal for udisks")
429
430
end

431
Then /^all persistent filesystems have safe access rights$/ do
432
  persistent_volumes_mountpoints.each do |mountpoint|
433
434
435
    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
436
437
438
    assert_equal("root", fs_owner)
    assert_equal("root", fs_group)
    assert_equal('775', fs_perms)
439
440
441
  end
end

442
Then /^all persistence configuration files have safe access rights$/ do
443
  persistent_volumes_mountpoints.each do |mountpoint|
444
    assert($vm.execute("test -e #{mountpoint}/persistence.conf").success?,
445
           "#{mountpoint}/persistence.conf does not exist, while it should")
446
    assert($vm.execute("test ! -e #{mountpoint}/live-persistence.conf").success?,
447
           "#{mountpoint}/live-persistence.conf does exist, while it should not")
448
    $vm.execute(
449
450
      "ls -1 #{mountpoint}/persistence.conf #{mountpoint}/live-*.conf"
    ).stdout.chomp.split.each do |f|
451
452
453
      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
454
455
456
      assert_equal("tails-persistence-setup", file_owner)
      assert_equal("tails-persistence-setup", file_group)
      assert_equal("600", file_perms)
457
    end
Tails developers's avatar
Tails developers committed
458
  end
459
460
end

461
Then /^all persistent directories(| from the old Tails version) have safe access rights$/ do |old_tails|
462
463
464
  if old_tails.empty?
    expected_dirs = persistent_dirs
  else
465
    assert_not_nil($remembered_persistence_dirs)
466
    expected_dirs = $remembered_persistence_dirs
467
  end
468
  persistent_volumes_mountpoints.each do |mountpoint|
469
    expected_dirs.each do |src, dest|
Tails developers's avatar
Tails developers committed
470
      full_src = "#{mountpoint}/#{src}"
471
472
473
      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
474
      if dest.start_with?("/home/#{LIVE_USER}")
475
        expected_perms = "700"
476
        expected_owner = LIVE_USER
477
478
479
480
481
482
483
484
485
486
      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}")
487
488
489
490
    end
  end
end

491
When /^I write some files expected to persist$/ do
492
  persistent_mounts.each do |_, dir|
493
    owner = $vm.execute("stat -c %U #{dir}").stdout.chomp
494
    assert($vm.execute("touch #{dir}/XXX_persist", :user => owner).success?,
495
           "Could not create file in persistent directory #{dir}")
496
497
498
  end
end

499
500
501
502
503
504
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

505
When /^I remove some files expected to persist$/ do
506
  persistent_mounts.each do |_, dir|
507
    owner = $vm.execute("stat -c %U #{dir}").stdout.chomp
508
    assert($vm.execute("rm #{dir}/XXX_persist", :user => owner).success?,
509
           "Could not remove file in persistent directory #{dir}")
510
511
512
513
  end
end

When /^I write some files not expected to persist$/ do
514
  persistent_mounts.each do |_, dir|
515
    owner = $vm.execute("stat -c %U #{dir}").stdout.chomp
516
    assert($vm.execute("touch #{dir}/XXX_gone", :user => owner).success?,
517
           "Could not create file in persistent directory #{dir}")
518
519
520
  end
end

521
When /^I take note of which persistence presets are available$/ do
522
523
  $remembered_persistence_mounts = persistent_mounts
  $remembered_persistence_dirs = persistent_dirs
524
525
526
527
528
529
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
530
    assert_not_nil($remembered_persistence_mounts)
531
    expected_mounts = $remembered_persistence_mounts
532
533
  end
  expected_mounts.each do |_, dir|
534
    assert($vm.execute("test -e #{dir}/XXX_persist").success?,
535
           "Could not find expected file in persistent directory #{dir}")
536
    assert(!$vm.execute("test -e #{dir}/XXX_gone").success?,
537
538
539
540
           "Found file that should not have persisted in persistent directory #{dir}")
  end
end

541
542
543
544
545
546
547
548
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

549
Then /^only the expected files are present on the persistence partition on USB drive "([^"]+)"$/ do |name|
550
  assert(!$vm.is_running?)
551
  disk = {
552
    :path => $vm.storage.disk_path(name),
553
    :opts => {
554
      :format => $vm.storage.disk_format(name),
555
556
557
      :readonly => true
    }
  }
558
  $vm.storage.guestfs_disk_helper(disk) do |g, disk_handle|
559
560
561
562
563
564
565
566
567
    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"
568
    g.luks_open(partition, @persistence_password, luks_mapping)
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
    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
585
end
586
587

When /^I delete the persistent partition$/ do
anonym's avatar
anonym committed
588
  step 'I start "Delete persistent volume" via GNOME Activities Overview'
intrigeri's avatar
intrigeri committed
589
  @screen.wait("PersistenceWizardDeletionStart.png", 120)
590
591
592
  @screen.type(" ")
  @screen.wait("PersistenceWizardDone.png", 120)
end
593
594

Then /^Tails has started in UEFI mode$/ do
595
  assert($vm.execute("test -d /sys/firmware/efi").success?,
596
597
         "/sys/firmware/efi does not exist")
 end
598
599

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

603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
Given /^the file system changes introduced in version (.+) are (not )?present(?: in the (\S+) Browser's chroot)?$/ do |version, not_present, chroot_browser|
  assert_equal('1.1~test', version)
  upgrade_applied = not_present.nil?
  chroot_browser = "#{chroot_browser.downcase}-browser" if chroot_browser
  changes = [
    {
      filesystem: :rootfs,
      path: 'some_new_file',
      status: :added,
      new_content: <<-EOF
Some content
      EOF
    },
    {
      filesystem: :rootfs,
      path: 'etc/amnesia/version',
      status: :modified,
      new_content: <<-EOF
#{version} - 20380119
ffffffffffffffffffffffffffffffffffffffff
live-build: 3.0.5+really+is+2.0.12-0.tails2
live-boot: 4.0.2-1
live-config: 4.0.4-1
      EOF
    },
    {
      filesystem: :rootfs,
      path: 'etc/os-release',
      status: :modified,
      new_content: <<-EOF
TAILS_PRODUCT_NAME="Tails"
TAILS_VERSION_ID="#{version}"
      EOF
    },
    {
      filesystem: :rootfs,
      path: 'usr/share/common-licenses/BSD',
      status: :removed
    },
    {
      filesystem: :medium,
      path: 'utils/linux/syslinux',
      status: :removed
    },
  ]
  changes.each do |change|
    case change[:filesystem]
    when :rootfs
      path = '/'
      path += "var/lib/#{chroot_browser}/chroot/" if chroot_browser
      path += change[:path]
    when :medium
      path = '/lib/live/mount/medium/' + change[:path]
    else
      raise "Unknown filesysten '#{change[:filesystem]}'"
    end
    case change[:status]
    when :removed
      assert_equal(!upgrade_applied, $vm.file_exist?(path))
    when :added
      assert_equal(upgrade_applied, $vm.file_exist?(path))
      if upgrade_applied && change[:new_content]
        assert_equal(change[:new_content], $vm.file_content(path))
      end
    when :modified
      assert($vm.file_exist?(path))
      if upgrade_applied
        assert_not_nil(change[:new_content])
        assert_equal(change[:new_content], $vm.file_content(path))
      end
    else
      raise "Unknown status '#{change[:status]}'"
    end
  end
end

Then /^I am proposed to install an incremental upgrade to version (.+)$/ do |version|
680
  recovery_proc = Proc.new do
681
    recover_from_upgrader_failure
682
683
684
685
686
687
688
  end
  failure_pic = 'TailsUpgraderFailure.png'
  success_pic = "TailsUpgraderUpgradeTo#{version}.png"
  retry_tor(recovery_proc) do
    match, _ = @screen.waitAny([success_pic, failure_pic], 2*60)
    assert_equal(success_pic, match)
  end
689
690
691
692
693
694
end

When /^I agree to install the incremental upgrade$/ do
  @screen.click('TailsUpgraderUpgradeNowButton.png')
end

anonym's avatar
anonym committed
695
696
Then /^I can successfully install the incremental upgrade to version (.+)$/ do |version|
  step 'I agree to install the incremental upgrade'
697
698
699
700
701
702
703
704
705
706
707
  recovery_proc = Proc.new do
    recover_from_upgrader_failure
    step "I am proposed to install an incremental upgrade to version #{version}"
    step 'I agree to install the incremental upgrade'
  end
  failure_pic = 'TailsUpgraderFailure.png'
  success_pic = "TailsUpgraderDone.png"
  retry_tor(recovery_proc) do
    match, _ = @screen.waitAny([success_pic, failure_pic], 2*60)
    assert_equal(success_pic, match)
  end
708
end