local_impl.go 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. // Package driveimpl provides an implementation of package drive.
  4. package driveimpl
  5. import (
  6. "log"
  7. "net"
  8. "net/http"
  9. "time"
  10. "tailscale.com/drive"
  11. "tailscale.com/drive/driveimpl/compositedav"
  12. "tailscale.com/drive/driveimpl/dirfs"
  13. "tailscale.com/types/logger"
  14. )
  15. const (
  16. // statCacheTTL causes the local WebDAV proxy to cache file metadata to
  17. // avoid excessive network roundtrips. This is similar to the
  18. // DirectoryCacheLifetime setting of Windows' built-in SMB client,
  19. // see https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-7/ff686200(v=ws.10)
  20. statCacheTTL = 10 * time.Second
  21. )
  22. // NewFileSystemForLocal starts serving a filesystem for local clients.
  23. // Inbound connections must be handed to HandleConn.
  24. func NewFileSystemForLocal(logf logger.Logf) *FileSystemForLocal {
  25. return newFileSystemForLocal(logf, &compositedav.StatCache{TTL: statCacheTTL})
  26. }
  27. func newFileSystemForLocal(logf logger.Logf, statCache *compositedav.StatCache) *FileSystemForLocal {
  28. if logf == nil {
  29. logf = log.Printf
  30. }
  31. fs := &FileSystemForLocal{
  32. logf: logf,
  33. h: &compositedav.Handler{
  34. Logf: logf,
  35. StatCache: statCache,
  36. },
  37. listener: newConnListener(),
  38. }
  39. fs.startServing()
  40. return fs
  41. }
  42. // FileSystemForLocal is the Taildrive filesystem exposed to local clients. It
  43. // provides a unified WebDAV interface to remote Taildrive shares on other nodes.
  44. type FileSystemForLocal struct {
  45. logf logger.Logf
  46. h *compositedav.Handler
  47. listener *connListener
  48. }
  49. func (s *FileSystemForLocal) startServing() {
  50. hs := &http.Server{Handler: s.h}
  51. go func() {
  52. err := hs.Serve(s.listener)
  53. if err != nil {
  54. // TODO(oxtoacart): should we panic or something different here?
  55. log.Printf("serve: %v", err)
  56. }
  57. }()
  58. }
  59. // HandleConn handles connections from local WebDAV clients
  60. func (s *FileSystemForLocal) HandleConn(conn net.Conn, remoteAddr net.Addr) error {
  61. return s.listener.HandleConn(conn, remoteAddr)
  62. }
  63. // SetRemotes sets the complete set of remotes on the given tailnet domain
  64. // using a map of name -> url. If transport is specified, that transport
  65. // will be used to connect to these remotes.
  66. func (s *FileSystemForLocal) SetRemotes(domain string, remotes []*drive.Remote, transport http.RoundTripper) {
  67. children := make([]*compositedav.Child, 0, len(remotes))
  68. for _, remote := range remotes {
  69. children = append(children, &compositedav.Child{
  70. Child: &dirfs.Child{
  71. Name: remote.Name,
  72. Available: remote.Available,
  73. },
  74. BaseURL: func() (string, error) { return remote.URL(), nil },
  75. Transport: transport,
  76. })
  77. }
  78. s.h.SetChildren(domain, children...)
  79. }
  80. // Close() stops serving the WebDAV content
  81. func (s *FileSystemForLocal) Close() error {
  82. err := s.listener.Close()
  83. s.h.Close()
  84. return err
  85. }