summaryrefslogtreecommitdiff
path: root/vendor/github.com/mrunalp/fileutils/idtools.go
blob: bad6539df5331118521d5e4acbbd2ee7907595bd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package fileutils

import (
	"os"
	"path/filepath"
	"syscall"
)

// MkdirAllNewAs creates a directory (include any along the path) and then modifies
// ownership ONLY of newly created directories to the requested uid/gid. If the
// directories along the path exist, no change of ownership will be performed
func MkdirAllNewAs(path string, mode os.FileMode, ownerUID, ownerGID int) error {
	// make an array containing the original path asked for, plus (for mkAll == true)
	// all path components leading up to the complete path that don't exist before we MkdirAll
	// so that we can chown all of them properly at the end.  If chownExisting is false, we won't
	// chown the full directory path if it exists
	var paths []string
	st, err := os.Stat(path)
	if err != nil && os.IsNotExist(err) {
		paths = []string{path}
	} else if err == nil {
		if !st.IsDir() {
			return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
		}
		// nothing to do; directory path fully exists already
		return nil
	}

	// walk back to "/" looking for directories which do not exist
	// and add them to the paths array for chown after creation
	dirPath := path
	for {
		dirPath = filepath.Dir(dirPath)
		if dirPath == "/" {
			break
		}
		if _, err := os.Stat(dirPath); err != nil && os.IsNotExist(err) {
			paths = append(paths, dirPath)
		}
	}

	if err := os.MkdirAll(path, mode); err != nil {
		return err
	}

	// even if it existed, we will chown the requested path + any subpaths that
	// didn't exist when we called MkdirAll
	for _, pathComponent := range paths {
		if err := os.Chown(pathComponent, ownerUID, ownerGID); err != nil {
			return err
		}
	}
	return nil
}