mirror of
https://github.com/SagerNet/sing.git
synced 2025-04-04 20:37:40 +03:00
Add filemanager api
This commit is contained in:
parent
2fc9c6028c
commit
a39dcdba79
5 changed files with 249 additions and 1 deletions
|
@ -4,7 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/sagernet/sing/common/service"
|
"github.com/sagernet/sing/service"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TimeFuncFromContext(ctx context.Context) func() time.Time {
|
func TimeFuncFromContext(ctx context.Context) func() time.Time {
|
||||||
|
|
164
service/filemanager/default.go
Normal file
164
service/filemanager/default.go
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
package filemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/common/rw"
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ Manager = (*defaultManager)(nil)
|
||||||
|
|
||||||
|
type defaultManager struct {
|
||||||
|
basePath string
|
||||||
|
tempPath string
|
||||||
|
chown bool
|
||||||
|
userID int
|
||||||
|
groupID int
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithDefault(ctx context.Context, basePath string, tempPath string, userID int, groupID int) context.Context {
|
||||||
|
chown := userID != os.Getuid() || groupID != os.Getgid()
|
||||||
|
if tempPath == "" {
|
||||||
|
tempPath = os.TempDir()
|
||||||
|
}
|
||||||
|
return service.ContextWith[Manager](ctx, &defaultManager{
|
||||||
|
basePath: basePath,
|
||||||
|
tempPath: tempPath,
|
||||||
|
chown: chown,
|
||||||
|
userID: userID,
|
||||||
|
groupID: groupID,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultManager) BasePath(name string) string {
|
||||||
|
if m.basePath == "" || strings.HasPrefix(name, "/") {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return filepath.Join(m.basePath, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultManager) OpenFile(name string, flag int, perm os.FileMode) (*os.File, error) {
|
||||||
|
name = m.BasePath(name)
|
||||||
|
willCreate := flag&os.O_CREATE != 0 && !rw.FileExists(name)
|
||||||
|
file, err := os.OpenFile(name, flag, perm)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if m.chown && willCreate {
|
||||||
|
err = file.Chown(m.userID, m.groupID)
|
||||||
|
if err != nil {
|
||||||
|
file.Close()
|
||||||
|
os.Remove(file.Name())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultManager) Create(name string) (*os.File, error) {
|
||||||
|
name = m.BasePath(name)
|
||||||
|
file, err := os.Create(name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if m.chown {
|
||||||
|
err = file.Chown(m.userID, m.groupID)
|
||||||
|
if err != nil {
|
||||||
|
file.Close()
|
||||||
|
os.Remove(file.Name())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultManager) CreateTemp(pattern string) (*os.File, error) {
|
||||||
|
file, err := os.CreateTemp(m.tempPath, pattern)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if m.chown {
|
||||||
|
err = file.Chown(m.userID, m.groupID)
|
||||||
|
if err != nil {
|
||||||
|
file.Close()
|
||||||
|
os.Remove(file.Name())
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return file, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultManager) Mkdir(path string, perm os.FileMode) error {
|
||||||
|
path = m.BasePath(path)
|
||||||
|
err := os.Mkdir(path, perm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if m.chown {
|
||||||
|
err = os.Chown(path, m.userID, m.groupID)
|
||||||
|
if err != nil {
|
||||||
|
os.Remove(path)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *defaultManager) MkdirAll(path string, perm os.FileMode) error {
|
||||||
|
path = m.BasePath(path)
|
||||||
|
dir, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
if dir.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &os.PathError{Op: "mkdir", Path: path, Err: syscall.ENOTDIR}
|
||||||
|
}
|
||||||
|
|
||||||
|
i := len(path)
|
||||||
|
for i > 0 && os.IsPathSeparator(path[i-1]) {
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
|
||||||
|
j := i
|
||||||
|
for j > 0 && !os.IsPathSeparator(path[j-1]) {
|
||||||
|
j--
|
||||||
|
}
|
||||||
|
|
||||||
|
if j > 1 {
|
||||||
|
err = m.MkdirAll(fixRootDirectory(path[:j-1]), perm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.Mkdir(path, perm)
|
||||||
|
if err != nil {
|
||||||
|
dir, err1 := os.Lstat(path)
|
||||||
|
if err1 == nil && dir.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if m.chown {
|
||||||
|
err = os.Chown(path, m.userID, m.groupID)
|
||||||
|
if err != nil {
|
||||||
|
os.Remove(path)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fixRootDirectory(p string) string {
|
||||||
|
if len(p) == len(`\\?\c:`) {
|
||||||
|
if os.IsPathSeparator(p[0]) && os.IsPathSeparator(p[1]) && p[2] == '?' && os.IsPathSeparator(p[3]) && p[5] == ':' {
|
||||||
|
return p + `\`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p
|
||||||
|
}
|
84
service/filemanager/manager.go
Normal file
84
service/filemanager/manager.go
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
package filemanager
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sagernet/sing/service"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Manager interface {
|
||||||
|
BasePath(name string) string
|
||||||
|
OpenFile(name string, flag int, perm os.FileMode) (*os.File, error)
|
||||||
|
Create(name string) (*os.File, error)
|
||||||
|
CreateTemp(pattern string) (*os.File, error)
|
||||||
|
Mkdir(path string, perm os.FileMode) error
|
||||||
|
MkdirAll(path string, perm os.FileMode) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func BasePath(ctx context.Context, name string) string {
|
||||||
|
manager := service.FromContext[Manager](ctx)
|
||||||
|
if manager == nil {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
return manager.BasePath(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenFile(ctx context.Context, name string, flag int, perm os.FileMode) (*os.File, error) {
|
||||||
|
manager := service.FromContext[Manager](ctx)
|
||||||
|
if manager == nil {
|
||||||
|
return os.OpenFile(name, flag, perm)
|
||||||
|
}
|
||||||
|
return manager.OpenFile(name, flag, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Create(ctx context.Context, name string) (*os.File, error) {
|
||||||
|
manager := service.FromContext[Manager](ctx)
|
||||||
|
if manager == nil {
|
||||||
|
return os.Create(name)
|
||||||
|
}
|
||||||
|
return manager.Create(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateTemp(ctx context.Context, pattern string) (*os.File, error) {
|
||||||
|
manager := service.FromContext[Manager](ctx)
|
||||||
|
if manager == nil {
|
||||||
|
return os.CreateTemp("", pattern)
|
||||||
|
}
|
||||||
|
return manager.CreateTemp(pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Mkdir(ctx context.Context, path string, perm os.FileMode) error {
|
||||||
|
manager := service.FromContext[Manager](ctx)
|
||||||
|
if manager == nil {
|
||||||
|
return os.Mkdir(path, perm)
|
||||||
|
}
|
||||||
|
return manager.Mkdir(path, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MkdirAll(ctx context.Context, path string, perm os.FileMode) error {
|
||||||
|
manager := service.FromContext[Manager](ctx)
|
||||||
|
if manager == nil {
|
||||||
|
return os.MkdirAll(path, perm)
|
||||||
|
}
|
||||||
|
return manager.MkdirAll(path, perm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteFile(ctx context.Context, name string, data []byte, perm os.FileMode) error {
|
||||||
|
manager := service.FromContext[Manager](ctx)
|
||||||
|
if manager == nil {
|
||||||
|
return os.WriteFile(name, data, perm)
|
||||||
|
}
|
||||||
|
file, err := manager.OpenFile(name, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = file.Write(data)
|
||||||
|
if err1 := file.Close(); err1 != nil && err == nil {
|
||||||
|
err = err1
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue