Codebase list golang-github-moby-sys / 7775bbc5-5eac-4c3b-8817-1cc4200354ae/upstream
Import upstream version 0.0~git20210813.f3885c8 Debian Janitor 2 years ago
34 changed file(s) with 899 addition(s) and 207 deletion(s). Raw diff Collapse all Expand all
+0
-31
.ci/Vagrantfile.fedora32 less more
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
-20
.ci/install-vagrant.sh less more
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
33 test:
44 strategy:
55 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]
88 runs-on: ${{ matrix.platform }}
99 steps:
1010 - name: Install Go
11 uses: actions/setup-go@v1
11 uses: actions/setup-go@v2
1212 with:
1313 go-version: ${{ matrix.go-version }}
1414 - name: Checkout code
1515 uses: actions/checkout@v2
16 - name: Test
17 run: make test
1816 - name: Lint
1917 run: make lint
2018 - name: Cross build
2119 if: ${{ runner.os == 'Linux' }}
2220 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
-15
.travis.yml less more
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'
00 .SHELLFLAGS = -ec
1 PACKAGES ?= mountinfo mount symlink
1 PACKAGES ?= mountinfo mount signal symlink
22 BINDIR ?= _build/bin
33 CROSS ?= linux/arm linux/arm64 linux/ppc64le linux/s390x \
44 freebsd/amd64 openbsd/amd64 darwin/amd64 darwin/arm64 windows/amd64
5 SUDO ?= sudo -n
56
67 .PHONY: all
78 all: lint test cross
89
910 .PHONY: test
11 test: RUN_VIA_SUDO = $(shell $(SUDO) true && echo -exec \"$(SUDO)\")
1012 test:
1113 for p in $(PACKAGES); do \
12 (cd $$p && go test -v .); \
14 (cd $$p && go test $(RUN_VIA_SUDO) -v .); \
1315 done
1416
1517 .PHONY: lint
2123 done
2224
2325 $(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
2527
2628 $(BINDIR):
2729 mkdir -p $(BINDIR)
100100 }
101101 opt := strings.SplitN(option, "=", 2)
102102 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)
104104 }
105105 if !dataCollisions[opt[0]] {
106106 // We prepend the option and add to collision map
22 go 1.14
33
44 require (
5 github.com/moby/sys/mountinfo v0.4.0
5 github.com/moby/sys/mountinfo v0.4.1
66 golang.org/x/sys v0.0.0-20200922070232-aee5d888a860
77 )
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=
22 golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
33 golang.org/x/sys v0.0.0-20200922070232-aee5d888a860 h1:YEu4SMq7D0cmT7CBbXfcH0NZeuChAXwsHe/9XueUO6o=
44 golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
55 "io/ioutil"
66 "os"
77 "path"
8 "strings"
89 "testing"
910
1011 "github.com/moby/sys/mountinfo"
1112 )
1213
1314 func TestMountOptionsParsing(t *testing.T) {
14 options := "noatime,ro,size=10k"
15 options := "noatime,ro,noexec,size=10k"
1516
1617 flag, data := parseOptions(options)
1718
1920 t.Fatalf("Expected size=10 got %s", data)
2021 }
2122
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)
2627 }
2728 }
2829
8081 }
8182 }
8283
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
83137 func TestMountReadonly(t *testing.T) {
84138 if os.Getuid() != 0 {
85139 t.Skip("root required")
128182 }
129183
130184 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"}
133187 merged, err := MergeTmpfsOptions(options)
134188 if err != nil {
135189 t.Fatal(err)
143197 }
144198 }
145199
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"}
147201 _, err = MergeTmpfsOptions(options)
148202 if err == nil {
149203 t.Fatal("Expected error got nil")
150204 }
151205 }
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
-61
mount/mounter_bsd.go less more
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 }
4646 }{
4747 // No options
4848 {"tmpfs", "tmpfs", "", "", "", ""},
49 // tmpfs mount with noexec set
50 {"tmpfs", "tmpfs", "noexec", "noexec", "", ""},
4951 // Default rw / ro test
5052 {source, "", "bind", "", "", ""},
5153 {source, "", "bind,private", "", "", ""},
198200 if mi.VFSOptions != "" {
199201 for _, opt := range strings.Split(mi.VFSOptions, ",") {
200202 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+
202206 t.Errorf("unexpected vfs option %q, expected %q", opt, vfs)
203207 }
204208 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 }
173173 }
174174 }
175175
176 func TestMountedBy(t *testing.T) {
176 func requireOpenat2(t *testing.T) {
177 t.Helper()
177178 if os.Getuid() != 0 {
178179 t.Skip("requires root")
179180 }
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)
180190
181191 dir, mounts, err := prepareMounts(t)
182192 defer cleanupMounts(t, dir, mounts)
223233 }
224234
225235 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)
231237
232238 mounts, err := GetMounts(nil)
233239 if err != nil {
2020
2121 count := int(C.getmntinfo(&rawEntries, C.MNT_WAIT))
2222 if count == 0 {
23 return nil, fmt.Errorf("Failed to call getmntinfo")
23 return nil, fmt.Errorf("failed to call getmntinfo")
2424 }
2525
2626 var entries []C.struct_statfs
1313 // stop: true if parsing should be stopped after the entry.
1414 type FilterFunc func(*Info) (skip, stop bool)
1515
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".
1823 func PrefixFilter(prefix string) FilterFunc {
1924 return func(m *Info) (bool, bool) {
20 skip := !strings.HasPrefix(m.Mountpoint, prefix)
25 skip := !strings.HasPrefix(m.Mountpoint+"/", prefix+"/")
2126 return skip, false
2227 }
2328 }
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 }
5151 numFields := len(fields)
5252 if numFields < 10 {
5353 // 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)
5555 }
5656
5757 // separator field
6666 for fields[sepIdx] != "-" {
6767 sepIdx--
6868 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)
7070 }
7171 }
7272
7474
7575 p.Mountpoint, err = unescape(fields[4])
7676 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)
7878 }
7979 p.FSType, err = unescape(fields[sepIdx+1])
8080 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)
8282 }
8383 p.Source, err = unescape(fields[sepIdx+2])
8484 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)
8686 }
8787 p.VFSOptions = fields[sepIdx+3]
8888
9191 p.Parent, _ = strconv.Atoi(fields[1])
9292 mm := strings.Split(fields[2], ":")
9393 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)
9595 }
9696 p.Major, _ = strconv.Atoi(mm[0])
9797 p.Minor, _ = strconv.Atoi(mm[1])
9898
9999 p.Root, err = unescape(fields[3])
100100 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)
102102 }
103103
104104 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 }
4646 return err
4747 }
4848 if expected != rewrite {
49 return fmt.Errorf("Expected %q got %q", expected, rewrite)
49 return fmt.Errorf("expected %q got %q", expected, rewrite)
5050 }
5151 return nil
5252 }
5353
5454 func TestFollowSymlinkAbsolute(t *testing.T) {
55 tmpdir, cleanup := mkTempDir(t)
56 defer cleanup()
55 tmpdir := mkTempDir(t)
56
5757 if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/d", target: "/b"}}); err != nil {
5858 t.Fatal(err)
5959 }
6363 }
6464
6565 func TestFollowSymlinkRelativePath(t *testing.T) {
66 tmpdir, cleanup := mkTempDir(t)
67 defer cleanup()
66 tmpdir := mkTempDir(t)
6867
6968 if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/i", target: "a"}}); err != nil {
7069 t.Fatal(err)
7574 }
7675
7776 func TestFollowSymlinkSkipSymlinksOutsideScope(t *testing.T) {
78 tmpdir, cleanup := mkTempDir(t)
79 defer cleanup()
77 tmpdir := mkTempDir(t)
8078
8179 if err := makeFs(tmpdir, []dirOrLink{
8280 {path: "linkdir", target: "realdir"},
9694 }
9795
9896 func TestFollowSymlinkLastLink(t *testing.T) {
99 tmpdir, cleanup := mkTempDir(t)
100 defer cleanup()
97 tmpdir := mkTempDir(t)
10198
10299 if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/d", target: "/b"}}); err != nil {
103100 t.Fatal(err)
108105 }
109106
110107 func TestFollowSymlinkRelativeLinkChangeScope(t *testing.T) {
111 tmpdir, cleanup := mkTempDir(t)
112 defer cleanup()
108 tmpdir := mkTempDir(t)
113109
114110 if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/e", target: "../b"}}); err != nil {
115111 t.Fatal(err)
125121 }
126122
127123 func TestFollowSymlinkDeepRelativeLinkChangeScope(t *testing.T) {
128 tmpdir, cleanup := mkTempDir(t)
129 defer cleanup()
124 tmpdir := mkTempDir(t)
130125
131126 if err := makeFs(tmpdir, []dirOrLink{{path: "testdata/fs/a/f", target: "../../../../test"}}); err != nil {
132127 t.Fatal(err)
146141 }
147142
148143 func TestFollowSymlinkRelativeLinkChain(t *testing.T) {
149 tmpdir, cleanup := mkTempDir(t)
150 defer cleanup()
144 tmpdir := mkTempDir(t)
151145
152146 // avoid letting symlink g (pointed at by symlink h) take out of scope
153147 // TODO: we should probably normalize to scope here because ../[....]/root
164158 }
165159
166160 func TestFollowSymlinkBreakoutPath(t *testing.T) {
167 tmpdir, cleanup := mkTempDir(t)
168 defer cleanup()
161 tmpdir := mkTempDir(t)
169162
170163 // avoid letting symlink -> ../directory/file escape from scope
171164 // normalize to "testdata/fs/j"
178171 }
179172
180173 func TestFollowSymlinkToRoot(t *testing.T) {
181 tmpdir, cleanup := mkTempDir(t)
182 defer cleanup()
174 tmpdir := mkTempDir(t)
183175
184176 // make sure we don't allow escaping to /
185177 // normalize to dir
192184 }
193185
194186 func TestFollowSymlinkSlashDotdot(t *testing.T) {
195 tmpdir, cleanup := mkTempDir(t)
196 defer cleanup()
187 tmpdir := mkTempDir(t)
197188 tmpdir = filepath.Join(tmpdir, "dir", "subdir")
198189
199190 // make sure we don't allow escaping to /
207198 }
208199
209200 func TestFollowSymlinkDotdot(t *testing.T) {
210 tmpdir, cleanup := mkTempDir(t)
211 defer cleanup()
201 tmpdir := mkTempDir(t)
212202 tmpdir = filepath.Join(tmpdir, "dir", "subdir")
213203
214204 // make sure we stay in scope without leaking information
223213 }
224214
225215 func TestFollowSymlinkRelativePath2(t *testing.T) {
226 tmpdir, cleanup := mkTempDir(t)
227 defer cleanup()
216 tmpdir := mkTempDir(t)
228217
229218 if err := makeFs(tmpdir, []dirOrLink{{path: "bar/foo", target: "baz/target"}}); err != nil {
230219 t.Fatal(err)
235224 }
236225
237226 func TestFollowSymlinkScopeLink(t *testing.T) {
238 tmpdir, cleanup := mkTempDir(t)
239 defer cleanup()
227 tmpdir := mkTempDir(t)
240228
241229 if err := makeFs(tmpdir, []dirOrLink{
242230 {path: "root2"},
251239 }
252240
253241 func TestFollowSymlinkRootScope(t *testing.T) {
254 tmpdir, cleanup := mkTempDir(t)
255 defer cleanup()
242 tmpdir := mkTempDir(t)
256243
257244 expected, err := filepath.EvalSymlinks(tmpdir)
258245 if err != nil {
282269 }
283270
284271 func TestFollowSymlinkCircular(t *testing.T) {
285 tmpdir, cleanup := mkTempDir(t)
286 defer cleanup()
272 tmpdir := mkTempDir(t)
287273
288274 if err := makeFs(tmpdir, []dirOrLink{{path: "root/foo", target: "foo"}}); err != nil {
289275 t.Fatal(err)
305291 }
306292
307293 func TestFollowSymlinkComplexChainWithTargetPathsContainingLinks(t *testing.T) {
308 tmpdir, cleanup := mkTempDir(t)
309 defer cleanup()
294 tmpdir := mkTempDir(t)
310295
311296 if err := makeFs(tmpdir, []dirOrLink{
312297 {path: "root2"},
326311 }
327312
328313 func TestFollowSymlinkBreakoutNonExistent(t *testing.T) {
329 tmpdir, cleanup := mkTempDir(t)
330 defer cleanup()
314 tmpdir := mkTempDir(t)
331315
332316 if err := makeFs(tmpdir, []dirOrLink{
333317 {path: "root/slash", target: "/"},
341325 }
342326
343327 func TestFollowSymlinkNoLexicalCleaning(t *testing.T) {
344 tmpdir, cleanup := mkTempDir(t)
345 defer cleanup()
328 tmpdir := mkTempDir(t)
346329
347330 if err := makeFs(tmpdir, []dirOrLink{
348331 {path: "root/sym", target: "/foo/bar"},
355338 }
356339 }
357340
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 {
359344 t.Helper()
360345 tmpdir, err := ioutil.TempDir("", t.Name())
361346 if err != nil {
362347 t.Fatal(err)
363348 }
364 return tmpdir, func() { _ = os.RemoveAll(tmpdir) }
365 }
349 t.Cleanup(func() { _ = os.RemoveAll(tmpdir) })
350 return tmpdir
351 }
8888 var b bytes.Buffer
8989 for n := 0; path != ""; n++ {
9090 if n > maxIter {
91 return "", errors.New("EvalSymlinks: too many links in " + originalPath)
91 return "", errors.New("too many links in " + originalPath)
9292 }
9393
9494 // A path beginning with `\\?\` represents the root, so automatically
00 module github.com/moby/sys/symlink
11
2 go 1.13
2 go 1.14
33
44 require golang.org/x/sys v0.0.0-20200922070232-aee5d888a860