mirror of
https://github.com/SagerNet/sing.git
synced 2025-04-02 03:17:37 +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"
|
||||
"time"
|
||||
|
||||
"github.com/sagernet/sing/common/service"
|
||||
"github.com/sagernet/sing/service"
|
||||
)
|
||||
|
||||
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