]> git.proxmox.com Git - pve-container.git/blame - src/test/bindmount_test.pl
fix and improve bindmount tests
[pve-container.git] / src / test / bindmount_test.pl
CommitLineData
619f27b4
WB
1#!/usr/bin/perl
2
3use strict;
4use warnings;
5
6use Cwd;
7use File::Path;
8
9use lib qw(..);
10
11use PVE::LXC;
12
13my $pwd = getcwd();
14
15my $rootdir = "./tmproot";
16my $destdir = "/mnt";
17
18my $sharedir = "/tmpshare";
19my $a = "/tmpshare/a";
20my $ab = "/tmpshare/a/b";
21my $abc = "/tmpshare/a/b/c";
22my $sym = "/tmpshare/sym";
6c25d5bb 23my $tmp = "/tmpshare/tmp";
619f27b4
WB
24
25my $secret = "/secret";
26
6c25d5bb
WB
27END { cleanup(); };
28sub cleanup {
619f27b4
WB
29 my $ignore_error;
30 File::Path::rmtree("$pwd/$rootdir", {error => \$ignore_error});
31 File::Path::rmtree("$pwd/$sharedir", {error => \$ignore_error});
32 File::Path::rmtree("$pwd/$secret", {error => \$ignore_error});
6c25d5bb
WB
33}
34
35sub setup {
36 # Create all the test paths...
37 PVE::LXC::walk_tree_nofollow('.', $rootdir, 1);
38 PVE::LXC::walk_tree_nofollow($rootdir, $destdir, 1);
39 PVE::LXC::walk_tree_nofollow('.', $abc, 1);
40 PVE::LXC::walk_tree_nofollow('.', $secret, 1);
41 # Create one evil symlink
42 symlink('a/b', "$pwd/$sym") or die "failed to prepare test folders: $!\n";
43}
619f27b4 44
6c25d5bb 45setup();
619f27b4
WB
46
47# Test walk_tree_nofollow:
48eval { PVE::LXC::walk_tree_nofollow('.', $rootdir, 0) };
49die "unexpected error: $@" if $@;
50eval { PVE::LXC::walk_tree_nofollow('.', "$sym/c", 0) };
51die "failed to catch symlink at $sym/c\n" if !$@;
52die "unexpected test error: '$@'\n" if $@ ne "symlink encountered at: .$sym\n";
53
54# Bindmount testing:
55sub bindmount {
6c25d5bb 56 my ($from, $rootdir, $destdir, $inject, $ro, $inject_write, $restore) = @_;
619f27b4
WB
57
58 my ($mpath, $mpfd, $parentfd, $last);
59 ($rootdir, $mpath, $mpfd, $parentfd, $last) =
60 PVE::LXC::__mount_prepare_rootdir($rootdir, $destdir);
61
62 my $srcdh = PVE::LXC::__bindmount_prepare('.', $from);
63
64 if ($inject) {
6c25d5bb
WB
65 if ($restore) {
66 rename(".$inject", ".$tmp")
67 or die "failed to move directory: $!\n";
68 } else {
69 File::Path::rmtree(".$inject");
70 }
71 symlink("$pwd/$secret", ".$inject")
72 or die "failed to create symlink\n";
73 File::Path::mkpath(".$from");
619f27b4 74 }
6c25d5bb
WB
75 eval {
76 PVE::LXC::__bindmount_do(".$from", $mpath, $inject_write ? 0 : $ro);
77
78 if ($restore) {
79 unlink(".$inject") or die "failed to restore path: $!\n";
80 rename(".$tmp", ".$inject") or die "failed to restore path: $!\n";
81 }
82
83 PVE::LXC::__bindmount_verify($srcdh, $parentfd, $last, $ro)
84 or die "bindmount verification failed\n";
85 };
86 my $err = $@;
619f27b4 87 system('umount', $mpath);
6c25d5bb 88 die $err if $err;
619f27b4
WB
89}
90
91bindmount($a, $rootdir, $destdir, undef, 0, 0);
92eval { bindmount($sym, $rootdir, $destdir, undef, 0, 0) };
93die "illegal symlink bindmount went through\n" if !$@;
6c25d5bb 94die "unexpected test error: $@\n" if $@ ne "symlink encountered at: .$sym\n";
619f27b4
WB
95bindmount($abc, $rootdir, $destdir, undef, 0, 0);
96# Race test: Assume someone exchanged 2 equivalent bind mounts between and
97# after the bindmount_do()'s mount and remount calls.
98# First: non-ro mount, should pass
99bindmount($abc, $rootdir, $destdir, undef, 0, 1);
100# Second: regular read-only mount, should pass.
101bindmount($abc, $rootdir, $destdir, undef, 1, 0);
102# Third: read-only requested with read-write injected
103eval { bindmount($abc, $rootdir, $destdir, undef, 1, 1) };
104die "read-write mount possible\n" if !$@;
105die "unexpected test error: $@\n" if $@ ne "failed to mark bind mount read only\n";
106# Race test: Replace /tmpshare/a/b with a symlink to /secret just before
107# __bindmount_do().
108eval { bindmount($abc, $rootdir, $destdir, $ab, 0, 0) };
109die "injected symlink bindmount went through\n" if !$@;
6c25d5bb
WB
110die "unexpected test error: $@\n" if $@ ne "bindmount verification failed\n";
111# Restore setup:
112cleanup();
113setup();
114# Race test 2: As above but also reset the symlink back after __bindmount_do()
115eval { bindmount($abc, $rootdir, $destdir, $ab, 0, 0, 1) };
116die "injected symlink bindmount went through\n" if !$@;
117die "unexpected test error: $@\n" if $@ ne "bindmount verification failed\n";