165 lines
3.3 KiB
Go
165 lines
3.3 KiB
Go
package file
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/sha256"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/user"
|
|
"strconv"
|
|
"syscall"
|
|
)
|
|
|
|
var (
|
|
DefaultPerm os.FileMode = 0740
|
|
)
|
|
|
|
type (
|
|
F struct {
|
|
Path string
|
|
Mode os.FileMode
|
|
IsDir bool // IsDir ensures the resource is a directory.
|
|
Content []byte
|
|
Owner string
|
|
Group string
|
|
}
|
|
)
|
|
|
|
// Is will check if the file resource exists.
|
|
func (f *F) Is() bool {
|
|
if f.Path == "" {
|
|
return false
|
|
}
|
|
if f.Mode == 0 {
|
|
f.Mode = DefaultPerm
|
|
}
|
|
|
|
stat := syscall.Stat_t{}
|
|
if err := syscall.Stat(f.Path, &stat); err != nil && os.IsNotExist(err) {
|
|
return false
|
|
} else if err != nil {
|
|
return false
|
|
}
|
|
mode := (os.FileMode)(stat.Mode)
|
|
if mode.IsDir() != f.IsDir {
|
|
return false
|
|
} else if f.Mode != 0 && mode.Perm() != f.Mode {
|
|
return false
|
|
}
|
|
if f.Owner != "" {
|
|
uid, err := getUID(f.Owner)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if int(stat.Uid) != uid {
|
|
return false
|
|
}
|
|
}
|
|
|
|
if f.Group != "" {
|
|
gid, err := getGID(f.Group)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if int(stat.Gid) != gid {
|
|
return false
|
|
}
|
|
}
|
|
|
|
want := sha256.New()
|
|
is := sha256.New()
|
|
file, err := os.Open(f.Path)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
defer file.Close()
|
|
l, err := io.Copy(is, file)
|
|
if l != int64(len(f.Content)) {
|
|
return false
|
|
}
|
|
want.Write(f.Content)
|
|
if bytes.Compare(want.Sum(nil), is.Sum(nil)) != 0 {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// Ensure writes the file out onto the disk.
|
|
func (f *F) Ensure() error {
|
|
if f.Path == "" {
|
|
return fmt.Errorf("path is empty")
|
|
}
|
|
if f.Mode == 0 {
|
|
f.Mode = DefaultPerm
|
|
}
|
|
if f.IsDir && len(f.Content) > 0 {
|
|
return fmt.Errorf("directory can't have content")
|
|
}
|
|
|
|
if f.IsDir {
|
|
if err := os.Mkdir(f.Path, f.Mode); err != nil && !os.IsExist(err) {
|
|
return fmt.Errorf("could not create directory '%s': %w", f.Path, err)
|
|
}
|
|
} else {
|
|
if err := ioutil.WriteFile(f.Path, f.Content, f.Mode); err != nil {
|
|
return fmt.Errorf("could not write file '%s': %w", f.Path, err)
|
|
}
|
|
}
|
|
|
|
if stat, err := os.Stat(f.Path); err != nil {
|
|
return fmt.Errorf("could not check resource '%s': %w", f.Path, err)
|
|
} else if stat.Mode() != f.Mode {
|
|
if err := os.Chmod(f.Path, os.FileMode(f.Mode)); err != nil {
|
|
return fmt.Errorf("could not set resource '%s' mode: %w", f.Path, err)
|
|
}
|
|
}
|
|
|
|
if f.Owner != "" {
|
|
uid, err := getUID(f.Owner)
|
|
if err != nil {
|
|
return fmt.Errorf("could not get user id '%s': %w", f.Owner, err)
|
|
}
|
|
if err := os.Chown(f.Path, uid, -1); err != nil {
|
|
return fmt.Errorf("could not set owner for '%s': %w", f.Path, err)
|
|
}
|
|
}
|
|
if f.Group != "" {
|
|
gid, err := getGID(f.Group)
|
|
if err != nil {
|
|
return fmt.Errorf("could not get group id '%s': %w", f.Group, err)
|
|
}
|
|
if err := os.Chown(f.Path, -1, gid); err != nil {
|
|
return fmt.Errorf("could not set group for '%s': %w", f.Path, err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getUID(name string) (int, error) {
|
|
user, err := user.Lookup(name)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("could not get user id: %w", err)
|
|
}
|
|
uid, err := strconv.Atoi(user.Uid)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("could not get user id: %w", err)
|
|
}
|
|
return uid, nil
|
|
}
|
|
|
|
func getGID(name string) (int, error) {
|
|
user, err := user.LookupGroup(name)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("could not get user id: %w", err)
|
|
}
|
|
gid, err := strconv.Atoi(user.Gid)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("could not get user id: %w", err)
|
|
}
|
|
return gid, nil
|
|
}
|