mirror of
https://github.com/safing/portbase
synced 2025-09-04 03:29:59 +00:00
Add new dir structure util to ensure correct dir permissions in a structure
This commit is contained in:
parent
46b151ddfe
commit
c4b9c73f41
3 changed files with 214 additions and 1 deletions
|
@ -6,6 +6,8 @@ import (
|
||||||
"runtime"
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const isWindows = runtime.GOOS == "windows"
|
||||||
|
|
||||||
// EnsureDirectory ensures that the given directoy exists and that is has the given permissions set.
|
// 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 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.
|
// 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
|
// file exists
|
||||||
if f.IsDir() {
|
if f.IsDir() {
|
||||||
// directory exists, check permissions
|
// directory exists, check permissions
|
||||||
if runtime.GOOS == "windows" {
|
if isWindows {
|
||||||
// TODO: set correct permission on windows
|
// TODO: set correct permission on windows
|
||||||
// acl.Chmod(path, perm)
|
// acl.Chmod(path, perm)
|
||||||
} else if f.Mode().Perm() != perm {
|
} else if f.Mode().Perm() != perm {
|
||||||
|
|
139
utils/structure.go
Normal file
139
utils/structure.go
Normal 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
72
utils/structure_test.go
Normal 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
|
||||||
|
})
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue