1
16
17 package volumepathhandler
18
19 import (
20 "fmt"
21 "os"
22 "path/filepath"
23
24 "k8s.io/klog/v2"
25 "k8s.io/mount-utils"
26 utilexec "k8s.io/utils/exec"
27
28 "k8s.io/apimachinery/pkg/types"
29 )
30
31 const (
32 losetupPath = "losetup"
33 ErrDeviceNotFound = "device not found"
34 )
35
36
37 type BlockVolumePathHandler interface {
38
39 MapDevice(devicePath string, mapPath string, linkName string, bindMount bool) error
40
41 UnmapDevice(mapPath string, linkName string, bindMount bool) error
42
43 RemoveMapPath(mapPath string) error
44
45 IsSymlinkExist(mapPath string) (bool, error)
46
47 IsDeviceBindMountExist(mapPath string) (bool, error)
48
49 GetDeviceBindMountRefs(devPath string, mapPath string) ([]string, error)
50
51
52 FindGlobalMapPathUUIDFromPod(pluginDir, mapPath string, podUID types.UID) (string, error)
53
54
55 AttachFileDevice(path string) (string, error)
56
57
58 DetachFileDevice(path string) error
59
60 GetLoopDevice(path string) (string, error)
61 }
62
63
64 func NewBlockVolumePathHandler() BlockVolumePathHandler {
65 var volumePathHandler VolumePathHandler
66 return volumePathHandler
67 }
68
69
70 type VolumePathHandler struct {
71 }
72
73
74 func (v VolumePathHandler) MapDevice(devicePath string, mapPath string, linkName string, bindMount bool) error {
75
76
77
78
79
80
81
82 if len(devicePath) == 0 {
83 return fmt.Errorf("failed to map device to map path. devicePath is empty")
84 }
85 if len(mapPath) == 0 {
86 return fmt.Errorf("failed to map device to map path. mapPath is empty")
87 }
88 if !filepath.IsAbs(mapPath) {
89 return fmt.Errorf("the map path should be absolute: map path: %s", mapPath)
90 }
91 klog.V(5).Infof("MapDevice: devicePath %s", devicePath)
92 klog.V(5).Infof("MapDevice: mapPath %s", mapPath)
93 klog.V(5).Infof("MapDevice: linkName %s", linkName)
94
95
96 _, err := os.Stat(mapPath)
97 if err != nil && !os.IsNotExist(err) {
98 return fmt.Errorf("cannot validate map path: %s: %v", mapPath, err)
99 }
100 if err = os.MkdirAll(mapPath, 0750); err != nil {
101 return fmt.Errorf("failed to mkdir %s: %v", mapPath, err)
102 }
103
104 if bindMount {
105 return mapBindMountDevice(v, devicePath, mapPath, linkName)
106 }
107 return mapSymlinkDevice(v, devicePath, mapPath, linkName)
108 }
109
110 func mapBindMountDevice(v VolumePathHandler, devicePath string, mapPath string, linkName string) error {
111
112 linkPath := filepath.Join(mapPath, string(linkName))
113
114 file, err := os.Stat(linkPath)
115 if err != nil {
116 if !os.IsNotExist(err) {
117 return fmt.Errorf("failed to stat file %s: %v", linkPath, err)
118 }
119
120
121 newFile, err := os.OpenFile(linkPath, os.O_CREATE|os.O_RDWR, 0750)
122 if err != nil {
123 return fmt.Errorf("failed to open file %s: %v", linkPath, err)
124 }
125 if err := newFile.Close(); err != nil {
126 return fmt.Errorf("failed to close file %s: %v", linkPath, err)
127 }
128 } else {
129
130
131 if file.Mode()&os.ModeDevice == os.ModeDevice {
132 klog.Warningf("Warning: Map skipped because bind mount already exist on the path: %v", linkPath)
133 return nil
134 }
135
136 klog.Warningf("Warning: file %s is already exist but not mounted, skip creating file", linkPath)
137 }
138
139
140 mounter := &mount.SafeFormatAndMount{Interface: mount.New(""), Exec: utilexec.New()}
141 if err := mounter.MountSensitiveWithoutSystemd(devicePath, linkPath, "" , []string{"bind"}, nil); err != nil {
142 return fmt.Errorf("failed to bind mount devicePath: %s to linkPath %s: %v", devicePath, linkPath, err)
143 }
144
145 return nil
146 }
147
148 func mapSymlinkDevice(v VolumePathHandler, devicePath string, mapPath string, linkName string) error {
149
150
151
152 linkPath := filepath.Join(mapPath, string(linkName))
153 if err := os.Remove(linkPath); err != nil && !os.IsNotExist(err) {
154 return fmt.Errorf("failed to remove file %s: %v", linkPath, err)
155 }
156 return os.Symlink(devicePath, linkPath)
157 }
158
159
160 func (v VolumePathHandler) UnmapDevice(mapPath string, linkName string, bindMount bool) error {
161 if len(mapPath) == 0 {
162 return fmt.Errorf("failed to unmap device from map path. mapPath is empty")
163 }
164 klog.V(5).Infof("UnmapDevice: mapPath %s", mapPath)
165 klog.V(5).Infof("UnmapDevice: linkName %s", linkName)
166
167 if bindMount {
168 return unmapBindMountDevice(v, mapPath, linkName)
169 }
170 return unmapSymlinkDevice(v, mapPath, linkName)
171 }
172
173 func unmapBindMountDevice(v VolumePathHandler, mapPath string, linkName string) error {
174
175 linkPath := filepath.Join(mapPath, string(linkName))
176 if isMountExist, checkErr := v.IsDeviceBindMountExist(linkPath); checkErr != nil {
177 return checkErr
178 } else if !isMountExist {
179 klog.Warningf("Warning: Unmap skipped because bind mount does not exist on the path: %v", linkPath)
180
181
182 if _, err := os.Stat(linkPath); err != nil {
183 if !os.IsNotExist(err) {
184 return fmt.Errorf("failed to check if path %s exists: %v", linkPath, err)
185 }
186
187 return nil
188 }
189
190 if err := os.Remove(linkPath); err != nil && !os.IsNotExist(err) {
191 return fmt.Errorf("failed to remove file %s: %v", linkPath, err)
192 }
193 return nil
194 }
195
196
197 mounter := &mount.SafeFormatAndMount{Interface: mount.New(""), Exec: utilexec.New()}
198 if err := mounter.Unmount(linkPath); err != nil {
199 return fmt.Errorf("failed to unmount linkPath %s: %v", linkPath, err)
200 }
201
202
203 if err := os.Remove(linkPath); err != nil && !os.IsNotExist(err) {
204 return fmt.Errorf("failed to remove file %s: %v", linkPath, err)
205 }
206
207 return nil
208 }
209
210 func unmapSymlinkDevice(v VolumePathHandler, mapPath string, linkName string) error {
211
212 linkPath := filepath.Join(mapPath, string(linkName))
213 if islinkExist, checkErr := v.IsSymlinkExist(linkPath); checkErr != nil {
214 return checkErr
215 } else if !islinkExist {
216 klog.Warningf("Warning: Unmap skipped because symlink does not exist on the path: %v", linkPath)
217 return nil
218 }
219 return os.Remove(linkPath)
220 }
221
222
223 func (v VolumePathHandler) RemoveMapPath(mapPath string) error {
224 if len(mapPath) == 0 {
225 return fmt.Errorf("failed to remove map path. mapPath is empty")
226 }
227 klog.V(5).Infof("RemoveMapPath: mapPath %s", mapPath)
228 err := os.RemoveAll(mapPath)
229 if err != nil && !os.IsNotExist(err) {
230 return fmt.Errorf("failed to remove directory %s: %v", mapPath, err)
231 }
232 return nil
233 }
234
235
236
237
238 func (v VolumePathHandler) IsSymlinkExist(mapPath string) (bool, error) {
239 fi, err := os.Lstat(mapPath)
240 if err != nil {
241
242 if os.IsNotExist(err) {
243 return false, nil
244 }
245
246 return false, fmt.Errorf("failed to Lstat file %s: %v", mapPath, err)
247 }
248
249 if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
250 return true, nil
251 }
252
253 return false, nil
254 }
255
256
257
258
259 func (v VolumePathHandler) IsDeviceBindMountExist(mapPath string) (bool, error) {
260 fi, err := os.Lstat(mapPath)
261 if err != nil {
262
263 if os.IsNotExist(err) {
264 return false, nil
265 }
266
267
268 return false, fmt.Errorf("failed to Lstat file %s: %v", mapPath, err)
269 }
270
271 if fi.Mode()&os.ModeDevice == os.ModeDevice {
272 return true, nil
273 }
274
275 return false, nil
276 }
277
278
279 func (v VolumePathHandler) GetDeviceBindMountRefs(devPath string, mapPath string) ([]string, error) {
280 var refs []string
281 files, err := os.ReadDir(mapPath)
282 if err != nil {
283 return nil, err
284 }
285 for _, file := range files {
286 if file.Type()&os.ModeDevice != os.ModeDevice {
287 continue
288 }
289 filename := file.Name()
290
291 refs = append(refs, filepath.Join(mapPath, filename))
292 }
293 klog.V(5).Infof("GetDeviceBindMountRefs: refs %v", refs)
294 return refs, nil
295 }
296
View as plain text