]>
Commit | Line | Data |
---|---|---|
619f27b4 WB |
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"; | |
6c25d5bb | 23 | my $tmp = "/tmpshare/tmp"; |
619f27b4 WB |
24 | |
25 | my $secret = "/secret"; | |
26 | ||
6c25d5bb WB |
27 | END { cleanup(); }; |
28 | sub 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 | ||
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 | } | |
619f27b4 | 44 | |
6c25d5bb | 45 | setup(); |
619f27b4 WB |
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 { | |
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 | ||
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 !$@; | |
6c25d5bb | 94 | die "unexpected test error: $@\n" if $@ ne "symlink encountered at: .$sym\n"; |
619f27b4 WB |
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 !$@; | |
6c25d5bb WB |
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"; |