github.com/puellanivis/breton@v0.2.16/lib/files/sftpfiles/sftpfiles.go (about)

     1  package sftpfiles
     2  
     3  import (
     4  	"context"
     5  	"net/url"
     6  	"os"
     7  	"path/filepath"
     8  	"sync"
     9  
    10  	"github.com/puellanivis/breton/lib/files"
    11  	"github.com/puellanivis/breton/lib/os/user"
    12  
    13  	"golang.org/x/crypto/ssh"
    14  	"golang.org/x/crypto/ssh/knownhosts"
    15  )
    16  
    17  type filesystem struct {
    18  	once sync.Once
    19  
    20  	agent      *Agent
    21  	auths      []ssh.AuthMethod
    22  	knownhosts ssh.HostKeyCallback
    23  
    24  	mu    sync.Mutex
    25  	hosts map[string]*Host
    26  }
    27  
    28  func (fs *filesystem) lazyInit() {
    29  	if agent, err := GetAgent(); err == nil && agent != nil {
    30  		fs.agent = agent
    31  		fs.auths = append(fs.auths, ssh.PublicKeysCallback(agent.Signers))
    32  	}
    33  
    34  	if home, err := user.CurrentHomeDir(); err == nil {
    35  		filename := filepath.Join(home, ".ssh", "known_hosts")
    36  
    37  		if cb, err := knownhosts.New(filename); err == nil {
    38  			fs.knownhosts = cb
    39  		}
    40  	}
    41  }
    42  
    43  func init() {
    44  	fs := &filesystem{
    45  		hosts: make(map[string]*Host),
    46  	}
    47  
    48  	files.RegisterScheme(fs, "sftp", "scp")
    49  }
    50  
    51  func (fs *filesystem) getHost(uri *url.URL) *Host {
    52  	fs.once.Do(fs.lazyInit)
    53  
    54  	h := NewHost(uri)
    55  
    56  	fs.mu.Lock()
    57  	defer fs.mu.Unlock()
    58  
    59  	key := h.Name()
    60  
    61  	if h := fs.hosts[key]; h != nil {
    62  		return h
    63  	}
    64  
    65  	_ = h.addAuths(fs.auths...)
    66  	_, _ = h.SetHostKeyCallback(fs.knownhosts, nil)
    67  
    68  	fs.hosts[key] = h
    69  
    70  	return h
    71  }
    72  
    73  func (fs *filesystem) List(ctx context.Context, uri *url.URL) ([]os.FileInfo, error) {
    74  	h := fs.getHost(uri)
    75  
    76  	cl, err := h.Connect()
    77  	if err != nil {
    78  		return nil, files.PathError("connect", h.Name(), err)
    79  	}
    80  
    81  	fi, err := cl.ReadDir(uri.Path)
    82  	if err != nil {
    83  		fixURL := *uri
    84  		fixURL.Host = h.uri.Host
    85  		fixURL.User = h.uri.User
    86  
    87  		return nil, files.PathError("readdir", fixURL.String(), err)
    88  	}
    89  
    90  	return fi, nil
    91  }