Skip to content

Commit ed869b0

Browse files
author
Mrunal Patel
authored
Merge pull request #1 from cyphar/fix-userns
fileutils: chown(2) directories when copying
2 parents 1e0e2ba + 3de356a commit ed869b0

2 files changed

Lines changed: 75 additions & 4 deletions

File tree

fileutils.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,31 +93,53 @@ func CopyDirectory(source string, dest string) error {
9393
if err != nil {
9494
return err
9595
}
96-
if err := os.MkdirAll(dest, fi.Mode()); err != nil {
96+
97+
// Get owner.
98+
st, ok := fi.Sys().(*syscall.Stat_t)
99+
if !ok {
100+
return fmt.Errorf("could not convert to syscall.Stat_t")
101+
}
102+
103+
// We have to pick an owner here anyway.
104+
if err := MkdirAllNewAs(dest, fi.Mode(), int(st.Uid), int(st.Gid)); err != nil {
97105
return err
98106
}
107+
99108
return filepath.Walk(source, func(path string, info os.FileInfo, err error) error {
100109
if err != nil {
101110
return err
102111
}
103112

104-
// get the relative path
113+
// Get the relative path
105114
relPath, err := filepath.Rel(source, path)
106115
if err != nil {
107116
return nil
108117
}
109118

110-
// skip the source directory
111119
if info.IsDir() {
120+
// Skip the source directory.
112121
if path != source {
122+
// Get the owner.
123+
st, ok := info.Sys().(*syscall.Stat_t)
124+
if !ok {
125+
return fmt.Errorf("could not convert to syscall.Stat_t")
126+
}
127+
128+
uid := int(st.Uid)
129+
gid := int(st.Gid)
130+
113131
if err := os.Mkdir(filepath.Join(dest, relPath), info.Mode()); err != nil {
114132
return err
115133
}
134+
135+
if err := os.Lchown(filepath.Join(dest, relPath), uid, gid); err != nil {
136+
return err
137+
}
116138
}
117139
return nil
118140
}
119141

120-
// Copy the file
142+
// Copy the file.
121143
if err := CopyFile(path, filepath.Join(dest, relPath)); err != nil {
122144
return err
123145
}

idtools.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package fileutils
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
)
7+
8+
// MkdirAllNewAs creates a directory (include any along the path) and then modifies
9+
// ownership ONLY of newly created directories to the requested uid/gid. If the
10+
// directories along the path exist, no change of ownership will be performed
11+
func MkdirAllNewAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
12+
// make an array containing the original path asked for, plus (for mkAll == true)
13+
// all path components leading up to the complete path that don't exist before we MkdirAll
14+
// so that we can chown all of them properly at the end. If chownExisting is false, we won't
15+
// chown the full directory path if it exists
16+
var paths []string
17+
if _, err := os.Stat(path); err != nil && os.IsNotExist(err) {
18+
paths = []string{path}
19+
} else if err == nil {
20+
// nothing to do; directory path fully exists already
21+
return nil
22+
}
23+
24+
// walk back to "/" looking for directories which do not exist
25+
// and add them to the paths array for chown after creation
26+
dirPath := path
27+
for {
28+
dirPath = filepath.Dir(dirPath)
29+
if dirPath == "/" {
30+
break
31+
}
32+
if _, err := os.Stat(dirPath); err != nil && os.IsNotExist(err) {
33+
paths = append(paths, dirPath)
34+
}
35+
}
36+
37+
if err := os.MkdirAll(path, mode); err != nil && !os.IsExist(err) {
38+
return err
39+
}
40+
41+
// even if it existed, we will chown the requested path + any subpaths that
42+
// didn't exist when we called MkdirAll
43+
for _, pathComponent := range paths {
44+
if err := os.Chown(pathComponent, ownerUID, ownerGID); err != nil {
45+
return err
46+
}
47+
}
48+
return nil
49+
}

0 commit comments

Comments
 (0)