Files
RenewCMS/internal/infrastructure/update/noFollowFilesystem_linux.go
T

164 lines
3.6 KiB
Go

//go:build linux
package update
import (
domain "RenewCMS/internal/domain/update"
"errors"
"io"
"io/fs"
"os"
"path/filepath"
"syscall"
)
const (
privateTempDirPrefix = "renewcms-update-"
readNoFollowFlags = os.O_RDONLY | syscall.O_NOFOLLOW | syscall.O_NONBLOCK
createNoFollowFlags = os.O_WRONLY | os.O_CREATE | os.O_EXCL | syscall.O_NOFOLLOW
directoryFlags = os.O_RDONLY | syscall.O_NOFOLLOW | syscall.O_DIRECTORY
)
var errNotRegularFile = errors.New("not a regular file")
type NoFollowFilesystem struct{}
var _ domain.HelperFilesystem = &NoFollowFilesystem{}
func NewNoFollowFilesystem() *NoFollowFilesystem {
return &NoFollowFilesystem{}
}
func (f *NoFollowFilesystem) Lstat(path string) (domain.FileInfo, error) {
info, err := os.Lstat(path)
if err != nil {
return domain.FileInfo{}, err
}
mode := info.Mode()
return domain.FileInfo{
IsSymlink: mode&fs.ModeSymlink != 0,
IsDir: mode.IsDir(),
IsRegular: mode.IsRegular(),
}, nil
}
func (f *NoFollowFilesystem) ReadFile(path string) ([]byte, error) {
file, err := openRegularNoFollow(path)
if err != nil {
return nil, err
}
defer file.Close()
return io.ReadAll(file)
}
func (f *NoFollowFilesystem) CreateExclusive(path string, content []byte, mode uint32) error {
file, err := os.OpenFile(path, createNoFollowFlags, fs.FileMode(mode))
if err != nil {
return err
}
if _, err := file.Write(content); err != nil {
file.Close()
return err
}
return finishWrite(file, mode)
}
func (f *NoFollowFilesystem) Copy(source string, destination string, mode uint32) error {
input, err := openRegularNoFollow(source)
if err != nil {
return err
}
defer input.Close()
output, err := os.OpenFile(destination, createNoFollowFlags, fs.FileMode(mode))
if err != nil {
return err
}
if _, err := io.Copy(output, input); err != nil {
output.Close()
return err
}
return finishWrite(output, mode)
}
func (f *NoFollowFilesystem) Rename(oldPath string, newPath string) error {
return os.Rename(oldPath, newPath)
}
func (f *NoFollowFilesystem) Remove(path string) error {
return os.Remove(path)
}
func (f *NoFollowFilesystem) RemoveAll(path string) error {
info, err := os.Lstat(path)
if errors.Is(err, fs.ErrNotExist) {
return nil
}
if err != nil {
return err
}
if !info.IsDir() {
return os.Remove(path)
}
names, err := readDirectoryNames(path)
if err != nil {
return err
}
for _, name := range names {
if err := f.RemoveAll(filepath.Join(path, name)); err != nil {
return err
}
}
return os.Remove(path)
}
func (f *NoFollowFilesystem) Chmod(path string, mode uint32) error {
file, err := os.OpenFile(path, readNoFollowFlags, 0)
if err != nil {
return err
}
defer file.Close()
return file.Chmod(fs.FileMode(mode))
}
func (f *NoFollowFilesystem) MakePrivateTempDir() (string, error) {
return os.MkdirTemp("", privateTempDirPrefix)
}
func openRegularNoFollow(path string) (*os.File, error) {
file, err := os.OpenFile(path, readNoFollowFlags, 0)
if err != nil {
return nil, err
}
info, err := file.Stat()
if err != nil {
file.Close()
return nil, err
}
if !info.Mode().IsRegular() {
file.Close()
return nil, errNotRegularFile
}
return file, nil
}
func finishWrite(file *os.File, mode uint32) error {
if err := file.Chmod(fs.FileMode(mode)); err != nil {
file.Close()
return err
}
if err := file.Sync(); err != nil {
file.Close()
return err
}
return file.Close()
}
func readDirectoryNames(path string) ([]string, error) {
directory, err := os.OpenFile(path, directoryFlags, 0)
if err != nil {
return nil, err
}
defer directory.Close()
return directory.Readdirnames(-1)
}