Add new dir structure util to ensure correct dir permissions in a structure

This commit is contained in:
Daniel 2019-07-31 22:30:57 +02:00
parent 46b151ddfe
commit c4b9c73f41
3 changed files with 214 additions and 1 deletions

View file

@ -6,6 +6,8 @@ import (
"runtime"
)
const isWindows = runtime.GOOS == "windows"
// EnsureDirectory ensures that the given directoy exists and that is has the given permissions set.
// If path is a file, it is deleted and a directory created.
// If a directory is created, also all missing directories up to the required one are created with the given permissions.
@ -16,7 +18,7 @@ func EnsureDirectory(path string, perm os.FileMode) error {
// file exists
if f.IsDir() {
// directory exists, check permissions
if runtime.GOOS == "windows" {
if isWindows {
// TODO: set correct permission on windows
// acl.Chmod(path, perm)
} else if f.Mode().Perm() != perm {

139
utils/structure.go Normal file
View file

@ -0,0 +1,139 @@
package utils
import (
"fmt"
"os"
"path/filepath"
"strings"
"sync"
)
// DirStructure represents a directory structure with permissions that should be enforced.
type DirStructure struct {
sync.Mutex
Path string
Dir string
Perm os.FileMode
Parent *DirStructure
Children map[string]*DirStructure
}
// NewDirStructure returns a new DirStructure.
func NewDirStructure(path string, perm os.FileMode) *DirStructure {
return &DirStructure{
Path: path,
Perm: perm,
Children: make(map[string]*DirStructure),
}
}
// ChildDir adds a new child DirStructure and returns it. Should the child already exist, the existing child is returned and the permissions are updated.
func (ds *DirStructure) ChildDir(dirName string, perm os.FileMode) (child *DirStructure) {
ds.Lock()
defer ds.Unlock()
// if exists, update
child, ok := ds.Children[dirName]
if ok {
child.Perm = perm
return child
}
// create new
new := &DirStructure{
Path: filepath.Join(ds.Path, dirName),
Dir: dirName,
Perm: perm,
Parent: ds,
Children: make(map[string]*DirStructure),
}
ds.Children[dirName] = new
return new
}
// Ensure ensures that the specified directory structure (from the first parent on) exists.
func (ds *DirStructure) Ensure() error {
return ds.EnsureAbsPath(ds.Path)
}
// EnsureRelPath ensures that the specified directory structure (from the first parent on) and the given relative path (to the DirStructure) exists.
func (ds *DirStructure) EnsureRelPath(dirPath string) error {
return ds.EnsureAbsPath(filepath.Join(ds.Path, dirPath))
}
// EnsureRelDir ensures that the specified directory structure (from the first parent on) and the given relative path (to the DirStructure) exists.
func (ds *DirStructure) EnsureRelDir(dirNames ...string) error {
return ds.EnsureAbsPath(filepath.Join(append([]string{ds.Path}, dirNames...)...))
}
// EnsureAbsPath ensures that the specified directory structure (from the first parent on) and the given absolute path exists.
// If the given path is outside the DirStructure, an error will be returned.
func (ds *DirStructure) EnsureAbsPath(dirPath string) error {
// always start at the top
if ds.Parent != nil {
return ds.Parent.EnsureAbsPath(dirPath)
}
// check if root
if dirPath == ds.Path {
return ds.ensure(nil)
}
// check scope
slashedPath := ds.Path
// add slash to end
if !strings.HasSuffix(slashedPath, string(filepath.Separator)) {
slashedPath += string(filepath.Separator)
}
// check if given path is in scope
if !strings.HasPrefix(dirPath, slashedPath) {
return fmt.Errorf(`path "%s" is outside of DirStructure scope`, dirPath)
}
// get relative path
relPath, err := filepath.Rel(ds.Path, dirPath)
if err != nil {
return fmt.Errorf("failed to get relative path: %s", err)
}
// split to path elements
pathDirs := strings.Split(filepath.ToSlash(relPath), "/")
// start checking
return ds.ensure(pathDirs)
}
func (ds *DirStructure) ensure(pathDirs []string) error {
ds.Lock()
defer ds.Unlock()
// check current dir
err := EnsureDirectory(ds.Path, ds.Perm)
if err != nil {
return err
}
if len(pathDirs) == 0 {
// we reached the end!
return nil
}
child, ok := ds.Children[pathDirs[0]]
if !ok {
// we have reached the end of the defined dir structure
// ensure all remaining dirs
dirPath := ds.Path
for _, dir := range pathDirs {
dirPath = filepath.Join(dirPath, dir)
err := EnsureDirectory(dirPath, ds.Perm)
if err != nil {
return err
}
}
return nil
}
// we got a child, continue
return child.ensure(pathDirs[1:])
}

72
utils/structure_test.go Normal file
View file

@ -0,0 +1,72 @@
// +build !windows
package utils
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
)
func ExampleDirStructure() {
// output:
// / [755]
// /repo [777]
// /repo/b [755]
// /repo/b/c [750]
// /repo/b/d [755]
// /repo/b/d/e [755]
// /repo/b/d/f [755]
// /secret [700]
basePath, err := ioutil.TempDir("", "")
if err != nil {
fmt.Println(err)
return
}
ds := NewDirStructure(basePath, 0755)
secret := ds.ChildDir("secret", 0700)
repo := ds.ChildDir("repo", 0777)
_ = repo.ChildDir("a", 0700)
b := repo.ChildDir("b", 0755)
c := b.ChildDir("c", 0750)
err = ds.Ensure()
if err != nil {
fmt.Println(err)
}
err = c.Ensure()
if err != nil {
fmt.Println(err)
}
err = secret.Ensure()
if err != nil {
fmt.Println(err)
}
err = b.EnsureRelDir("d", "e")
if err != nil {
fmt.Println(err)
}
err = b.EnsureRelPath("d/f")
if err != nil {
fmt.Println(err)
}
filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error {
if err == nil {
dir := strings.TrimPrefix(path, basePath)
if dir == "" {
dir = "/"
}
fmt.Printf("%s [%o]\n", dir, info.Mode().Perm())
}
return nil
})
}