// Copyright (c) Tailscale Inc & AUTHORS // SPDX-License-Identifier: BSD-3-Clause // Package tailfsimpl provides an implementation of package tailfs. package tailfsimpl import ( "log" "net" "net/http" "time" "tailscale.com/tailfs" "tailscale.com/tailfs/tailfsimpl/compositedav" "tailscale.com/tailfs/tailfsimpl/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 { if logf == nil { logf = log.Printf } fs := &FileSystemForLocal{ logf: logf, h: &compositedav.Handler{ Logf: logf, StatCache: &compositedav.StatCache{TTL: statCacheTTL}, }, listener: newConnListener(), } fs.startServing() return fs } // FileSystemForLocal is the TailFS filesystem exposed to local clients. It // provides a unified WebDAV interface to remote TailFS shares on other nodes. type FileSystemForLocal struct { logf logger.Logf h *compositedav.Handler listener *connListener } func (s *FileSystemForLocal) startServing() { hs := &http.Server{Handler: s.h} 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. func (s *FileSystemForLocal) SetRemotes(domain string, remotes []*tailfs.Remote, transport http.RoundTripper) { children := make([]*compositedav.Child, 0, len(remotes)) for _, remote := range remotes { children = append(children, &compositedav.Child{ Child: &dirfs.Child{ Name: remote.Name, Available: remote.Available, }, BaseURL: remote.URL, Transport: transport, }) } s.h.SetChildren(domain, children...) } // Close() stops serving the WebDAV content func (s *FileSystemForLocal) Close() error { err := s.listener.Close() s.h.Close() return err }