usb.rb 26.5 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
77
78
79
80
81
82
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)
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
class UpgradeNotSupported < StandardError
91
92
end

93
def usb_install_helper(name)
anonym's avatar
anonym committed
94
  if tails_installer_match_status('It is impossible to upgrade the device')
95
    raise UpgradeNotSupported
96
  end
97
  assert(tails_installer_is_device_selected?(name))
98
  begin
99
100
101
102
    @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)
103
  rescue FindFailed => e
104
105
    path = $vm.execute_successfully('ls -1 /tmp/tails-installer-*').stdout.chomp
    debug_log("Tails Installer debug log:\n" + $vm.file_content(path))
106
107
    raise e
  end
108
109
end

110
When /^I start Tails Installer in "([^"]+)" mode$/ do |mode|
111
112
113
  step 'I run "export DEBUG=1 ; tails-installer-launcher" in GNOME Terminal'
  installer_launcher = Dogtail::Application.new('tails-installer-launcher')
  installer_launcher.wait(10)
114
  installer_launcher.button(mode).click
115
  @installer = Dogtail::Application.new('tails-installer')
116
  @installer.child('Tails Installer', roleName: 'frame').wait
117
118
end

119
Then /^Tails Installer detects that a device is too small$/ do
120
  try_for(10) do
121
    tails_installer_match_status(/^The device .* is too small to install Tails/)
122
  end
123
124
end

125
126
127
128
129
130
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

anonym's avatar
anonym committed
131
When /^I am suggested to do a "Install by cloning"$/ do
132
133
134
135
136
137
  try_for(10) do
    tails_installer_match_status(
      /You should instead use "Install by cloning" to upgrade Tails/
    )
  end
end
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

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

154
155
When /^I "([^"]*)" Tails to USB drive "([^"]+)"$/ do |mode, name|
  step "I start Tails Installer in \"#{mode}\" mode"
156
157
158
  usb_install_helper(name)
end

anonym's avatar
anonym committed
159
When /^I fail to "([^"]*)" Tails to USB drive "([^"]+)"$/ do |mode, name|
160
  begin
161
    step "I \"#{mode}\" Tails to USB drive \"#{name}\""
162
  rescue UpgradeNotSupported
163
164
165
166
167
168
    # this is what we expect
  else
    raise "The USB installer should not succeed"
  end
end

169
When /^I do a "Upgrade from ISO" on USB drive "([^"]+)"$/ do |name|
170
  step 'I start Tails Installer in "Upgrade from ISO" mode'
171
172
173
174
  @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)
175
  @screen.type("l", Sikuli::KeyModifier.CTRL)
176
  # The only visible text element will be the path entry
177
  file_chooser.child(roleName: 'text').text = @iso_path
178
  file_chooser.button('Open').click
179
180
181
182
183
  usb_install_helper(name)
end

Given /^I enable all persistence presets$/ do
  @screen.wait('PersistenceWizardPresets.png', 20)
184
185
186
187
188
189
  # 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)
190
  end
191
  @screen.wait_and_click('PersistenceWizardSave.png', 10)
intrigeri's avatar
intrigeri committed
192
  @screen.wait('PersistenceWizardDone.png', 60)
193
  @screen.type(Sikuli::Key.F4, Sikuli::KeyModifier.ALT)
194
195
end

196
When /^I disable the first persistence preset$/ do
197
  step 'I start "Configure persistent volume" via the GNOME "Tails" applications menu'
198
199
200
201
202
203
204
  @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

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

213
def check_disk_integrity(name, dev, scheme)
214
  info = $vm.execute("udisksctl info --block-device '#{dev}'").stdout
215
216
217
218
219
220
221
  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

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

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

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

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

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

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

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

275
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|
  iso_root = "/mnt/iso"
282
  $vm.execute("mkdir -p #{iso_root}")
283
  $vm.execute("mount -o loop #{@iso_path} #{iso_root}")
Tails developers's avatar
Tails developers committed
284
  tails_is_installed_helper(target_name, iso_root, "isolinux")
285
  $vm.execute("umount #{iso_root}")
286
287
end

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

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

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

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

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

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

335
Given /^I enable persistence$/ do
336
337
338
  @screen.wait_and_click('TailsGreeterPersistencePassphrase.png', 10)
  @screen.type(@persistence_password + Sikuli::Key.ENTER)
  @screen.wait('TailsGreeterPersistenceUnlocked.png', 30)
