2024-02-09 17:26:43 +00:00
|
|
|
// Copyright (c) Tailscale Inc & AUTHORS
|
|
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
|
2024-04-02 21:32:30 +01:00
|
|
|
// Package driveimpl provides an implementation of package drive.
|
|
|
|
package driveimpl
|
2024-02-09 17:26:43 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"log"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"time"
|
|
|
|
|
2024-04-02 21:32:30 +01:00
|
|
|
"tailscale.com/drive"
|
|
|
|
"tailscale.com/drive/driveimpl/compositedav"
|
|
|
|
"tailscale.com/drive/driveimpl/dirfs"
|
2024-02-09 17:26:43 +00:00
|
|
|
"tailscale.com/types/logger"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
// statCacheTTL causes the local WebDAV proxy to cache file metadata to
|
|
|
|
// avoid excessive network roundtrips. This is similar to the
|
|
|
|
// DirectoryCacheLifetime setting of Windows' built-in SMB client,
|
|
|
|
// see https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-7/ff686200(v=ws.10)
|
|
|
|
statCacheTTL = 10 * time.Second
|
|
|
|
)
|
|
|
|
|
|
|
|
// NewFileSystemForLocal starts serving a filesystem for local clients.
|
|
|
|
// Inbound connections must be handed to HandleConn.
|
|
|
|
func NewFileSystemForLocal(logf logger.Logf) *FileSystemForLocal {
|
|
|
|
if logf == nil {
|
|
|
|
logf = log.Printf
|
|
|
|
}
|
|
|
|
fs := &FileSystemForLocal{
|
2024-02-21 12:40:12 +00:00
|
|
|
logf: logf,
|
|
|
|
h: &compositedav.Handler{
|
|
|
|
Logf: logf,
|
|
|
|
StatCache: &compositedav.StatCache{TTL: statCacheTTL},
|
|
|
|
},
|
2024-02-09 17:26:43 +00:00
|
|
|
listener: newConnListener(),
|
|
|
|
}
|
|
|
|
fs.startServing()
|
|
|
|
return fs
|
|
|
|
}
|
|
|
|
|
2024-04-03 18:09:58 +01:00
|
|
|
// FileSystemForLocal is the Taildrive filesystem exposed to local clients. It
|
|
|
|
// provides a unified WebDAV interface to remote Taildrive shares on other nodes.
|
2024-02-09 17:26:43 +00:00
|
|
|
type FileSystemForLocal struct {
|
|
|
|
logf logger.Logf
|
2024-02-21 12:40:12 +00:00
|
|
|
h *compositedav.Handler
|
2024-02-09 17:26:43 +00:00
|
|
|
listener *connListener
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *FileSystemForLocal) startServing() {
|
2024-02-21 12:40:12 +00:00
|
|
|
hs := &http.Server{Handler: s.h}
|
2024-02-09 17:26:43 +00:00
|
|
|
go func() {
|
|
|
|
err := hs.Serve(s.listener)
|
|
|
|
if err != nil {
|
|
|
|
// TODO(oxtoacart): should we panic or something different here?
|
|
|
|
log.Printf("serve: %v", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
|
|
|
// HandleConn handles connections from local WebDAV clients
|
|
|
|
func (s *FileSystemForLocal) HandleConn(conn net.Conn, remoteAddr net.Addr) error {
|
|
|
|
return s.listener.HandleConn(conn, remoteAddr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetRemotes sets the complete set of remotes on the given tailnet domain
|
|
|
|
// using a map of name -> url. If transport is specified, that transport
|
|
|
|
// will be used to connect to these remotes.
|
2024-04-02 21:32:30 +01:00
|
|
|
func (s *FileSystemForLocal) SetRemotes(domain string, remotes []*drive.Remote, transport http.RoundTripper) {
|
2024-02-21 12:40:12 +00:00
|
|
|
children := make([]*compositedav.Child, 0, len(remotes))
|
2024-02-09 17:26:43 +00:00
|
|
|
for _, remote := range remotes {
|
2024-02-21 12:40:12 +00:00
|
|
|
children = append(children, &compositedav.Child{
|
|
|
|
Child: &dirfs.Child{
|
|
|
|
Name: remote.Name,
|
|
|
|
Available: remote.Available,
|
|
|
|
},
|
|
|
|
BaseURL: remote.URL,
|
|
|
|
Transport: transport,
|
2024-02-09 17:26:43 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2024-02-21 12:40:12 +00:00
|
|
|
s.h.SetChildren(domain, children...)
|
2024-02-09 17:26:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Close() stops serving the WebDAV content
|
|
|
|
func (s *FileSystemForLocal) Close() error {
|
2024-02-21 12:40:12 +00:00
|
|
|
err := s.listener.Close()
|
|
|
|
s.h.Close()
|
|
|
|
return err
|
2024-02-09 17:26:43 +00:00
|
|
|
}
|