Import upstream version 0.0~git20210813.f3885c8
Debian Janitor
2 years ago
0 | # -*- mode: ruby -*- | |
1 | # vi: set ft=ruby : | |
2 | ||
3 | Vagrant.configure("2") do |config| | |
4 | # Fedora box is used for testing cgroup v2 support | |
5 | config.vm.box = "fedora/32-cloud-base" | |
6 | config.vm.provider :virtualbox do |v| | |
7 | v.memory = 2048 | |
8 | v.cpus = 2 | |
9 | end | |
10 | config.vm.provider :libvirt do |v| | |
11 | v.memory = 2048 | |
12 | v.cpus = 2 | |
13 | end | |
14 | config.vm.provision "shell", inline: <<-SHELL | |
15 | set -e -u -o pipefail | |
16 | # Work around dnf mirror failures by retrying a few times | |
17 | for i in $(seq 0 2); do | |
18 | sleep $i | |
19 | cat << EOF | dnf -y shell && break | |
20 | config exclude kernel,kernel-core | |
21 | config install_weak_deps false | |
22 | update | |
23 | install make golang-go libseccomp-devel git-core | |
24 | ts run | |
25 | EOF | |
26 | done | |
27 | dnf clean all | |
28 | ||
29 | SHELL | |
30 | end |
0 | # -*- mode: ruby -*- | |
1 | # vi: set ft=ruby : | |
2 | ||
3 | Vagrant.configure("2") do |config| | |
4 | # Fedora box is used for testing cgroup v2 support | |
5 | config.vm.box = "fedora/34-cloud-base" | |
6 | config.vm.provider :virtualbox do |v| | |
7 | v.memory = 2048 | |
8 | v.cpus = 2 | |
9 | end | |
10 | config.vm.provider :libvirt do |v| | |
11 | v.memory = 2048 | |
12 | v.cpus = 2 | |
13 | end | |
14 | config.vm.provision "shell", inline: <<-SHELL | |
15 | set -e -u -o pipefail | |
16 | # Work around dnf mirror failures by retrying a few times | |
17 | for i in $(seq 0 2); do | |
18 | sleep $i | |
19 | cat << EOF | dnf -y shell && break | |
20 | config exclude kernel,kernel-core | |
21 | config install_weak_deps false | |
22 | update | |
23 | install make golang-go libseccomp-devel git-core | |
24 | ts run | |
25 | EOF | |
26 | done | |
27 | dnf clean all | |
28 | ||
29 | SHELL | |
30 | end |
0 | #!/bin/bash | |
1 | set -eux -o pipefail | |
2 | VAGRANT_VERSION="2.2.10" | |
3 | ||
4 | # Based on code from https://github.com/opencontainers/runc | |
5 | DEB="vagrant_${VAGRANT_VERSION}_$(uname -m).deb" | |
6 | wget "https://releases.hashicorp.com/vagrant/${VAGRANT_VERSION}/$DEB" | |
7 | apt-get update | |
8 | apt-get install -q -y \ | |
9 | bridge-utils \ | |
10 | dnsmasq-base \ | |
11 | ebtables \ | |
12 | libvirt-bin \ | |
13 | libvirt-dev \ | |
14 | qemu-kvm \ | |
15 | qemu-utils \ | |
16 | ruby-dev \ | |
17 | ./"$DEB" | |
18 | rm -f "$DEB" | |
19 | vagrant plugin install vagrant-libvirt |
3 | 3 | test: |
4 | 4 | strategy: |
5 | 5 | matrix: |
6 | go-version: [1.14.x, 1.15.x] | |
7 | platform: [ubuntu-latest, windows-latest] | |
6 | go-version: [1.14.x, 1.15.x, 1.16.x] | |
7 | platform: [ubuntu-20.04, windows-latest] | |
8 | 8 | runs-on: ${{ matrix.platform }} |
9 | 9 | steps: |
10 | 10 | - name: Install Go |
11 | uses: actions/setup-go@v1 | |
11 | uses: actions/setup-go@v2 | |
12 | 12 | with: |
13 | 13 | go-version: ${{ matrix.go-version }} |
14 | 14 | - name: Checkout code |
15 | 15 | uses: actions/checkout@v2 |
16 | - name: Test | |
17 | run: make test | |
18 | 16 | - name: Lint |
19 | 17 | run: make lint |
20 | 18 | - name: Cross build |
21 | 19 | if: ${{ runner.os == 'Linux' }} |
22 | 20 | run: make cross |
21 | - name: Test | |
22 | run: make test | |
23 | ||
24 | # some features, like openat2, require a newer kernel | |
25 | fedora: | |
26 | # nested virtualization is only available on macOS hosts | |
27 | runs-on: macos-10.15 | |
28 | steps: | |
29 | - uses: actions/checkout@v2 | |
30 | - name: prepare vagrant | |
31 | run: | | |
32 | ln -sf .ci/Vagrantfile.fedora34 Vagrantfile | |
33 | # Retry if it fails (download.fedoraproject.org returns 404 sometimes) | |
34 | vagrant up || vagrant up | |
35 | vagrant ssh-config >> ~/.ssh/config | |
36 | ||
37 | - name: system info | |
38 | run: ssh default 'sh -exc "uname -a && df -T"' | |
39 | ||
40 | - name: tests | |
41 | run: ssh default 'cd /vagrant && make test' |
0 | dist: bionic | |
1 | os: linux | |
2 | language: minimal | |
3 | cache: | |
4 | directories: | |
5 | - /home/travis/.vagrant.d/boxes | |
6 | jobs: | |
7 | include: | |
8 | - name: "Fedora 32" | |
9 | before_install: | |
10 | - sudo .ci/install-vagrant.sh | |
11 | - ln -sf .ci/Vagrantfile.fedora32 Vagrantfile | |
12 | - sudo vagrant up && sudo mkdir -p /root/.ssh && sudo sh -c "vagrant ssh-config >> /root/.ssh/config" | |
13 | script: | |
14 | - sudo ssh default -t 'cd /vagrant && sudo make' |
0 | 0 | .SHELLFLAGS = -ec |
1 | PACKAGES ?= mountinfo mount symlink | |
1 | PACKAGES ?= mountinfo mount signal symlink | |
2 | 2 | BINDIR ?= _build/bin |
3 | 3 | CROSS ?= linux/arm linux/arm64 linux/ppc64le linux/s390x \ |
4 | 4 | freebsd/amd64 openbsd/amd64 darwin/amd64 darwin/arm64 windows/amd64 |
5 | SUDO ?= sudo -n | |
5 | 6 | |
6 | 7 | .PHONY: all |
7 | 8 | all: lint test cross |
8 | 9 | |
9 | 10 | .PHONY: test |
11 | test: RUN_VIA_SUDO = $(shell $(SUDO) true && echo -exec \"$(SUDO)\") | |
10 | 12 | test: |
11 | 13 | for p in $(PACKAGES); do \ |
12 | (cd $$p && go test -v .); \ | |
14 | (cd $$p && go test $(RUN_VIA_SUDO) -v .); \ | |
13 | 15 | done |
14 | 16 | |
15 | 17 | .PHONY: lint |
21 | 23 | done |
22 | 24 | |
23 | 25 | $(BINDIR)/golangci-lint: $(BINDIR) |
24 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(BINDIR) v1.31.0 | |
26 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(BINDIR) v1.41.1 | |
25 | 27 | |
26 | 28 | $(BINDIR): |
27 | 29 | mkdir -p $(BINDIR) |
100 | 100 | } |
101 | 101 | opt := strings.SplitN(option, "=", 2) |
102 | 102 | if len(opt) != 2 || !validFlags[opt[0]] { |
103 | return nil, fmt.Errorf("Invalid tmpfs option %q", opt) | |
103 | return nil, fmt.Errorf("invalid tmpfs option %q", opt) | |
104 | 104 | } |
105 | 105 | if !dataCollisions[opt[0]] { |
106 | 106 | // We prepend the option and add to collision map |
2 | 2 | go 1.14 |
3 | 3 | |
4 | 4 | require ( |
5 | github.com/moby/sys/mountinfo v0.4.0 | |
5 | github.com/moby/sys/mountinfo v0.4.1 | |
6 | 6 | golang.org/x/sys v0.0.0-20200922070232-aee5d888a860 |
7 | 7 | ) |
0 | github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM= | |
1 | github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= | |
0 | github.com/moby/sys/mountinfo v0.4.1 h1:1O+1cHA1aujwEwwVMa2Xm2l+gIpUHyd3+D+d7LZh1kM= | |
1 | github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= | |
2 | 2 | golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
3 | 3 | golang.org/x/sys v0.0.0-20200922070232-aee5d888a860 h1:YEu4SMq7D0cmT7CBbXfcH0NZeuChAXwsHe/9XueUO6o= |
4 | 4 | golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= |
5 | 5 | "io/ioutil" |
6 | 6 | "os" |
7 | 7 | "path" |
8 | "strings" | |
8 | 9 | "testing" |
9 | 10 | |
10 | 11 | "github.com/moby/sys/mountinfo" |
11 | 12 | ) |
12 | 13 | |
13 | 14 | func TestMountOptionsParsing(t *testing.T) { |
14 | options := "noatime,ro,size=10k" | |
15 | options := "noatime,ro,noexec,size=10k" | |
15 | 16 | |
16 | 17 | flag, data := parseOptions(options) |
17 | 18 | |
19 | 20 | t.Fatalf("Expected size=10 got %s", data) |
20 | 21 | } |
21 | 22 | |
22 | expectedFlag := NOATIME | RDONLY | |
23 | ||
24 | if flag != expectedFlag { | |
25 | t.Fatalf("Expected %d got %d", expectedFlag, flag) | |
23 | expected := NOATIME | RDONLY | NOEXEC | |
24 | ||
25 | if flag != expected { | |
26 | t.Fatalf("Expected %d got %d", expected, flag) | |
26 | 27 | } |
27 | 28 | } |
28 | 29 | |
80 | 81 | } |
81 | 82 | } |
82 | 83 | |
84 | func TestMountTmpfsOptions(t *testing.T) { | |
85 | if os.Getuid() != 0 { | |
86 | t.Skip("root required") | |
87 | } | |
88 | ||
89 | testCases := []struct { | |
90 | opts string | |
91 | expected string | |
92 | unexpected string | |
93 | }{ | |
94 | { | |
95 | opts: "exec", | |
96 | unexpected: "noexec", | |
97 | }, | |
98 | { | |
99 | opts: "noexec", | |
100 | expected: "noexec", | |
101 | unexpected: "exec", | |
102 | }, | |
103 | } | |
104 | ||
105 | target := path.Join(os.TempDir(), "mount-tmpfs-tests-"+t.Name()) | |
106 | if err := os.MkdirAll(target, 0777); err != nil { | |
107 | t.Fatal(err) | |
108 | } | |
109 | defer os.RemoveAll(target) | |
110 | ||
111 | for _, tc := range testCases { | |
112 | t.Run(tc.opts, func(t *testing.T) { | |
113 | if err := Mount("tmpfs", target, "tmpfs", tc.opts); err != nil { | |
114 | t.Fatal(err) | |
115 | } | |
116 | defer ensureUnmount(t, target) | |
117 | ||
118 | mounts, err := mountinfo.GetMounts(mountinfo.SingleEntryFilter(target)) | |
119 | if err != nil { | |
120 | t.Fatal(err) | |
121 | } | |
122 | if len(mounts) != 1 { | |
123 | t.Fatal("Mount point ", target, " not found") | |
124 | } | |
125 | entry := mounts[0] | |
126 | opts := "," + entry.Options + "," | |
127 | if tc.expected != "" && !strings.Contains(opts, ","+tc.expected+",") { | |
128 | t.Fatal("Expected option ", tc.expected, " missing from ", entry.Options) | |
129 | } | |
130 | if tc.unexpected != "" && strings.Contains(opts, ","+tc.unexpected+",") { | |
131 | t.Fatal("Unexpected option ", tc.unexpected, " in ", entry.Options) | |
132 | } | |
133 | }) | |
134 | } | |
135 | } | |
136 | ||
83 | 137 | func TestMountReadonly(t *testing.T) { |
84 | 138 | if os.Getuid() != 0 { |
85 | 139 | t.Skip("root required") |
128 | 182 | } |
129 | 183 | |
130 | 184 | func TestMergeTmpfsOptions(t *testing.T) { |
131 | options := []string{"noatime", "ro", "size=10k", "defaults", "atime", "defaults", "rw", "rprivate", "size=1024k", "slave"} | |
132 | expected := []string{"atime", "rw", "size=1024k", "slave"} | |
185 | options := []string{"noatime", "ro", "size=10k", "defaults", "noexec", "atime", "defaults", "rw", "rprivate", "size=1024k", "slave", "exec"} | |
186 | expected := []string{"atime", "rw", "size=1024k", "slave", "exec"} | |
133 | 187 | merged, err := MergeTmpfsOptions(options) |
134 | 188 | if err != nil { |
135 | 189 | t.Fatal(err) |
143 | 197 | } |
144 | 198 | } |
145 | 199 | |
146 | options = []string{"noatime", "ro", "size=10k", "atime", "rw", "rprivate", "size=1024k", "slave", "size"} | |
200 | options = []string{"noatime", "ro", "size=10k", "atime", "rw", "rprivate", "size=1024k", "slave", "size", "exec"} | |
147 | 201 | _, err = MergeTmpfsOptions(options) |
148 | 202 | if err == nil { |
149 | 203 | t.Fatal("Expected error got nil") |
150 | 204 | } |
151 | 205 | } |
206 | ||
207 | func TestRecursiveUnmountTooGreedy(t *testing.T) { | |
208 | if os.Getuid() != 0 { | |
209 | t.Skip("root required") | |
210 | } | |
211 | ||
212 | tmp, err := ioutil.TempDir("", t.Name()) | |
213 | if err != nil { | |
214 | t.Fatal(err) | |
215 | } | |
216 | defer os.RemoveAll(tmp) | |
217 | ||
218 | // Create a bunch of tmpfs mounts. Make sure "dir" itself is not | |
219 | // a mount point, or we'll hit the fast path in RecursiveUnmount. | |
220 | dirs := []string{"dir-other", "dir/subdir1", "dir/subdir1/subsub", "dir/subdir2/subsub"} | |
221 | for _, d := range dirs { | |
222 | dir := path.Join(tmp, d) | |
223 | if err := os.MkdirAll(dir, 0o700); err != nil { | |
224 | t.Fatal(err) | |
225 | } | |
226 | if err := Mount("tmpfs", dir, "tmpfs", ""); err != nil { | |
227 | t.Fatal(err) | |
228 | } | |
229 | //nolint:errcheck | |
230 | defer Unmount(dir) | |
231 | } | |
232 | // sanity check | |
233 | mounted, err := mountinfo.Mounted(path.Join(tmp, "dir-other")) | |
234 | if err != nil { | |
235 | t.Fatalf("[pre-check] error from mountinfo.mounted: %v", err) | |
236 | } | |
237 | if !mounted { | |
238 | t.Fatal("[pre-check] expected dir-other to be mounted, but it's not") | |
239 | } | |
240 | // Unmount dir, make sure dir-other is still mounted. | |
241 | if err := RecursiveUnmount(path.Join(tmp, "dir")); err != nil { | |
242 | t.Fatal(err) | |
243 | } | |
244 | mounted, err = mountinfo.Mounted(path.Join(tmp, "dir-other")) | |
245 | if err != nil { | |
246 | t.Fatalf("error from mountinfo.mounted: %v", err) | |
247 | } | |
248 | if !mounted { | |
249 | t.Fatal("expected dir-other to be mounted, but it's not") | |
250 | } | |
251 | } |
0 | // +build freebsd,cgo openbsd,cgo | |
1 | ||
2 | package mount | |
3 | ||
4 | /* | |
5 | #include <errno.h> | |
6 | #include <stdlib.h> | |
7 | #include <string.h> | |
8 | #include <sys/_iovec.h> | |
9 | #include <sys/mount.h> | |
10 | #include <sys/param.h> | |
11 | */ | |
12 | import "C" | |
13 | ||
14 | import ( | |
15 | "strings" | |
16 | "syscall" | |
17 | "unsafe" | |
18 | ) | |
19 | ||
20 | func allocateIOVecs(options []string) []C.struct_iovec { | |
21 | out := make([]C.struct_iovec, len(options)) | |
22 | for i, option := range options { | |
23 | out[i].iov_base = unsafe.Pointer(C.CString(option)) | |
24 | out[i].iov_len = C.size_t(len(option) + 1) | |
25 | } | |
26 | return out | |
27 | } | |
28 | ||
29 | func mount(device, target, mType string, flag uintptr, data string) error { | |
30 | isNullFS := false | |
31 | ||
32 | xs := strings.Split(data, ",") | |
33 | for _, x := range xs { | |
34 | if x == "bind" { | |
35 | isNullFS = true | |
36 | } | |
37 | } | |
38 | ||
39 | options := []string{"fspath", target} | |
40 | if isNullFS { | |
41 | options = append(options, "fstype", "nullfs", "target", device) | |
42 | } else { | |
43 | options = append(options, "fstype", mType, "from", device) | |
44 | } | |
45 | rawOptions := allocateIOVecs(options) | |
46 | for _, rawOption := range rawOptions { | |
47 | defer C.free(rawOption.iov_base) | |
48 | } | |
49 | ||
50 | if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 { | |
51 | return &mountError{ | |
52 | op: "mount", | |
53 | source: device, | |
54 | target: target, | |
55 | flags: flag, | |
56 | err: syscall.Errno(errno), | |
57 | } | |
58 | } | |
59 | return nil | |
60 | } |
0 | // +build freebsd,cgo | |
1 | ||
2 | package mount | |
3 | ||
4 | /* | |
5 | #include <errno.h> | |
6 | #include <stdlib.h> | |
7 | #include <string.h> | |
8 | #include <sys/_iovec.h> | |
9 | #include <sys/mount.h> | |
10 | #include <sys/param.h> | |
11 | */ | |
12 | import "C" | |
13 | ||
14 | import ( | |
15 | "strings" | |
16 | "syscall" | |
17 | "unsafe" | |
18 | ) | |
19 | ||
20 | func allocateIOVecs(options []string) []C.struct_iovec { | |
21 | out := make([]C.struct_iovec, len(options)) | |
22 | for i, option := range options { | |
23 | out[i].iov_base = unsafe.Pointer(C.CString(option)) | |
24 | out[i].iov_len = C.size_t(len(option) + 1) | |
25 | } | |
26 | return out | |
27 | } | |
28 | ||
29 | func mount(device, target, mType string, flag uintptr, data string) error { | |
30 | isNullFS := false | |
31 | ||
32 | xs := strings.Split(data, ",") | |
33 | for _, x := range xs { | |
34 | if x == "bind" { | |
35 | isNullFS = true | |
36 | } | |
37 | } | |
38 | ||
39 | options := []string{"fspath", target} | |
40 | if isNullFS { | |
41 | options = append(options, "fstype", "nullfs", "target", device) | |
42 | } else { | |
43 | options = append(options, "fstype", mType, "from", device) | |
44 | } | |
45 | rawOptions := allocateIOVecs(options) | |
46 | for _, rawOption := range rawOptions { | |
47 | defer C.free(rawOption.iov_base) | |
48 | } | |
49 | ||
50 | if errno := C.nmount(&rawOptions[0], C.uint(len(options)), C.int(flag)); errno != 0 { | |
51 | return &mountError{ | |
52 | op: "mount", | |
53 | source: device, | |
54 | target: target, | |
55 | flags: flag, | |
56 | err: syscall.Errno(errno), | |
57 | } | |
58 | } | |
59 | return nil | |
60 | } |
46 | 46 | }{ |
47 | 47 | // No options |
48 | 48 | {"tmpfs", "tmpfs", "", "", "", ""}, |
49 | // tmpfs mount with noexec set | |
50 | {"tmpfs", "tmpfs", "noexec", "noexec", "", ""}, | |
49 | 51 | // Default rw / ro test |
50 | 52 | {source, "", "bind", "", "", ""}, |
51 | 53 | {source, "", "bind,private", "", "", ""}, |
198 | 200 | if mi.VFSOptions != "" { |
199 | 201 | for _, opt := range strings.Split(mi.VFSOptions, ",") { |
200 | 202 | opt = clean(opt) |
201 | if !has(wantedVFS, opt) && opt != "seclabel" { // can be added by selinux | |
203 | if !has(wantedVFS, opt) && | |
204 | opt != "seclabel" && // can be added by selinux | |
205 | opt != "inode64" && opt != "inode32" { // can be added by kernel 5.9+ | |
202 | 206 | t.Errorf("unexpected vfs option %q, expected %q", opt, vfs) |
203 | 207 | } |
204 | 208 | delete(wantedVFS, opt) |
0 | // +build openbsd,cgo | |
1 | ||
2 | /* | |
3 | Due to how OpenBSD mount(2) works, filesystem types need to be | |
4 | supported explicitly since it uses separate structs to pass | |
5 | filesystem-specific arguments. | |
6 | ||
7 | For now only UFS/FFS is supported as it's the default fs | |
8 | on OpenBSD systems. | |
9 | ||
10 | See: https://man.openbsd.org/mount.2 | |
11 | */ | |
12 | ||
13 | package mount | |
14 | ||
15 | /* | |
16 | #include <sys/types.h> | |
17 | #include <sys/mount.h> | |
18 | */ | |
19 | import "C" | |
20 | ||
21 | import ( | |
22 | "fmt" | |
23 | "syscall" | |
24 | "unsafe" | |
25 | ) | |
26 | ||
27 | func createExportInfo(readOnly bool) C.struct_export_args { | |
28 | exportFlags := C.int(0) | |
29 | if readOnly { | |
30 | exportFlags = C.MNT_EXRDONLY | |
31 | } | |
32 | out := C.struct_export_args{ | |
33 | ex_root: 0, | |
34 | ex_flags: exportFlags, | |
35 | } | |
36 | return out | |
37 | } | |
38 | ||
39 | func createUfsArgs(device string, readOnly bool) unsafe.Pointer { | |
40 | out := &C.struct_ufs_args{ | |
41 | fspec: C.CString(device), | |
42 | export_info: createExportInfo(readOnly), | |
43 | } | |
44 | return unsafe.Pointer(out) | |
45 | } | |
46 | ||
47 | func mount(device, target, mType string, flag uintptr, data string) error { | |
48 | readOnly := flag&RDONLY != 0 | |
49 | ||
50 | var fsArgs unsafe.Pointer | |
51 | ||
52 | switch mType { | |
53 | case "ffs": | |
54 | fsArgs = createUfsArgs(device, readOnly) | |
55 | default: | |
56 | return &mountError{ | |
57 | op: "mount", | |
58 | source: device, | |
59 | target: target, | |
60 | flags: flag, | |
61 | err: fmt.Errorf("unsupported file system type: %s", mType), | |
62 | } | |
63 | } | |
64 | ||
65 | if errno := C.mount(C.CString(mType), C.CString(target), C.int(flag), fsArgs); errno != 0 { | |
66 | return &mountError{ | |
67 | op: "mount", | |
68 | source: device, | |
69 | target: target, | |
70 | flags: flag, | |
71 | err: syscall.Errno(errno), | |
72 | } | |
73 | } | |
74 | ||
75 | return nil | |
76 | } |
173 | 173 | } |
174 | 174 | } |
175 | 175 | |
176 | func TestMountedBy(t *testing.T) { | |
176 | func requireOpenat2(t *testing.T) { | |
177 | t.Helper() | |
177 | 178 | if os.Getuid() != 0 { |
178 | 179 | t.Skip("requires root") |
179 | 180 | } |
181 | fd, err := unix.Openat2(unix.AT_FDCWD, ".", &unix.OpenHow{Flags: unix.O_RDONLY}) | |
182 | if err != nil { | |
183 | t.Skipf("openat2: %v (old kernel? need Linux 5.6+)", err) | |
184 | } | |
185 | unix.Close(fd) | |
186 | } | |
187 | ||
188 | func TestMountedBy(t *testing.T) { | |
189 | requireOpenat2(t) | |
180 | 190 | |
181 | 191 | dir, mounts, err := prepareMounts(t) |
182 | 192 | defer cleanupMounts(t, dir, mounts) |
223 | 233 | } |
224 | 234 | |
225 | 235 | func TestMountedByOpenat2VsMountinfo(t *testing.T) { |
226 | fd, err := unix.Openat2(unix.AT_FDCWD, ".", &unix.OpenHow{Flags: unix.O_RDONLY}) | |
227 | if err != nil { | |
228 | t.Skipf("openat2: %v (old kernel? need Linux 5.6+)", err) | |
229 | } | |
230 | unix.Close(fd) | |
236 | requireOpenat2(t) | |
231 | 237 | |
232 | 238 | mounts, err := GetMounts(nil) |
233 | 239 | if err != nil { |
20 | 20 | |
21 | 21 | count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT)) |
22 | 22 | if count == 0 { |
23 | return nil, fmt.Errorf("Failed to call getmntinfo") | |
23 | return nil, fmt.Errorf("failed to call getmntinfo") | |
24 | 24 | } |
25 | 25 | |
26 | 26 | var entries []C.struct_statfs |
13 | 13 | // stop: true if parsing should be stopped after the entry. |
14 | 14 | type FilterFunc func(*Info) (skip, stop bool) |
15 | 15 | |
16 | // PrefixFilter discards all entries whose mount points | |
17 | // do not start with a specific prefix. | |
16 | // PrefixFilter discards all entries whose mount points do not start with, or | |
17 | // are equal to the path specified in prefix. The prefix path must be absolute, | |
18 | // have all symlinks resolved, and cleaned (i.e. no extra slashes or dots). | |
19 | // | |
20 | // PrefixFilter treats prefix as a path, not a partial prefix, which means that | |
21 | // given "/foo", "/foo/bar" and "/foobar" entries, PrefixFilter("/foo") returns | |
22 | // "/foo" and "/foo/bar", and discards "/foobar". | |
18 | 23 | func PrefixFilter(prefix string) FilterFunc { |
19 | 24 | return func(m *Info) (bool, bool) { |
20 | skip := !strings.HasPrefix(m.Mountpoint, prefix) | |
25 | skip := !strings.HasPrefix(m.Mountpoint+"/", prefix+"/") | |
21 | 26 | return skip, false |
22 | 27 | } |
23 | 28 | } |
0 | package mountinfo | |
1 | ||
2 | import "testing" | |
3 | ||
4 | func TestPrefixFilter(t *testing.T) { | |
5 | tests := []struct { | |
6 | prefix string | |
7 | mountPoint string | |
8 | shouldSkip bool | |
9 | }{ | |
10 | {prefix: "/a", mountPoint: "/a", shouldSkip: false}, | |
11 | {prefix: "/a", mountPoint: "/a/b", shouldSkip: false}, | |
12 | {prefix: "/a", mountPoint: "/aa", shouldSkip: true}, | |
13 | {prefix: "/a", mountPoint: "/aa/b", shouldSkip: true}, | |
14 | ||
15 | // invalid prefix: prefix path must be cleaned and have no trailing slash | |
16 | {prefix: "/a/", mountPoint: "/a", shouldSkip: true}, | |
17 | {prefix: "/a/", mountPoint: "/a/b", shouldSkip: true}, | |
18 | } | |
19 | for _, tc := range tests { | |
20 | filter := PrefixFilter(tc.prefix) | |
21 | skip, _ := filter(&Info{Mountpoint: tc.mountPoint}) | |
22 | if skip != tc.shouldSkip { | |
23 | if tc.shouldSkip { | |
24 | t.Errorf("prefix %q: expected %q to be skipped", tc.prefix, tc.mountPoint) | |
25 | } else { | |
26 | t.Errorf("prefix %q: expected %q not to be skipped", tc.prefix, tc.mountPoint) | |
27 | } | |
28 | } | |
29 | } | |
30 | } |
51 | 51 | numFields := len(fields) |
52 | 52 | if numFields < 10 { |
53 | 53 | // should be at least 10 fields |
54 | return nil, fmt.Errorf("Parsing '%s' failed: not enough fields (%d)", text, numFields) | |
54 | return nil, fmt.Errorf("parsing '%s' failed: not enough fields (%d)", text, numFields) | |
55 | 55 | } |
56 | 56 | |
57 | 57 | // separator field |
66 | 66 | for fields[sepIdx] != "-" { |
67 | 67 | sepIdx-- |
68 | 68 | if sepIdx == 5 { |
69 | return nil, fmt.Errorf("Parsing '%s' failed: missing - separator", text) | |
69 | return nil, fmt.Errorf("parsing '%s' failed: missing - separator", text) | |
70 | 70 | } |
71 | 71 | } |
72 | 72 | |
74 | 74 | |
75 | 75 | p.Mountpoint, err = unescape(fields[4]) |
76 | 76 | if err != nil { |
77 | return nil, fmt.Errorf("Parsing '%s' failed: mount point: %w", fields[4], err) | |
77 | return nil, fmt.Errorf("parsing '%s' failed: mount point: %w", fields[4], err) | |
78 | 78 | } |
79 | 79 | p.FSType, err = unescape(fields[sepIdx+1]) |
80 | 80 | if err != nil { |
81 | return nil, fmt.Errorf("Parsing '%s' failed: fstype: %w", fields[sepIdx+1], err) | |
81 | return nil, fmt.Errorf("parsing '%s' failed: fstype: %w", fields[sepIdx+1], err) | |
82 | 82 | } |
83 | 83 | p.Source, err = unescape(fields[sepIdx+2]) |
84 | 84 | if err != nil { |
85 | return nil, fmt.Errorf("Parsing '%s' failed: source: %w", fields[sepIdx+2], err) | |
85 | return nil, fmt.Errorf("parsing '%s' failed: source: %w", fields[sepIdx+2], err) | |
86 | 86 | } |
87 | 87 | p.VFSOptions = fields[sepIdx+3] |
88 | 88 | |
91 | 91 | p.Parent, _ = strconv.Atoi(fields[1]) |
92 | 92 | mm := strings.Split(fields[2], ":") |
93 | 93 | if len(mm) != 2 { |
94 | return nil, fmt.Errorf("Parsing '%s' failed: unexpected minor:major pair %s", text, mm) | |
94 | return nil, fmt.Errorf("parsing '%s' failed: unexpected major:minor pair %s", text, mm) | |
95 | 95 | } |
96 | 96 | p.Major, _ = strconv.Atoi(mm[0]) |
97 | 97 | p.Minor, _ = strconv.Atoi(mm[1]) |
98 | 98 | |
99 | 99 | p.Root, err = unescape(fields[3]) |
100 | 100 | if err != nil { |
101 | return nil, fmt.Errorf("Parsing '%s' failed: root: %w", fields[3], err) | |
101 | return nil, fmt.Errorf("parsing '%s' failed: root: %w", fields[3], err) | |
102 | 102 | } |
103 | 103 | |
104 | 104 | p.Options = fields[5] |
0 | module github.com/moby/sys/signal | |
1 | ||
2 | go 1.13 | |
3 | ||
4 | require golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c |
0 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c h1:F1jZWGFhYfh0Ci55sIpILtKKK8p3i2/krTr0H1rg74I= | |
1 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= |
0 | // Package signal provides helper functions for dealing with signals across | |
1 | // various operating systems. | |
2 | package signal | |
3 | ||
4 | import ( | |
5 | "fmt" | |
6 | "os" | |
7 | "os/signal" | |
8 | "strconv" | |
9 | "strings" | |
10 | "syscall" | |
11 | ) | |
12 | ||
13 | // CatchAll catches all signals and relays them to the specified channel. | |
14 | // SIGURG is not handled, as it's used by the Go runtime to support | |
15 | // preemptable system calls. | |
16 | func CatchAll(sigc chan os.Signal) { | |
17 | var handledSigs []os.Signal | |
18 | for n, s := range SignalMap { | |
19 | if n == "URG" { | |
20 | // Do not handle SIGURG, as in go1.14+, the go runtime issues | |
21 | // SIGURG as an interrupt to support preemptable system calls on Linux. | |
22 | continue | |
23 | } | |
24 | handledSigs = append(handledSigs, s) | |
25 | } | |
26 | signal.Notify(sigc, handledSigs...) | |
27 | } | |
28 | ||
29 | // StopCatch stops catching the signals and closes the specified channel. | |
30 | func StopCatch(sigc chan os.Signal) { | |
31 | signal.Stop(sigc) | |
32 | close(sigc) | |
33 | } | |
34 | ||
35 | // ParseSignal translates a string to a valid syscall signal. | |
36 | // It returns an error if the signal map doesn't include the given signal. | |
37 | func ParseSignal(rawSignal string) (syscall.Signal, error) { | |
38 | s, err := strconv.Atoi(rawSignal) | |
39 | if err == nil { | |
40 | if s == 0 { | |
41 | return -1, fmt.Errorf("invalid signal: %s", rawSignal) | |
42 | } | |
43 | return syscall.Signal(s), nil | |
44 | } | |
45 | signal, ok := SignalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")] | |
46 | if !ok { | |
47 | return -1, fmt.Errorf("invalid signal: %s", rawSignal) | |
48 | } | |
49 | return signal, nil | |
50 | } | |
51 | ||
52 | // ValidSignalForPlatform returns true if a signal is valid on the platform | |
53 | func ValidSignalForPlatform(sig syscall.Signal) bool { | |
54 | for _, v := range SignalMap { | |
55 | if v == sig { | |
56 | return true | |
57 | } | |
58 | } | |
59 | return false | |
60 | } |
0 | package signal | |
1 | ||
2 | import ( | |
3 | "syscall" | |
4 | ) | |
5 | ||
6 | // SignalMap is a map of Darwin signals. | |
7 | var SignalMap = map[string]syscall.Signal{ | |
8 | "ABRT": syscall.SIGABRT, | |
9 | "ALRM": syscall.SIGALRM, | |
10 | "BUS": syscall.SIGBUS, | |
11 | "CHLD": syscall.SIGCHLD, | |
12 | "CONT": syscall.SIGCONT, | |
13 | "EMT": syscall.SIGEMT, | |
14 | "FPE": syscall.SIGFPE, | |
15 | "HUP": syscall.SIGHUP, | |
16 | "ILL": syscall.SIGILL, | |
17 | "INFO": syscall.SIGINFO, | |
18 | "INT": syscall.SIGINT, | |
19 | "IO": syscall.SIGIO, | |
20 | "IOT": syscall.SIGIOT, | |
21 | "KILL": syscall.SIGKILL, | |
22 | "PIPE": syscall.SIGPIPE, | |
23 | "PROF": syscall.SIGPROF, | |
24 | "QUIT": syscall.SIGQUIT, | |
25 | "SEGV": syscall.SIGSEGV, | |
26 | "STOP": syscall.SIGSTOP, | |
27 | "SYS": syscall.SIGSYS, | |
28 | "TERM": syscall.SIGTERM, | |
29 | "TRAP": syscall.SIGTRAP, | |
30 | "TSTP": syscall.SIGTSTP, | |
31 | "TTIN": syscall.SIGTTIN, | |
32 | "TTOU": syscall.SIGTTOU, | |
33 | "URG": syscall.SIGURG, | |
34 | "USR1": syscall.SIGUSR1, | |
35 | "USR2": syscall.SIGUSR2, | |
36 | "VTALRM": syscall.SIGVTALRM, | |
37 | "WINCH": syscall.SIGWINCH, | |
38 | "XCPU": syscall.SIGXCPU, | |
39 | "XFSZ": syscall.SIGXFSZ, | |
40 | } |
0 | package signal | |
1 | ||
2 | import ( | |
3 | "syscall" | |
4 | ) | |
5 | ||
6 | // SignalMap is a map of FreeBSD signals. | |
7 | var SignalMap = map[string]syscall.Signal{ | |
8 | "ABRT": syscall.SIGABRT, | |
9 | "ALRM": syscall.SIGALRM, | |
10 | "BUS": syscall.SIGBUS, | |
11 | "CHLD": syscall.SIGCHLD, | |
12 | "CONT": syscall.SIGCONT, | |
13 | "EMT": syscall.SIGEMT, | |
14 | "FPE": syscall.SIGFPE, | |
15 | "HUP": syscall.SIGHUP, | |
16 | "ILL": syscall.SIGILL, | |
17 | "INFO": syscall.SIGINFO, | |
18 | "INT": syscall.SIGINT, | |
19 | "IO": syscall.SIGIO, | |
20 | "IOT": syscall.SIGIOT, | |
21 | "KILL": syscall.SIGKILL, | |
22 | "LWP": syscall.SIGLWP, | |
23 | "PIPE": syscall.SIGPIPE, | |
24 | "PROF": syscall.SIGPROF, | |
25 | "QUIT": syscall.SIGQUIT, | |
26 | "SEGV": syscall.SIGSEGV, | |
27 | "STOP": syscall.SIGSTOP, | |
28 | "SYS": syscall.SIGSYS, | |
29 | "TERM": syscall.SIGTERM, | |
30 | "THR": syscall.SIGTHR, | |
31 | "TRAP": syscall.SIGTRAP, | |
32 | "TSTP": syscall.SIGTSTP, | |
33 | "TTIN": syscall.SIGTTIN, | |
34 | "TTOU": syscall.SIGTTOU, | |
35 | "URG": syscall.SIGURG, | |
36 | "USR1": syscall.SIGUSR1, | |
37 | "USR2": syscall.SIGUSR2, | |
38 | "VTALRM": syscall.SIGVTALRM, | |
39 | "WINCH": syscall.SIGWINCH, | |
40 | "XCPU": syscall.SIGXCPU, | |
41 | "XFSZ": syscall.SIGXFSZ, | |
42 | } |
0 | // +build !mips,!mipsle,!mips64,!mips64le | |
1 | ||
2 | package signal | |
3 | ||
4 | import ( | |
5 | "syscall" | |
6 | ||
7 | "golang.org/x/sys/unix" | |
8 | ) | |
9 | ||
10 | const ( | |
11 | sigrtmin = 34 | |
12 | sigrtmax = 64 | |
13 | ) | |
14 | ||
15 | // SignalMap is a map of Linux signals. | |
16 | var SignalMap = map[string]syscall.Signal{ | |
17 | "ABRT": unix.SIGABRT, | |
18 | "ALRM": unix.SIGALRM, | |
19 | "BUS": unix.SIGBUS, | |
20 | "CHLD": unix.SIGCHLD, | |
21 | "CLD": unix.SIGCLD, | |
22 | "CONT": unix.SIGCONT, | |
23 | "FPE": unix.SIGFPE, | |
24 | "HUP": unix.SIGHUP, | |
25 | "ILL": unix.SIGILL, | |
26 | "INT": unix.SIGINT, | |
27 | "IO": unix.SIGIO, | |
28 | "IOT": unix.SIGIOT, | |
29 | "KILL": unix.SIGKILL, | |
30 | "PIPE": unix.SIGPIPE, | |
31 | "POLL": unix.SIGPOLL, | |
32 | "PROF": unix.SIGPROF, | |
33 | "PWR": unix.SIGPWR, | |
34 | "QUIT": unix.SIGQUIT, | |
35 | "SEGV": unix.SIGSEGV, | |
36 | "STKFLT": unix.SIGSTKFLT, | |
37 | "STOP": unix.SIGSTOP, | |
38 | "SYS": unix.SIGSYS, | |
39 | "TERM": unix.SIGTERM, | |
40 | "TRAP": unix.SIGTRAP, | |
41 | "TSTP": unix.SIGTSTP, | |
42 | "TTIN": unix.SIGTTIN, | |
43 | "TTOU": unix.SIGTTOU, | |
44 | "URG": unix.SIGURG, | |
45 | "USR1": unix.SIGUSR1, | |
46 | "USR2": unix.SIGUSR2, | |
47 | "VTALRM": unix.SIGVTALRM, | |
48 | "WINCH": unix.SIGWINCH, | |
49 | "XCPU": unix.SIGXCPU, | |
50 | "XFSZ": unix.SIGXFSZ, | |
51 | "RTMIN": sigrtmin, | |
52 | "RTMIN+1": sigrtmin + 1, | |
53 | "RTMIN+2": sigrtmin + 2, | |
54 | "RTMIN+3": sigrtmin + 3, | |
55 | "RTMIN+4": sigrtmin + 4, | |
56 | "RTMIN+5": sigrtmin + 5, | |
57 | "RTMIN+6": sigrtmin + 6, | |
58 | "RTMIN+7": sigrtmin + 7, | |
59 | "RTMIN+8": sigrtmin + 8, | |
60 | "RTMIN+9": sigrtmin + 9, | |
61 | "RTMIN+10": sigrtmin + 10, | |
62 | "RTMIN+11": sigrtmin + 11, | |
63 | "RTMIN+12": sigrtmin + 12, | |
64 | "RTMIN+13": sigrtmin + 13, | |
65 | "RTMIN+14": sigrtmin + 14, | |
66 | "RTMIN+15": sigrtmin + 15, | |
67 | "RTMAX-14": sigrtmax - 14, | |
68 | "RTMAX-13": sigrtmax - 13, | |
69 | "RTMAX-12": sigrtmax - 12, | |
70 | "RTMAX-11": sigrtmax - 11, | |
71 | "RTMAX-10": sigrtmax - 10, | |
72 | "RTMAX-9": sigrtmax - 9, | |
73 | "RTMAX-8": sigrtmax - 8, | |
74 | "RTMAX-7": sigrtmax - 7, | |
75 | "RTMAX-6": sigrtmax - 6, | |
76 | "RTMAX-5": sigrtmax - 5, | |
77 | "RTMAX-4": sigrtmax - 4, | |
78 | "RTMAX-3": sigrtmax - 3, | |
79 | "RTMAX-2": sigrtmax - 2, | |
80 | "RTMAX-1": sigrtmax - 1, | |
81 | "RTMAX": sigrtmax, | |
82 | } |
0 | // +build linux | |
1 | // +build mips mipsle mips64 mips64le | |
2 | ||
3 | package signal | |
4 | ||
5 | import ( | |
6 | "syscall" | |
7 | ||
8 | "golang.org/x/sys/unix" | |
9 | ) | |
10 | ||
11 | const ( | |
12 | sigrtmin = 34 | |
13 | sigrtmax = 127 | |
14 | ) | |
15 | ||
16 | // SignalMap is a map of Linux signals. | |
17 | var SignalMap = map[string]syscall.Signal{ | |
18 | "ABRT": unix.SIGABRT, | |
19 | "ALRM": unix.SIGALRM, | |
20 | "BUS": unix.SIGBUS, | |
21 | "CHLD": unix.SIGCHLD, | |
22 | "CLD": unix.SIGCLD, | |
23 | "CONT": unix.SIGCONT, | |
24 | "FPE": unix.SIGFPE, | |
25 | "HUP": unix.SIGHUP, | |
26 | "ILL": unix.SIGILL, | |
27 | "INT": unix.SIGINT, | |
28 | "IO": unix.SIGIO, | |
29 | "IOT": unix.SIGIOT, | |
30 | "KILL": unix.SIGKILL, | |
31 | "PIPE": unix.SIGPIPE, | |
32 | "POLL": unix.SIGPOLL, | |
33 | "PROF": unix.SIGPROF, | |
34 | "PWR": unix.SIGPWR, | |
35 | "QUIT": unix.SIGQUIT, | |
36 | "SEGV": unix.SIGSEGV, | |
37 | "EMT": unix.SIGEMT, | |
38 | "STOP": unix.SIGSTOP, | |
39 | "SYS": unix.SIGSYS, | |
40 | "TERM": unix.SIGTERM, | |
41 | "TRAP": unix.SIGTRAP, | |
42 | "TSTP": unix.SIGTSTP, | |
43 | "TTIN": unix.SIGTTIN, | |
44 | "TTOU": unix.SIGTTOU, | |
45 | "URG": unix.SIGURG, | |
46 | "USR1": unix.SIGUSR1, | |
47 | "USR2": unix.SIGUSR2, | |
48 | "VTALRM": unix.SIGVTALRM, | |
49 | "WINCH": unix.SIGWINCH, | |
50 | "XCPU": unix.SIGXCPU, | |
51 | "XFSZ": unix.SIGXFSZ, | |
52 | "RTMIN": sigrtmin, | |
53 | "RTMIN+1": sigrtmin + 1, | |
54 | "RTMIN+2": sigrtmin + 2, | |
55 | "RTMIN+3": sigrtmin + 3, | |
56 | "RTMIN+4": sigrtmin + 4, | |
57 | "RTMIN+5": sigrtmin + 5, | |
58 | "RTMIN+6": sigrtmin + 6, | |
59 | "RTMIN+7": sigrtmin + 7, | |
60 | "RTMIN+8": sigrtmin + 8, | |
61 | "RTMIN+9": sigrtmin + 9, | |
62 | "RTMIN+10": sigrtmin + 10, | |
63 | "RTMIN+11": sigrtmin + 11, | |
64 | "RTMIN+12": sigrtmin + 12, | |
65 | "RTMIN+13": sigrtmin + 13, | |
66 | "RTMIN+14": sigrtmin + 14, | |
67 | "RTMIN+15": sigrtmin + 15, | |
68 | "RTMAX-14": sigrtmax - 14, | |
69 | "RTMAX-13": sigrtmax - 13, | |
70 | "RTMAX-12": sigrtmax - 12, | |
71 | "RTMAX-11": sigrtmax - 11, | |
72 | "RTMAX-10": sigrtmax - 10, | |
73 | "RTMAX-9": sigrtmax - 9, | |
74 | "RTMAX-8": sigrtmax - 8, | |
75 | "RTMAX-7": sigrtmax - 7, | |
76 | "RTMAX-6": sigrtmax - 6, | |
77 | "RTMAX-5": sigrtmax - 5, | |
78 | "RTMAX-4": sigrtmax - 4, | |
79 | "RTMAX-3": sigrtmax - 3, | |
80 | "RTMAX-2": sigrtmax - 2, | |
81 | "RTMAX-1": sigrtmax - 1, | |
82 | "RTMAX": sigrtmax, | |
83 | } |
0 | // +build darwin linux | |
1 | ||
2 | package signal | |
3 | ||
4 | import ( | |
5 | "os" | |
6 | "syscall" | |
7 | "testing" | |
8 | "time" | |
9 | ) | |
10 | ||
11 | func TestCatchAll(t *testing.T) { | |
12 | sigs := make(chan os.Signal, 1) | |
13 | CatchAll(sigs) | |
14 | defer StopCatch(sigs) | |
15 | ||
16 | listOfSignals := map[string]string{ | |
17 | "CONT": syscall.SIGCONT.String(), | |
18 | "HUP": syscall.SIGHUP.String(), | |
19 | "CHLD": syscall.SIGCHLD.String(), | |
20 | "ILL": syscall.SIGILL.String(), | |
21 | "FPE": syscall.SIGFPE.String(), | |
22 | "CLD": syscall.SIGCLD.String(), | |
23 | } | |
24 | ||
25 | for sigStr := range listOfSignals { | |
26 | if signal, ok := SignalMap[sigStr]; ok { | |
27 | _ = syscall.Kill(syscall.Getpid(), signal) | |
28 | s := <-sigs | |
29 | if s.String() != signal.String() { | |
30 | t.Errorf("expected: %q, got: %q", signal, s) | |
31 | } | |
32 | } | |
33 | } | |
34 | } | |
35 | ||
36 | func TestCatchAllIgnoreSigUrg(t *testing.T) { | |
37 | sigs := make(chan os.Signal, 1) | |
38 | CatchAll(sigs) | |
39 | defer StopCatch(sigs) | |
40 | ||
41 | err := syscall.Kill(syscall.Getpid(), syscall.SIGURG) | |
42 | if err != nil { | |
43 | t.Fatal(err) | |
44 | } | |
45 | timer := time.NewTimer(1 * time.Second) | |
46 | defer timer.Stop() | |
47 | select { | |
48 | case <-timer.C: | |
49 | case s := <-sigs: | |
50 | t.Fatalf("expected no signals to be handled, but received %q", s.String()) | |
51 | } | |
52 | } | |
53 | ||
54 | func TestStopCatch(t *testing.T) { | |
55 | signal := SignalMap["HUP"] | |
56 | channel := make(chan os.Signal, 1) | |
57 | CatchAll(channel) | |
58 | _ = syscall.Kill(syscall.Getpid(), signal) | |
59 | signalString := <-channel | |
60 | if signalString.String() != signal.String() { | |
61 | t.Errorf("expected: %q, got: %q", signal, signalString) | |
62 | } | |
63 | ||
64 | StopCatch(channel) | |
65 | _, ok := <-channel | |
66 | if ok { | |
67 | t.Error("expected: !ok, got: ok") | |
68 | } | |
69 | } |
0 | package signal | |
1 | ||
2 | import ( | |
3 | "syscall" | |
4 | "testing" | |
5 | ) | |
6 | ||
7 | func TestParseSignal(t *testing.T) { | |
8 | _, err := ParseSignal("0") | |
9 | expectedErr := "invalid signal: 0" | |
10 | if err == nil || err.Error() != expectedErr { | |
11 | t.Errorf("expected %q, but got %v", expectedErr, err) | |
12 | } | |
13 | ||
14 | _, err = ParseSignal("SIG") | |
15 | expectedErr = "invalid signal: SIG" | |
16 | if err == nil || err.Error() != expectedErr { | |
17 | t.Errorf("expected %q, but got %v", expectedErr, err) | |
18 | } | |
19 | ||
20 | for sigStr := range SignalMap { | |
21 | responseSignal, err := ParseSignal(sigStr) | |
22 | if err != nil { | |
23 | t.Error(err) | |
24 | } | |
25 | signal := SignalMap[sigStr] | |
26 | if responseSignal != signal { | |
27 | t.Errorf("expected: %q, got: %q", signal, responseSignal) | |
28 | } | |
29 | } | |
30 | } | |
31 | ||
32 | func TestValidSignalForPlatform(t *testing.T) { | |
33 | isValidSignal := ValidSignalForPlatform(syscall.Signal(0)) | |
34 | if isValidSignal { | |
35 | t.Error("expected !isValidSignal") | |
36 | } | |
37 | ||
38 | for _, sigN := range SignalMap { | |
39 | isValidSignal = ValidSignalForPlatform(sigN) | |
40 | if !isValidSignal { | |
41 | t.Error("expected isValidSignal") | |
42 | } | |
43 | } | |
44 | } |
0 | // +build !windows | |
1 | ||
2 | package signal | |
3 | ||
4 | import ( | |
5 | "syscall" | |
6 | ) | |
7 | ||
8 | // Signals used in cli/command (no windows equivalent, use | |
9 | // invalid signals so they don't get handled) | |
10 | ||
11 | const ( | |
12 | // SIGCHLD is a signal sent to a process when a child process terminates, is interrupted, or resumes after being interrupted. | |
13 | SIGCHLD = syscall.SIGCHLD | |
14 | // SIGWINCH is a signal sent to a process when its controlling terminal changes its size | |
15 | SIGWINCH = syscall.SIGWINCH | |
16 | // SIGPIPE is a signal sent to a process when a pipe is written to before the other end is open for reading | |
17 | SIGPIPE = syscall.SIGPIPE | |
18 | ) |
0 | // +build !linux,!darwin,!freebsd,!windows | |
1 | ||
2 | package signal | |
3 | ||
4 | import ( | |
5 | "syscall" | |
6 | ) | |
7 | ||
8 | // SignalMap is an empty map of signals for unsupported platform. | |
9 | var SignalMap = map[string]syscall.Signal{} |
0 | package signal | |
1 | ||
2 | import ( | |
3 | "syscall" | |
4 | ||
5 | "golang.org/x/sys/windows" | |
6 | ) | |
7 | ||
8 | // Signals used in cli/command (no windows equivalent, use | |
9 | // invalid signals so they don't get handled) | |
10 | const ( | |
11 | SIGCHLD = syscall.Signal(0xff) | |
12 | SIGWINCH = syscall.Signal(0xff) | |
13 | SIGPIPE = syscall.Signal(0xff) | |
14 | ) | |
15 | ||
16 | // SignalMap is a map of "supported" signals. As per the comment in GOLang's | |
17 | // ztypes_windows.go: "More invented values for signals". Windows doesn't | |
18 | // really support signals in any way, shape or form that Unix does. | |
19 | var SignalMap = map[string]syscall.Signal{ | |
20 | "ABRT": syscall.Signal(windows.SIGABRT), | |
21 | "ALRM": syscall.Signal(windows.SIGALRM), | |
22 | "BUS": syscall.Signal(windows.SIGBUS), | |
23 | "FPE": syscall.Signal(windows.SIGFPE), | |
24 | "HUP": syscall.Signal(windows.SIGHUP), | |
25 | "ILL": syscall.Signal(windows.SIGILL), | |
26 | "INT": syscall.Signal(windows.SIGINT), | |
27 | "KILL": syscall.Signal(windows.SIGKILL), | |
28 | "PIPE": syscall.Signal(windows.SIGPIPE), | |
29 | "QUIT": syscall.Signal(windows.SIGQUIT), | |
30 | "SEGV": syscall.Signal(windows.SIGSEGV), | |
31 | "TERM": syscall.Signal(windows.SIGTERM), | |
32 | "TRAP": syscall.Signal(windows.SIGTRAP), | |
33 | } |
46 | 46 | return err |
47 | 47 | } |
48 | 48 | if expected != rewrite { |
49 | return fmt.Errorf("Expected %q got %q", expected, rewrite) | |
49 | return fmt.Errorf("expected %q got %q", expected, rewrite) | |
50 | 50 | } |
51 | 51 | return nil |
52 | 52 | } |
53 | 53 | |
54 | 54 | func TestFollowSymlinkAbsolute(t *testing.T) { |
55 | tmpdir, cleanup := mkTempDir(t) | |
56 | defer cleanup() | |
55 | tmpdir := mkTempDir(t) | |
56 | ||
57 | 57 | if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/d", target: "/b"}}); err != nil { |
58 | 58 | t.Fatal(err) |
59 | 59 | } |
63 | 63 | } |
64 | 64 | |
65 | 65 | func TestFollowSymlinkRelativePath(t *testing.T) { |
66 | tmpdir, cleanup := mkTempDir(t) | |
67 | defer cleanup() | |
66 | tmpdir := mkTempDir(t) | |
68 | 67 | |
69 | 68 | if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/i", target: "a"}}); err != nil { |
70 | 69 | t.Fatal(err) |
75 | 74 | } |
76 | 75 | |
77 | 76 | func TestFollowSymlinkSkipSymlinksOutsideScope(t *testing.T) { |
78 | tmpdir, cleanup := mkTempDir(t) | |
79 | defer cleanup() | |
77 | tmpdir := mkTempDir(t) | |
80 | 78 | |
81 | 79 | if err := makeFs(tmpdir, []dirOrLink{ |
82 | 80 | {path: "linkdir", target: "realdir"}, |
96 | 94 | } |
97 | 95 | |
98 | 96 | func TestFollowSymlinkLastLink(t *testing.T) { |
99 | tmpdir, cleanup := mkTempDir(t) | |
100 | defer cleanup() | |
97 | tmpdir := mkTempDir(t) | |
101 | 98 | |
102 | 99 | if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/d", target: "/b"}}); err != nil { |
103 | 100 | t.Fatal(err) |
108 | 105 | } |
109 | 106 | |
110 | 107 | func TestFollowSymlinkRelativeLinkChangeScope(t *testing.T) { |
111 | tmpdir, cleanup := mkTempDir(t) | |
112 | defer cleanup() | |
108 | tmpdir := mkTempDir(t) | |
113 | 109 | |
114 | 110 | if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/e", target: "../b"}}); err != nil { |
115 | 111 | t.Fatal(err) |
125 | 121 | } |
126 | 122 | |
127 | 123 | func TestFollowSymlinkDeepRelativeLinkChangeScope(t *testing.T) { |
128 | tmpdir, cleanup := mkTempDir(t) | |
129 | defer cleanup() | |
124 | tmpdir := mkTempDir(t) | |
130 | 125 | |
131 | 126 | if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/f", target: "../../../../test"}}); err != nil { |
132 | 127 | t.Fatal(err) |
146 | 141 | } |
147 | 142 | |
148 | 143 | func TestFollowSymlinkRelativeLinkChain(t *testing.T) { |
149 | tmpdir, cleanup := mkTempDir(t) | |
150 | defer cleanup() | |
144 | tmpdir := mkTempDir(t) | |
151 | 145 | |
152 | 146 | // avoid letting symlink g (pointed at by symlink h) take out of scope |
153 | 147 | // TODO: we should probably normalize to scope here because ../[....]/root |
164 | 158 | } |
165 | 159 | |
166 | 160 | func TestFollowSymlinkBreakoutPath(t *testing.T) { |
167 | tmpdir, cleanup := mkTempDir(t) | |
168 | defer cleanup() | |
161 | tmpdir := mkTempDir(t) | |
169 | 162 | |
170 | 163 | // avoid letting symlink -> ../directory/file escape from scope |
171 | 164 | // normalize to "testdata/fs/j" |
178 | 171 | } |
179 | 172 | |
180 | 173 | func TestFollowSymlinkToRoot(t *testing.T) { |
181 | tmpdir, cleanup := mkTempDir(t) | |
182 | defer cleanup() | |
174 | tmpdir := mkTempDir(t) | |
183 | 175 | |
184 | 176 | // make sure we don't allow escaping to / |
185 | 177 | // normalize to dir |
192 | 184 | } |
193 | 185 | |
194 | 186 | func TestFollowSymlinkSlashDotdot(t *testing.T) { |
195 | tmpdir, cleanup := mkTempDir(t) | |
196 | defer cleanup() | |
187 | tmpdir := mkTempDir(t) | |
197 | 188 | tmpdir = filepath.Join(tmpdir, "dir", "subdir") |
198 | 189 | |
199 | 190 | // make sure we don't allow escaping to / |
207 | 198 | } |
208 | 199 | |
209 | 200 | func TestFollowSymlinkDotdot(t *testing.T) { |
210 | tmpdir, cleanup := mkTempDir(t) | |
211 | defer cleanup() | |
201 | tmpdir := mkTempDir(t) | |
212 | 202 | tmpdir = filepath.Join(tmpdir, "dir", "subdir") |
213 | 203 | |
214 | 204 | // make sure we stay in scope without leaking information |
223 | 213 | } |
224 | 214 | |
225 | 215 | func TestFollowSymlinkRelativePath2(t *testing.T) { |
226 | tmpdir, cleanup := mkTempDir(t) | |
227 | defer cleanup() | |
216 | tmpdir := mkTempDir(t) | |
228 | 217 | |
229 | 218 | if err := makeFs(tmpdir, []dirOrLink{{path: "bar/foo", target: "baz/target"}}); err != nil { |
230 | 219 | t.Fatal(err) |
235 | 224 | } |
236 | 225 | |
237 | 226 | func TestFollowSymlinkScopeLink(t *testing.T) { |
238 | tmpdir, cleanup := mkTempDir(t) | |
239 | defer cleanup() | |
227 | tmpdir := mkTempDir(t) | |
240 | 228 | |
241 | 229 | if err := makeFs(tmpdir, []dirOrLink{ |
242 | 230 | {path: "root2"}, |
251 | 239 | } |
252 | 240 | |
253 | 241 | func TestFollowSymlinkRootScope(t *testing.T) { |
254 | tmpdir, cleanup := mkTempDir(t) | |
255 | defer cleanup() | |
242 | tmpdir := mkTempDir(t) | |
256 | 243 | |
257 | 244 | expected, err := filepath.EvalSymlinks(tmpdir) |
258 | 245 | if err != nil { |
282 | 269 | } |
283 | 270 | |
284 | 271 | func TestFollowSymlinkCircular(t *testing.T) { |
285 | tmpdir, cleanup := mkTempDir(t) | |
286 | defer cleanup() | |
272 | tmpdir := mkTempDir(t) | |
287 | 273 | |
288 | 274 | if err := makeFs(tmpdir, []dirOrLink{{path: "root/foo", target: "foo"}}); err != nil { |
289 | 275 | t.Fatal(err) |
305 | 291 | } |
306 | 292 | |
307 | 293 | func TestFollowSymlinkComplexChainWithTargetPathsContainingLinks(t *testing.T) { |
308 | tmpdir, cleanup := mkTempDir(t) | |
309 | defer cleanup() | |
294 | tmpdir := mkTempDir(t) | |
310 | 295 | |
311 | 296 | if err := makeFs(tmpdir, []dirOrLink{ |
312 | 297 | {path: "root2"}, |
326 | 311 | } |
327 | 312 | |
328 | 313 | func TestFollowSymlinkBreakoutNonExistent(t *testing.T) { |
329 | tmpdir, cleanup := mkTempDir(t) | |
330 | defer cleanup() | |
314 | tmpdir := mkTempDir(t) | |
331 | 315 | |
332 | 316 | if err := makeFs(tmpdir, []dirOrLink{ |
333 | 317 | {path: "root/slash", target: "/"}, |
341 | 325 | } |
342 | 326 | |
343 | 327 | func TestFollowSymlinkNoLexicalCleaning(t *testing.T) { |
344 | tmpdir, cleanup := mkTempDir(t) | |
345 | defer cleanup() | |
328 | tmpdir := mkTempDir(t) | |
346 | 329 | |
347 | 330 | if err := makeFs(tmpdir, []dirOrLink{ |
348 | 331 | {path: "root/sym", target: "/foo/bar"}, |
355 | 338 | } |
356 | 339 | } |
357 | 340 | |
358 | func mkTempDir(t *testing.T) (string, func()) { | |
341 | // TODO use testing.TempDir() instead (https://golang.org/pkg/testing/#T.TempDir) | |
342 | // once we no longer test on Go 1.14 and older. | |
343 | func mkTempDir(t *testing.T) string { | |
359 | 344 | t.Helper() |
360 | 345 | tmpdir, err := ioutil.TempDir("", t.Name()) |
361 | 346 | if err != nil { |
362 | 347 | t.Fatal(err) |
363 | 348 | } |
364 | return tmpdir, func() { _ = os.RemoveAll(tmpdir) } | |
365 | } | |
349 | t.Cleanup(func() { _ = os.RemoveAll(tmpdir) }) | |
350 | return tmpdir | |
351 | } |
88 | 88 | var b bytes.Buffer |
89 | 89 | for n := 0; path != ""; n++ { |
90 | 90 | if n > maxIter { |
91 | return "", errors.New("EvalSymlinks: too many links in " + originalPath) | |
91 | return "", errors.New("too many links in " + originalPath) | |
92 | 92 | } |
93 | 93 | |
94 | 94 | // A path beginning with `\\?\` represents the root, so automatically |