Files
2023-05-12 00:59:17 +10:00

275 lines
5.3 KiB
Go

package plg_backend_samba
import (
"fmt"
"io"
"net"
"os"
"strings"
"time"
"github.com/hirochachacha/go-smb2"
. "github.com/mickael-kerjean/filestash/server/common"
)
var SambaCache AppCache
func init() {
Backend.Register("samba", Samba{})
SambaCache = NewAppCache(30)
SambaCache.OnEvict(func(key string, value interface{}) {
smb := value.(*Samba)
for key, _ := range smb.share {
if err := smb.share[key].Umount(); err != nil {
Log.Warning("samba: error unmounting share: %v", err)
}
}
if err := smb.session.Logoff(); err != nil {
Log.Warning("samba: error logging out: %v", err)
}
})
}
type Samba struct {
session *smb2.Session
share map[string]*smb2.Share
}
func (smb Samba) Init(params map[string]string, app *App) (IBackend, error) {
c := SambaCache.Get(params)
if c != nil {
return c.(*Samba), nil
}
conn, err := net.DialTimeout(
"tcp",
fmt.Sprintf(
"%s:%s",
params["host"],
func() string {
if params["port"] == "" {
return "445"
}
return params["port"]
}(),
),
10*time.Second,
)
if err != nil {
return nil, err
}
s := &Samba{nil, make(map[string]*smb2.Share, 0)}
s.session, err = (&smb2.Dialer{
Initiator: &smb2.NTLMInitiator{
User: func() string {
if params["username"] == "" {
return "Guest"
}
return params["username"]
}(),
Password: params["password"],
Domain: params["domain"],
},
}).Dial(conn)
if err != nil {
return nil, err
}
if params["share"] == "" {
names, err := s.session.ListSharenames()
if err != nil {
return nil, err
}
for _, name := range names {
if strings.HasSuffix(name, "$") {
continue
}
if m, err := s.session.Mount(name); err == nil {
s.share[name] = m
}
}
} else {
if m, err := s.session.Mount(params["share"]); err == nil {
s.share[params["share"]] = m
}
}
SambaCache.Set(params, s)
return s, nil
}
func (smb Samba) LoginForm() Form {
return Form{
Elmnts: []FormElement{
{
Name: "type",
Type: "hidden",
Value: "samba",
},
{
Name: "host",
Type: "text",
Placeholder: "Hostname",
},
{
Name: "username",
Type: "text",
Placeholder: "Username",
},
{
Name: "password",
Type: "password",
Placeholder: "Password",
},
{
Name: "advanced",
Type: "enable",
Placeholder: "Advanced",
Target: []string{"samba_port", "samba_path", "samba_domain", "samba_share"},
},
{
Id: "samba_path",
Name: "path",
Type: "text",
Placeholder: "Path",
},
{
Id: "samba_port",
Name: "port",
Type: "number",
Placeholder: "Port - eg: 445",
},
{
Id: "samba_domain",
Name: "domain",
Type: "text",
Placeholder: "Domain",
},
{
Id: "samba_share",
Name: "share",
Type: "text",
Placeholder: "Share Name",
},
},
}
}
func (smb Samba) Ls(path string) ([]os.FileInfo, error) {
if path == "/" {
f := make([]os.FileInfo, 0)
for key, _ := range smb.share {
f = append(f, File{
FName: key,
FType: "directory",
})
}
return f, nil
}
share, path, err := smb.toSambaPath(path)
if err != nil {
return nil, err
}
dir, err := share.Open(path)
if err != nil {
return nil, fromSambaErr(err)
}
defer dir.Close()
fs, err := dir.Readdir(-1)
return fs, fromSambaErr(err)
}
func (smb Samba) Cat(path string) (io.ReadCloser, error) {
share, path, err := smb.toSambaPath(path)
if err != nil {
return nil, err
}
f, err := share.Open(path)
return f, fromSambaErr(err)
}
func (smb Samba) Mkdir(path string) error {
share, path, err := smb.toSambaPath(path)
if err != nil {
return err
}
return fromSambaErr(share.Mkdir(path, os.ModeDir))
}
func (smb Samba) Rm(path string) error {
share, path, err := smb.toSambaPath(path)
if err != nil {
return err
}
return fromSambaErr(share.RemoveAll(path))
}
func (smb Samba) Mv(from, to string) error {
fromShare, fromPath, err := smb.toSambaPath(from)
if err != nil {
return err
}
toShare, toPath, err := smb.toSambaPath(to)
if err != nil {
return err
}
if fromShare != toShare {
return ErrNotImplemented
}
return fromSambaErr(fromShare.Rename(fromPath, toPath))
}
func (smb Samba) Save(path string, content io.Reader) error {
share, path, err := smb.toSambaPath(path)
if err != nil {
return err
}
f, err := share.Create(path)
if err != nil {
return fromSambaErr(err)
}
if _, err = io.Copy(f, content); err != nil {
f.Close()
return fromSambaErr(err)
}
return f.Close()
}
func (smb Samba) Touch(path string) error {
share, path, err := smb.toSambaPath(path)
if err != nil {
return err
}
f, err := share.Create(path)
if err != nil {
return fromSambaErr(err)
}
return fromSambaErr(f.Close())
}
func (smb Samba) toSambaPath(path string) (*smb2.Share, string, error) {
p := strings.Split(strings.Trim(path, "/"), "/")
if len(p) == 0 {
return nil, "", ErrNotAllowed
}
sharename := p[0]
oPath := strings.TrimLeft(strings.Join(p[1:], "\\"), "\\")
if smb.share[sharename] == nil {
return nil, "", ErrNotFound
}
return smb.share[sharename], oPath, nil
}
func fromSambaErr(err error) error {
switch {
case os.IsPermission(err):
return ErrPermissionDenied
case os.IsNotExist(err):
return ErrNotFound
default:
return err
}
}