]> git.proxmox.com Git - pve-container.git/blob - src/test/bindmount_test.pl
fix and improve bindmount tests
[pve-container.git] / src / test / bindmount_test.pl
1 #!/usr/bin/perl
2
3 use strict;
4 use warnings;
5
6 use Cwd;
7 use File::Path;
8
9 use lib qw(..);
10
11 use PVE::LXC;
12
13 my $pwd = getcwd();
14
15 my $rootdir = "./tmproot";
16 my $destdir = "/mnt";
17
18 my $sharedir = "/tmpshare";
19 my $a = "/tmpshare/a";
20 my $ab = "/tmpshare/a/b";
21 my $abc = "/tmpshare/a/b/c";
22 my $sym = "/tmpshare/sym";
23 my $tmp = "/tmpshare/tmp";
24
25 my $secret = "/secret";
26
27 END { cleanup(); };
28 sub cleanup {
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});
33 }
34
35 sub 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 }
44
45 setup();
46
47 # Test walk_tree_nofollow:
48 eval { PVE::LXC::walk_tree_nofollow('.', $rootdir, 0) };
49 die "unexpected error: $@" if $@;
50 eval { PVE::LXC::walk_tree_nofollow('.', "$sym/c", 0) };
51 die "failed to catch symlink at $sym/c\n" if !$@;
52 die "unexpected test error: '$@'\n" if $@ ne "symlink encountered at: .$sym\n";
53
54 # Bindmount testing:
55 sub bindmount {
56 my ($from, $rootdir, $destdir, $inject, $ro, $inject_write, $restore) = @_;
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) {
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");
74 }
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 = $@;
87 system('umount', $mpath);
88 die $err if $err;
89 }
90
91 bindmount($a, $rootdir, $destdir, undef, 0, 0);
92 eval { bindmount($sym, $rootdir, $destdir, undef, 0, 0) };
93 die "illegal symlink bindmount went through\n" if !$@;
94 die "unexpected test error: $@\n" if $@ ne "symlink encountered at: .$sym\n";
95 bindmount($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
99 bindmount($abc, $rootdir, $destdir, undef, 0, 1);
100 # Second: regular read-only mount, should pass.
101 bindmount($abc, $rootdir, $destdir, undef, 1, 0);
102 # Third: read-only requested with read-write injected
103 eval { bindmount($abc, $rootdir, $destdir, undef, 1, 1) };
104 die "read-write mount possible\n" if !$@;
105 die "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().
108 eval { bindmount($abc, $rootdir, $destdir, $ab, 0, 0) };
109 die "injected symlink bindmount went through\n" if !$@;
110 die "unexpected test error: $@\n" if $@ ne "bindmount verification failed\n";
111 # Restore setup:
112 cleanup();
113 setup();
114 # Race test 2: As above but also reset the symlink back after __bindmount_do()
115 eval { bindmount($abc, $rootdir, $destdir, $ab, 0, 0, 1) };
116 die "injected symlink bindmount went through\n" if !$@;
117 die "unexpected test error: $@\n" if $@ ne "bindmount verification failed\n";