go-sandbox/runner/config/file_set.go
2019-08-30 00:57:07 -07:00

159 lines
3.7 KiB
Go

package config
import (
"path/filepath"
"strings"
)
// FileSet stores the file permissions in the hierarchical set
type FileSet struct {
Set map[string]bool
SystemRoot bool
}
// FilePerm stores the permission apply to the file
type FilePerm int
// FilePermWrite / Read / Stat are permissions
const (
FilePermWrite = iota + 1
FilePermRead
FilePermStat
)
// NewFileSet creates the new file set
func NewFileSet() FileSet {
return FileSet{make(map[string]bool), false}
}
// IsInSetSmart same from uoj-judger
func (s *FileSet) IsInSetSmart(name string) bool {
if s.Set[name] {
return true
}
if name == "/" && s.SystemRoot {
return true
}
// check ...
level := 0
for level = 0; name != ""; level++ {
if level == 1 && s.Set[name+"/*"] {
return true
}
if s.Set[name+"/"] {
return true
}
name = dirname(name)
}
if level == 1 && s.Set["/*"] {
return true
}
if s.Set["/"] {
return true
}
return false
}
// Add adds a single file path into the FileSet
func (s *FileSet) Add(name string) {
if name == "/" {
s.SystemRoot = true
} else {
s.Set[name] = true
}
}
// AddRange adds multiple files into the FileSet
// If path is relative path, add according to the workPath
func (s *FileSet) AddRange(names []string, workPath string) {
for _, n := range names {
if filepath.IsAbs(n) {
if n == "/" {
s.SystemRoot = true
} else {
s.Set[n] = true
}
} else {
s.Set[filepath.Join(workPath, n)+"/"] = true
}
}
}
// FileSets agregates multiple permissions including write / read / stat / soft ban
type FileSets struct {
Writable, Readable, Statable, SoftBan FileSet
}
// NewFileSets creates new FileSets struct
func NewFileSets() *FileSets {
return &FileSets{NewFileSet(), NewFileSet(), NewFileSet(), NewFileSet()}
}
// IsWritableFile determines whether the file path inside the write set
func (s *FileSets) IsWritableFile(name string) bool {
return s.Writable.IsInSetSmart(name) || s.Writable.IsInSetSmart(realPath(name))
}
// IsReadableFile determines whether the file path inside the read / write set
func (s *FileSets) IsReadableFile(name string) bool {
return s.IsWritableFile(name) || s.Readable.IsInSetSmart(name) || s.Readable.IsInSetSmart(realPath(name))
}
// IsStatableFile determines whether the file path inside the stat / read / write set
func (s *FileSets) IsStatableFile(name string) bool {
return s.IsReadableFile(name) || s.Statable.IsInSetSmart(name) || s.Statable.IsInSetSmart(realPath(name))
}
// IsSoftBanFile determines whether the file path inside the softban set
func (s *FileSets) IsSoftBanFile(name string) bool {
return s.SoftBan.IsInSetSmart(name) || s.SoftBan.IsInSetSmart(realPath(name))
}
// AddFilePermission adds the file into fileSets according to the given permission
func (s *FileSets) AddFilePermission(name string, mode FilePerm) {
if mode == FilePermWrite {
s.Writable.Add(name)
} else if mode == FilePermRead {
s.Readable.Add(name)
} else if mode == FilePermStat {
s.Statable.Add(name)
}
for name = dirname(name); name != ""; name = dirname(name) {
s.Statable.Add(name)
}
}
// GetExtraSet evaluates the concated file set according to real path or raw path
func GetExtraSet(extra, raw []string) []string {
rt := make([]string, 0, len(extra)+len(raw))
rt = append(rt, raw...)
for _, v := range extra {
rt = append(rt, realPath(v))
}
return rt
}
// basename return path with last "/"
func basename(path string) string {
if p := strings.LastIndex(path, "/"); p >= 0 {
return path[:p+1]
}
return path
}
// dirname return path without last "/"
func dirname(path string) string {
if p := strings.LastIndex(path, "/"); p >= 0 {
return path[:p]
}
return ""
}
func realPath(p string) string {
f, err := filepath.EvalSymlinks(p)
if err != nil {
return ""
}
return f
}