mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-10-27 19:53:41 +08:00
288 lines
6.1 KiB
Go
288 lines
6.1 KiB
Go
package plg_backend_samba
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"net/url"
|
|
"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) {
|
|
if c := SambaCache.Get(params); c != nil {
|
|
return c.(*Samba), nil
|
|
}
|
|
if strings.HasPrefix(params["host"], "smb://") == false {
|
|
params["host"] = "smb://" + params["host"]
|
|
}
|
|
if u, err := url.Parse(params["host"]); err == nil {
|
|
params["host"] = u.Host
|
|
if params["port"] == "" {
|
|
params["port"] = u.Port()
|
|
}
|
|
if params["share"] == "" {
|
|
params["share"] = strings.ReplaceAll(u.Path, "/", "")
|
|
}
|
|
if params["username"] == "" && u.User != nil {
|
|
params["username"] = u.User.Username()
|
|
}
|
|
if params["password"] == "" && u.User != nil {
|
|
params["password"], _ = u.User.Password()
|
|
}
|
|
}
|
|
if params["port"] == "" {
|
|
params["port"] = "445"
|
|
}
|
|
|
|
host := fmt.Sprintf("%s:%s", params["host"], params["port"])
|
|
conn, err := net.DialTimeout("tcp", host, 10*time.Second)
|
|
if err != nil {
|
|
Log.Debug("plg_backend_samba::netdial host[%s] err[%s]", host, err.Error())
|
|
return nil, err
|
|
}
|
|
|
|
smb.share = make(map[string]*smb2.Share, 0)
|
|
smb.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 {
|
|
Log.Debug("plg_backend_samba::smbdial host[%s] err[%s] username[%s] domain[%s]", host, err.Error(), params["username"], params["domain"])
|
|
return nil, err
|
|
}
|
|
if params["share"] == "" {
|
|
names, err := smb.session.ListSharenames()
|
|
if err != nil {
|
|
Log.Debug("plg_backend_samba::list host[%s] err[%s]", host, err.Error())
|
|
return nil, err
|
|
}
|
|
for _, name := range names {
|
|
if strings.HasSuffix(name, "$") {
|
|
continue
|
|
}
|
|
if m, err := smb.session.Mount(name); err == nil {
|
|
smb.share[name] = m
|
|
}
|
|
}
|
|
} else {
|
|
if m, err := smb.session.Mount(params["share"]); err == nil {
|
|
smb.share[params["share"]] = m
|
|
}
|
|
}
|
|
SambaCache.Set(params, &smb)
|
|
return &smb, 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
|
|
}
|
|
}
|