Something went wrong. Try again.
forked from tailscale/tailscale
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142// Copyright (c) Tailscale Inc & contributors// SPDX-License-Identifier: BSD-3-Clause
// Package driveimpl provides an implementation of package drive.package driveimpl
import ( "log" "net" "net/http" "sync" "time"
"tailscale.com/drive" "tailscale.com/drive/driveimpl/compositedav" "tailscale.com/drive/driveimpl/dirfs" "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 { return newFileSystemForLocal(logf, &compositedav.StatCache{TTL: statCacheTTL})}
func newFileSystemForLocal(logf logger.Logf, statCache *compositedav.StatCache) *FileSystemForLocal { if logf == nil { logf = log.Printf } fs := &FileSystemForLocal{ logf: logf, h: &compositedav.Handler{ Logf: logf, StatCache: statCache, }, listener: newConnListener(), } fs.startServing() return fs}
// FileSystemForLocal is the Taildrive filesystem exposed to local clients. It// provides a unified WebDAV interface to remote Taildrive shares on other nodes.type FileSystemForLocal struct { logf logger.Logf h *compositedav.Handler listener *connListener
// sourceMu guards source and cachedGen. It also serializes the // rebuild path so concurrent requests don't race to replace // children with stale data. sourceMu sync.Mutex source drive.RemoteSource cachedGen uint64 haveGen bool // true once cachedGen reflects an actual source.Generation call}
func (s *FileSystemForLocal) startServing() { hs := &http.Server{Handler: http.HandlerFunc(s.serveHTTP)} go func() { err := hs.Serve(s.listener) if err != nil { // TODO(oxtoacart): should we panic or something different here? log.Printf("serve: %v", err) } }()}
// serveHTTP refreshes the underlying compositedav children from the// remote source if its generation has changed, then delegates to the// composite handler. The refresh path is skipped entirely when the// generation is unchanged, which is the common case.func (s *FileSystemForLocal) serveHTTP(w http.ResponseWriter, r *http.Request) { s.refresh() s.h.ServeHTTP(w, r)}
// refresh rebuilds the compositedav children from the current source// if its generation has changed since the last refresh. It is a no-op// when no source is set or when the generation matches the cached// value.func (s *FileSystemForLocal) refresh() { s.sourceMu.Lock() defer s.sourceMu.Unlock()
source := s.source if source == nil { return } gen := source.Generation() if s.haveGen && gen == s.cachedGen { return }
transport := source.Transport() var children []*compositedav.Child for remote := range source.Remotes() { children = append(children, &compositedav.Child{ Child: &dirfs.Child{ Name: remote.Name, Available: remote.Available, }, BaseURL: func() (string, error) { return remote.URL(), nil }, Transport: transport, }) } s.h.SetChildren(source.Domain(), children...) s.cachedGen = gen s.haveGen = true}
// HandleConn handles connections from local WebDAV clientsfunc (s *FileSystemForLocal) HandleConn(conn net.Conn, remoteAddr net.Addr) error { return s.listener.HandleConn(conn, remoteAddr)}
// SetRemoteSource sets the source from which the filesystem reads the// current set of remotes. It replaces any previously set source and// forces a rebuild on the next incoming request.func (s *FileSystemForLocal) SetRemoteSource(source drive.RemoteSource) { s.sourceMu.Lock() s.source = source s.cachedGen = 0 s.haveGen = false s.sourceMu.Unlock()}
// Close() stops serving the WebDAV contentfunc (s *FileSystemForLocal) Close() error { err := s.listener.Close() s.h.Close() return err}