mirror of
https://github.com/harivansh-afk/betterNAS.git
synced 2026-04-17 19:03:53 +00:00
Keep the NAS-side runtime bounded to the configured export path, make WebDAV and registration behavior env-driven, and add runtime coverage so the first storage loop can be verified locally. Generated with [Devin](https://cli.devin.ai/docs) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
132 lines
2.8 KiB
Go
132 lines
2.8 KiB
Go
package nodeagent
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"golang.org/x/net/webdav"
|
|
)
|
|
|
|
type exportFileSystem struct {
|
|
root *os.Root
|
|
}
|
|
|
|
var _ webdav.FileSystem = (*exportFileSystem)(nil)
|
|
|
|
func newExportFileSystem(rootPath string) (*exportFileSystem, error) {
|
|
root, err := os.OpenRoot(rootPath)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("open export root %s: %w", rootPath, err)
|
|
}
|
|
|
|
return &exportFileSystem{
|
|
root: root,
|
|
}, nil
|
|
}
|
|
|
|
func (f *exportFileSystem) Close() error {
|
|
if f.root == nil {
|
|
return nil
|
|
}
|
|
|
|
err := f.root.Close()
|
|
f.root = nil
|
|
return err
|
|
}
|
|
|
|
func (f *exportFileSystem) Mkdir(_ context.Context, name string, perm os.FileMode) error {
|
|
resolvedName, err := resolveExportName(name)
|
|
if err != nil {
|
|
return pathError("mkdir", name, err)
|
|
}
|
|
|
|
if resolvedName == "." {
|
|
return pathError("mkdir", name, os.ErrInvalid)
|
|
}
|
|
|
|
return f.root.Mkdir(resolvedName, perm)
|
|
}
|
|
|
|
func (f *exportFileSystem) OpenFile(_ context.Context, name string, flag int, perm os.FileMode) (webdav.File, error) {
|
|
resolvedName, err := resolveExportName(name)
|
|
if err != nil {
|
|
return nil, pathError("open", name, err)
|
|
}
|
|
|
|
file, err := f.root.OpenFile(resolvedName, flag, perm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return file, nil
|
|
}
|
|
|
|
func (f *exportFileSystem) RemoveAll(_ context.Context, name string) error {
|
|
resolvedName, err := resolveExportName(name)
|
|
if err != nil {
|
|
return pathError("removeall", name, err)
|
|
}
|
|
|
|
if resolvedName == "." {
|
|
return pathError("removeall", name, os.ErrInvalid)
|
|
}
|
|
|
|
return f.root.RemoveAll(resolvedName)
|
|
}
|
|
|
|
func (f *exportFileSystem) Rename(_ context.Context, oldName, newName string) error {
|
|
resolvedOldName, err := resolveExportName(oldName)
|
|
if err != nil {
|
|
return pathError("rename", oldName, err)
|
|
}
|
|
|
|
resolvedNewName, err := resolveExportName(newName)
|
|
if err != nil {
|
|
return pathError("rename", newName, err)
|
|
}
|
|
|
|
if resolvedOldName == "." || resolvedNewName == "." {
|
|
return pathError("rename", oldName, os.ErrInvalid)
|
|
}
|
|
|
|
return f.root.Rename(resolvedOldName, resolvedNewName)
|
|
}
|
|
|
|
func (f *exportFileSystem) Stat(_ context.Context, name string) (os.FileInfo, error) {
|
|
resolvedName, err := resolveExportName(name)
|
|
if err != nil {
|
|
return nil, pathError("stat", name, err)
|
|
}
|
|
|
|
return f.root.Stat(resolvedName)
|
|
}
|
|
|
|
func resolveExportName(name string) (string, error) {
|
|
if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) {
|
|
return "", os.ErrNotExist
|
|
}
|
|
|
|
if strings.Contains(name, "\x00") {
|
|
return "", os.ErrNotExist
|
|
}
|
|
|
|
cleanedName := path.Clean("/" + name)
|
|
cleanedName = strings.TrimPrefix(cleanedName, "/")
|
|
if cleanedName == "" {
|
|
return ".", nil
|
|
}
|
|
|
|
return filepath.FromSlash(cleanedName), nil
|
|
}
|
|
|
|
func pathError(op, path string, err error) error {
|
|
return &os.PathError{
|
|
Op: op,
|
|
Path: path,
|
|
Err: err,
|
|
}
|
|
}
|