1
2
3
4
19
20 package volume
21
22 import (
23 "path/filepath"
24 "syscall"
25
26 "os"
27 "time"
28
29 v1 "k8s.io/api/core/v1"
30 "k8s.io/klog/v2"
31 "k8s.io/kubernetes/pkg/volume/util/types"
32 )
33
34 const (
35 rwMask = os.FileMode(0660)
36 roMask = os.FileMode(0440)
37 execMask = os.FileMode(0110)
38 )
39
40
41
42
43 func SetVolumeOwnership(mounter Mounter, dir string, fsGroup *int64, fsGroupChangePolicy *v1.PodFSGroupChangePolicy, completeFunc func(types.CompleteFuncParam)) error {
44 if fsGroup == nil {
45 return nil
46 }
47
48 timer := time.AfterFunc(30*time.Second, func() {
49 klog.Warningf("Setting volume ownership for %s and fsGroup set. If the volume has a lot of files then setting volume ownership could be slow, see https://github.com/kubernetes/kubernetes/issues/69699", dir)
50 })
51 defer timer.Stop()
52
53 if skipPermissionChange(mounter, dir, fsGroup, fsGroupChangePolicy) {
54 klog.V(3).InfoS("Skipping permission and ownership change for volume", "path", dir)
55 return nil
56 }
57
58 err := walkDeep(dir, func(path string, info os.FileInfo, err error) error {
59 if err != nil {
60 return err
61 }
62 return changeFilePermission(path, fsGroup, mounter.GetAttributes().ReadOnly, info)
63 })
64 if completeFunc != nil {
65 completeFunc(types.CompleteFuncParam{
66 Err: &err,
67 })
68 }
69 return err
70 }
71
72 func changeFilePermission(filename string, fsGroup *int64, readonly bool, info os.FileInfo) error {
73 err := os.Lchown(filename, -1, int(*fsGroup))
74 if err != nil {
75 klog.ErrorS(err, "Lchown failed", "path", filename)
76 }
77
78
79
80
81
82
83
84
85 if info.Mode()&os.ModeSymlink != 0 {
86 return nil
87 }
88
89 mask := rwMask
90 if readonly {
91 mask = roMask
92 }
93
94 if info.IsDir() {
95 mask |= os.ModeSetgid
96 mask |= execMask
97 }
98
99 err = os.Chmod(filename, info.Mode()|mask)
100 if err != nil {
101 klog.ErrorS(err, "chmod failed", "path", filename)
102 }
103
104 return nil
105 }
106
107 func skipPermissionChange(mounter Mounter, dir string, fsGroup *int64, fsGroupChangePolicy *v1.PodFSGroupChangePolicy) bool {
108 if fsGroupChangePolicy == nil || *fsGroupChangePolicy != v1.FSGroupChangeOnRootMismatch {
109 klog.V(4).InfoS("Perform recursive ownership change for directory", "path", dir)
110 return false
111 }
112 return !requiresPermissionChange(dir, fsGroup, mounter.GetAttributes().ReadOnly)
113 }
114
115 func requiresPermissionChange(rootDir string, fsGroup *int64, readonly bool) bool {
116 fsInfo, err := os.Stat(rootDir)
117 if err != nil {
118 klog.ErrorS(err, "Performing recursive ownership change on rootDir because reading permissions of root volume failed", "path", rootDir)
119 return true
120 }
121 stat, ok := fsInfo.Sys().(*syscall.Stat_t)
122 if !ok || stat == nil {
123 klog.ErrorS(nil, "Performing recursive ownership change on rootDir because reading permissions of root volume failed", "path", rootDir)
124 return true
125 }
126
127 if int(stat.Gid) != int(*fsGroup) {
128 klog.V(4).InfoS("Expected group ownership of volume did not match with Gid", "path", rootDir, "GID", stat.Gid)
129 return true
130 }
131 unixPerms := rwMask
132
133 if readonly {
134 unixPerms = roMask
135 }
136
137
138 if !fsInfo.IsDir() {
139 return true
140 }
141 unixPerms |= execMask
142 filePerm := fsInfo.Mode().Perm()
143
144
145
146
147
148
149
150
151 if (unixPerms&filePerm != unixPerms) || (fsInfo.Mode()&os.ModeSetgid == 0) {
152 klog.V(4).InfoS("Performing recursive ownership change on rootDir because of mismatching mode", "path", rootDir)
153 return true
154 }
155 return false
156 }
157
158
159
160
161
162 func readDirNames(dirname string) ([]string, error) {
163 f, err := os.Open(dirname)
164 if err != nil {
165 return nil, err
166 }
167 names, err := f.Readdirnames(-1)
168 f.Close()
169 if err != nil {
170 return nil, err
171 }
172 return names, nil
173 }
174
175
176
177
178
179 func walkDeep(root string, walkFunc filepath.WalkFunc) error {
180 info, err := os.Lstat(root)
181 if err != nil {
182 return walkFunc(root, nil, err)
183 }
184 return walk(root, info, walkFunc)
185 }
186
187 func walk(path string, info os.FileInfo, walkFunc filepath.WalkFunc) error {
188 if !info.IsDir() {
189 return walkFunc(path, info, nil)
190 }
191 names, err := readDirNames(path)
192 if err != nil {
193 return err
194 }
195 for _, name := range names {
196 filename := filepath.Join(path, name)
197 fileInfo, err := os.Lstat(filename)
198 if err != nil {
199 if err := walkFunc(filename, fileInfo, err); err != nil {
200 return err
201 }
202 } else {
203 err = walk(filename, fileInfo, walkFunc)
204 if err != nil {
205 return err
206 }
207 }
208 }
209 return walkFunc(path, info, nil)
210 }
211
View as plain text