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
anonym's avatar
anonym committed
91
  @installer_log_path = '/tmp/tails-installer.log'
92
  step "I run \"/usr/bin/tails-installer --verbose > #{@installer_log_path} 2>&1\" in GNOME Terminal"
93
  @installer = Dogtail::Application.new('tails-installer')
94
  @installer.child('Tails Installer', roleName: 'frame')
95
96
97
98
99
  # 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.
100
101
  sleep 3
  $vm.focus_window('Tails Installer')
102
103
end

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

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

126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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
anonym's avatar
anonym committed
150
151
    debug_log("Tails Installer debug log:\n" +
              $vm.file_content(@installer_log_path))
152
153
    raise e
  end
154
155
end

156
157
158
159
160
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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Given /^I create a ([[:alpha:]]+) label on disk "([^"]+)"$/ do |type, name|
601
  $vm.storage.disk_mklabel(name, type)
602
end
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
680
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|
681
  recovery_proc = Proc.new do
682
    recover_from_upgrader_failure
683
684
685
686
687
688
689
  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
690
691
692
693
694
695
end

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

anonym's avatar
anonym committed
696
697
Then /^I can successfully install the incremental upgrade to version (.+)$/ do |version|
  step 'I agree to install the incremental upgrade'
698
699
700
701
702
703
704
705
706
707
708
  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
709
end