339
340
end

341
342
def tails_persistence_enabled?
  persistence_state_file = "/var/lib/live/config/tails.persistence"
343
344
  return $vm.execute("test -e '#{persistence_state_file}'").success? &&
         $vm.execute(". '#{persistence_state_file}' && " +
345
                     'test "$TAILS_PERSISTENCE_ENABLED" = true').success?
346
347
end

348
349
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.")
350
351
352
  try_for(120, :msg => "Persistence is disabled") do
    tails_persistence_enabled?
  end
353
  unexpected_mounts = Array.new
354
  # Check that all persistent directories are mounted
355
356
  if old_tails.empty?
    expected_mounts = persistent_mounts
357
358
359
360
361
362
    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
363
  else
364
    assert_not_nil($remembered_persistence_mounts)
365
    expected_mounts = $remembered_persistence_mounts
366
  end
367
  mount = $vm.execute("mount").stdout.chomp
368
  for _, dir in expected_mounts do
369
370
371
    assert(mount.include?("on #{dir} "),
           "Persistent directory '#{dir}' is not mounted")
  end
372
373
374
375
  for dir in unexpected_mounts do
    assert(! mount.include?("on #{dir} "),
           "Persistent directory '#{dir}' is mounted")
  end
376
377
end

378
379
380
381
Given /^persistence is disabled$/ do
  assert(!tails_persistence_enabled?, "Persistence is enabled")
end

382
def boot_device
383
384
  # Approach borrowed from
  # config/chroot_local_includes/lib/live/config/998-permissions
385
386
  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
387
388
389
  return boot_dev
end

anonym's avatar
anonym committed
390
def device_info(dev)
391
392
  # Approach borrowed from
  # config/chroot_local_includes/lib/live/config/998-permissions
anonym's avatar
anonym committed
393
394
395
396
397
398
  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']
399
400
end

401
402
403
Then /^Tails is running from (.*) drive "([^"]+)"$/ do |bus, name|
  bus = bus.downcase
  case bus
404
  when "sata"
405
406
407
408
409
    expected_bus = "ata"
  else
    expected_bus = bus
  end
  assert_equal(expected_bus, boot_device_type)
410
  actual_dev = boot_device
411
412
413
414
415
416
  # 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),
417
         "We are running from device #{actual_dev}, but for #{bus} drive " +
418
         "'#{name}' we expected to run from one of #{expected_devs}")
419
420
421
422
423
end

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

  super_boot_dev = boot_device.sub(/[[:digit:]]+$/, "")
424
  devs = $vm.execute("ls -1 #{super_boot_dev}*").stdout.chomp.split
425
  assert(devs.size > 0, "Could not determine boot device")
426
  all_users = $vm.execute("cut -d':' -f1 /etc/passwd").stdout.chomp.split
427
  all_users_with_groups = all_users.collect do |user|
428
    groups = $vm.execute("groups #{user}").stdout.chomp.sub(/^#{user} : /, "").split(" ")
429
430
431
    [user, groups]
  end
  for dev in devs do
432
433
434
    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
435
    assert_equal("root", dev_owner)
436
437
    assert(dev_group == "disk" || dev_group == "root",
           "Boot device '#{dev}' owned by group '#{dev_group}', expected " +
438
           "'disk' or 'root'.")
439
    assert_equal("660", dev_perms)
440
441
442
443
444
445
446
    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
447

448
  info = $vm.execute("udisksctl info --block-device '#{super_boot_dev}'").stdout
intrigeri's avatar
intrigeri committed
449
  assert(info.match("^    HintSystem: +true$"),
450
         "Boot device '#{super_boot_dev}' is not system internal for udisks")
451
452
end

453
Then /^all persistent filesystems have safe access rights$/ do
454
  persistent_volumes_mountpoints.each do |mountpoint|
455
456
457
    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
458
459
460
    assert_equal("root", fs_owner)
    assert_equal("root", fs_group)
    assert_equal('775', fs_perms)
461
462
463
  end
end

464
Then /^all persistence configuration files have safe access rights$/ do
465
  persistent_volumes_mountpoints.each do |mountpoint|
466
    assert($vm.execute("test -e #{mountpoint}/persistence.conf").success?,
467
           "#{mountpoint}/persistence.conf does not exist, while it should")
468
    assert($vm.execute("test ! -e #{mountpoint}/live-persistence.conf").success?,
469
           "#{mountpoint}/live-persistence.conf does exist, while it should not")
470
    $vm.execute(
471
472
      "ls -1 #{mountpoint}/persistence.conf #{mountpoint}/live-*.conf"
    ).stdout.chomp.split.each do |f|
473
474
475
      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
476
477
478
      assert_equal("tails-persistence-setup", file_owner)
      assert_equal("tails-persistence-setup", file_group)
      assert_equal("600", file_perms)
479
    end
Tails developers's avatar
Tails developers committed
480
  end
481
482
end

483
Then /^all persistent directories(| from the old Tails version) have safe access rights$/ do |old_tails|
484
485
486
  if old_tails.empty?
    expected_dirs = persistent_dirs
  else
487
    assert_not_nil($remembered_persistence_dirs)
488
    expected_dirs = $remembered_persistence_dirs
489
  end
490
  persistent_volumes_mountpoints.each do |mountpoint|
491
    expected_dirs.each do |src, dest|
Tails developers's avatar
Tails developers committed
492
      full_src = "#{mountpoint}/#{src}"
493
494
495
      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
496
      if dest.start_with?("/home/#{LIVE_USER}")
497
        expected_perms = "700"
498
        expected_owner = LIVE_USER
499
500
501
502
503
504
505
506
507
508
      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}")
