1
2
3
4 package storage
5
6 import (
7 "bufio"
8 "context"
9 "fmt"
10 "os"
11 "strings"
12 "syscall"
13
14 "github.com/pkg/errors"
15 "go.opencensus.io/trace"
16 "golang.org/x/sys/unix"
17
18 "github.com/Microsoft/hcsshim/internal/oc"
19 )
20
21 const procMountFile = "/proc/mounts"
22 const numProcMountFields = 6
23
24
25 var (
26 osStat = os.Stat
27 unixUnmount = unix.Unmount
28 unixMount = unix.Mount
29 osRemoveAll = os.RemoveAll
30 listMounts = listMountPointsUnderPath
31
32 flags = map[string]struct {
33 clear bool
34 flag uintptr
35 }{
36 "acl": {false, unix.MS_POSIXACL},
37 "async": {true, unix.MS_SYNCHRONOUS},
38 "atime": {true, unix.MS_NOATIME},
39 "bind": {false, unix.MS_BIND},
40 "defaults": {false, 0},
41 "dev": {true, unix.MS_NODEV},
42 "diratime": {true, unix.MS_NODIRATIME},
43 "dirsync": {false, unix.MS_DIRSYNC},
44 "exec": {true, unix.MS_NOEXEC},
45 "iversion": {false, unix.MS_I_VERSION},
46 "lazytime": {false, unix.MS_LAZYTIME},
47 "loud": {true, unix.MS_SILENT},
48 "mand": {false, unix.MS_MANDLOCK},
49 "noacl": {true, unix.MS_POSIXACL},
50 "noatime": {false, unix.MS_NOATIME},
51 "nodev": {false, unix.MS_NODEV},
52 "nodiratime": {false, unix.MS_NODIRATIME},
53 "noexec": {false, unix.MS_NOEXEC},
54 "noiversion": {true, unix.MS_I_VERSION},
55 "nolazytime": {true, unix.MS_LAZYTIME},
56 "nomand": {true, unix.MS_MANDLOCK},
57 "norelatime": {true, unix.MS_RELATIME},
58 "nostrictatime": {true, unix.MS_STRICTATIME},
59 "nosuid": {false, unix.MS_NOSUID},
60 "rbind": {false, unix.MS_BIND | unix.MS_REC},
61 "relatime": {false, unix.MS_RELATIME},
62 "remount": {false, unix.MS_REMOUNT},
63 "ro": {false, unix.MS_RDONLY},
64 "rw": {true, unix.MS_RDONLY},
65 "silent": {false, unix.MS_SILENT},
66 "strictatime": {false, unix.MS_STRICTATIME},
67 "suid": {true, unix.MS_NOSUID},
68 "sync": {false, unix.MS_SYNCHRONOUS},
69 }
70
71 propagationFlags = map[string]uintptr{
72 "private": unix.MS_PRIVATE,
73 "shared": unix.MS_SHARED,
74 "slave": unix.MS_SLAVE,
75 "unbindable": unix.MS_UNBINDABLE,
76 "rprivate": unix.MS_PRIVATE | unix.MS_REC,
77 "rshared": unix.MS_SHARED | unix.MS_REC,
78 "rslave": unix.MS_SLAVE | unix.MS_REC,
79 "runbindable": unix.MS_UNBINDABLE | unix.MS_REC,
80 }
81 )
82
83 func ParseMountOptions(options []string) (flagOpts uintptr, pgFlags []uintptr, data []string) {
84 for _, o := range options {
85 if f, exists := flags[o]; exists && f.flag != 0 {
86 if f.clear {
87 flagOpts &= ^f.flag
88 } else {
89 flagOpts |= f.flag
90 }
91 } else if f, exists := propagationFlags[o]; exists && f != 0 {
92 pgFlags = append(pgFlags, f)
93 } else {
94 data = append(data, o)
95 }
96 }
97 return
98 }
99
100
101
102 func MountRShared(path string) error {
103 if path == "" {
104 return errors.New("path must not be empty to mount as rshared")
105 }
106 if err := unixMount(path, path, "", syscall.MS_BIND, ""); err != nil {
107 return fmt.Errorf("failed to create bind mount for %v: %v", path, err)
108 }
109 if err := unixMount(path, path, "", syscall.MS_SHARED|syscall.MS_REC, ""); err != nil {
110 return fmt.Errorf("failed to make %v rshared: %v", path, err)
111 }
112 return nil
113 }
114
115
116
117 func UnmountPath(ctx context.Context, target string, removeTarget bool) (err error) {
118 _, span := oc.StartSpan(ctx, "storage::UnmountPath")
119 defer span.End()
120 defer func() { oc.SetSpanStatus(span, err) }()
121
122 span.AddAttributes(
123 trace.StringAttribute("target", target),
124 trace.BoolAttribute("remove", removeTarget))
125
126 if _, err := osStat(target); err != nil {
127 if os.IsNotExist(err) {
128 return nil
129 }
130 return errors.Wrapf(err, "failed to determine if path '%s' exists", target)
131 }
132
133 if err := unixUnmount(target, 0); err != nil {
134
135
136 if err != unix.EINVAL {
137 return errors.Wrapf(err, "failed to unmount path '%s'", target)
138 }
139 }
140 if removeTarget {
141 return osRemoveAll(target)
142 }
143 return nil
144 }
145
146 func UnmountAllInPath(ctx context.Context, path string, removeTarget bool) (err error) {
147 childMounts, err := listMounts(path)
148 if err != nil {
149 return err
150 }
151
152 for i := len(childMounts) - 1; i >= 0; i-- {
153 childPath := childMounts[i]
154 if err := UnmountPath(ctx, childPath, removeTarget); err != nil {
155 return err
156 }
157 }
158 return nil
159 }
160
161 func listMountPointsUnderPath(path string) ([]string, error) {
162 var mountPoints []string
163 f, err := os.Open(procMountFile)
164 if err != nil {
165 return nil, err
166 }
167 defer f.Close()
168 scanner := bufio.NewScanner(f)
169 for scanner.Scan() {
170 line := scanner.Text()
171 fields := strings.Split(line, " ")
172 if len(fields) < numProcMountFields {
173 continue
174 }
175 destPath := fields[1]
176 if strings.HasPrefix(destPath, path) {
177 mountPoints = append(mountPoints, destPath)
178 }
179 }
180
181 return mountPoints, nil
182 }
183
View as plain text