509
510
511
512
    end
  end
end

513
When /^I write some files 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_persist", :user => owner).success?,
517
           "Could not create file in persistent directory #{dir}")
518
519
520
  end
end

521
522
523
524
525
526
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

527
When /^I remove some files expected to persist$/ do
528
  persistent_mounts.each do |_, dir|
529
    owner = $vm.execute("stat -c %U #{dir}").stdout.chomp
530
    assert($vm.execute("rm #{dir}/XXX_persist", :user => owner).success?,
531
           "Could not remove file in persistent directory #{dir}")
532
533
534
535
  end
end

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

543
When /^I take note of which persistence presets are available$/ do
544
545
  $remembered_persistence_mounts = persistent_mounts
  $remembered_persistence_dirs = persistent_dirs
546
547
548
549
550
551
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
552
    assert_not_nil($remembered_persistence_mounts)
553
    expected_mounts = $remembered_persistence_mounts
554
555
  end
  expected_mounts.each do |_, dir|
556
    assert($vm.execute("test -e #{dir}/XXX_persist").success?,
557
           "Could not find expected file in persistent directory #{dir}")
558
    assert(!$vm.execute("test -e #{dir}/XXX_gone").success?,
559
560
561
562
           "Found file that should not have persisted in persistent directory #{dir}")
  end
end

563
564
565
566
567
568
569
570
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

571
Then /^only the expected files are present on the persistence partition on USB drive "([^"]+)"$/ do |name|
572
  assert(!$vm.is_running?)
573
  disk = {
574
    :path => $vm.storage.disk_path(name),
575
    :opts => {
576
      :format => $vm.storage.disk_format(name),
577
578
579
      :readonly => true
    }
  }
580
  $vm.storage.guestfs_disk_helper(disk) do |g, disk_handle|
581
582
583
584
585
586
587
588
589
    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"
590
    g.luks_open(partition, @persistence_password, luks_mapping)
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
    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
607
end
608
609

When /^I delete the persistent partition$/ do
610
  step 'I start "Delete persistent volume" via the GNOME "Tails" applications menu'
intrigeri's avatar
intrigeri committed
611
  @screen.wait("PersistenceWizardDeletionStart.png", 120)
612
613
614
  @screen.type(" ")
  @screen.wait("PersistenceWizardDone.png", 120)
end
615
616

Then /^Tails has started in UEFI mode$/ do
617
  assert($vm.execute("test -d /sys/firmware/efi").success?,
618
619
         "/sys/firmware/efi does not exist")
 end
620
621

Given /^I create a ([[:alpha:]]+) label on disk "([^"]+)"$/ do |type, name|
622
  $vm.storage.disk_mklabel(name, type)
623
end
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
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
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|
702
  recovery_proc = Proc.new do
703
    recover_from_upgrader_failure
704
705
706
707
708
709
710
  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
711
712
713
714
715
716
end

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

anonym's avatar
anonym committed
717
718
Then /^I can successfully install the incremental upgrade to version (.+)$/ do |version|
  step 'I agree to install the incremental upgrade'
719
720
721
722
723
724
725
726
727
728
729
  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
730
end