tailscale: update tailfs functions and vars to use drive naming (#11597)

This change updates all tailfs functions and the majority of the tailfs
variables to use the new drive naming.

Updates tailscale/corp#16827

Signed-off-by: Charlotte Brandhorst-Satzkorn <charlotte@tailscale.com>
pull/11609/head
Charlotte Brandhorst-Satzkorn 8 months ago committed by GitHub
parent 2409661a0d
commit 93618a3518
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -1418,25 +1418,25 @@ func (lc *LocalClient) CheckUpdate(ctx context.Context) (*tailcfg.ClientVersion,
return &cv, nil return &cv, nil
} }
// TailFSSetFileServerAddr instructs TailFS to use the server at addr to access // DriveSetServerAddr instructs Taildrive to use the server at addr to access
// the filesystem. This is used on platforms like Windows and MacOS to let // the filesystem. This is used on platforms like Windows and MacOS to let
// TailFS know to use the file server running in the GUI app. // Taildrive know to use the file server running in the GUI app.
func (lc *LocalClient) TailFSSetFileServerAddr(ctx context.Context, addr string) error { func (lc *LocalClient) DriveSetServerAddr(ctx context.Context, addr string) error {
_, err := lc.send(ctx, "PUT", "/localapi/v0/tailfs/fileserver-address", http.StatusCreated, strings.NewReader(addr)) _, err := lc.send(ctx, "PUT", "/localapi/v0/tailfs/fileserver-address", http.StatusCreated, strings.NewReader(addr))
return err return err
} }
// TailFSShareSet adds or updates the given share in the list of shares that // DriveShareSet adds or updates the given share in the list of shares that
// TailFS will serve to remote nodes. If a share with the same name already // Taildrive will serve to remote nodes. If a share with the same name already
// exists, the existing share is replaced/updated. // exists, the existing share is replaced/updated.
func (lc *LocalClient) TailFSShareSet(ctx context.Context, share *drive.Share) error { func (lc *LocalClient) DriveShareSet(ctx context.Context, share *drive.Share) error {
_, err := lc.send(ctx, "PUT", "/localapi/v0/tailfs/shares", http.StatusCreated, jsonBody(share)) _, err := lc.send(ctx, "PUT", "/localapi/v0/tailfs/shares", http.StatusCreated, jsonBody(share))
return err return err
} }
// TailFSShareRemove removes the share with the given name from the list of // DriveShareRemove removes the share with the given name from the list of
// shares that TailFS will serve to remote nodes. // shares that Taildrive will serve to remote nodes.
func (lc *LocalClient) TailFSShareRemove(ctx context.Context, name string) error { func (lc *LocalClient) DriveShareRemove(ctx context.Context, name string) error {
_, err := lc.send( _, err := lc.send(
ctx, ctx,
"DELETE", "DELETE",
@ -1446,8 +1446,8 @@ func (lc *LocalClient) TailFSShareRemove(ctx context.Context, name string) error
return err return err
} }
// TailFSShareRename renames the share from old to new name. // DriveShareRename renames the share from old to new name.
func (lc *LocalClient) TailFSShareRename(ctx context.Context, oldName, newName string) error { func (lc *LocalClient) DriveShareRename(ctx context.Context, oldName, newName string) error {
_, err := lc.send( _, err := lc.send(
ctx, ctx,
"POST", "POST",
@ -1457,9 +1457,9 @@ func (lc *LocalClient) TailFSShareRename(ctx context.Context, oldName, newName s
return err return err
} }
// TailFSShareList returns the list of shares that TailFS is currently serving // DriveShareList returns the list of shares that drive is currently serving
// to remote nodes. // to remote nodes.
func (lc *LocalClient) TailFSShareList(ctx context.Context) ([]*drive.Share, error) { func (lc *LocalClient) DriveShareList(ctx context.Context) ([]*drive.Share, error) {
result, err := lc.get200(ctx, "/localapi/v0/tailfs/shares") result, err := lc.get200(ctx, "/localapi/v0/tailfs/shares")
if err != nil { if err != nil {
return nil, err return nil, err

@ -829,7 +829,7 @@ func TestPrefFlagMapping(t *testing.T) {
// Handled by TS_DEBUG_FIREWALL_MODE env var, we don't want to have // Handled by TS_DEBUG_FIREWALL_MODE env var, we don't want to have
// a CLI flag for this. The Pref is used by c2n. // a CLI flag for this. The Pref is used by c2n.
continue continue
case "TailFSShares": case "DriveShares":
// Handled by the tailscale share subcommand, we don't want a CLI // Handled by the tailscale share subcommand, we don't want a CLI
// flag for this. // flag for this.
continue continue

@ -69,7 +69,7 @@ func runShareSet(ctx context.Context, args []string) error {
name, path := args[0], args[1] name, path := args[0], args[1]
err := localClient.TailFSShareSet(ctx, &drive.Share{ err := localClient.DriveShareSet(ctx, &drive.Share{
Name: name, Name: name,
Path: path, Path: path,
}) })
@ -86,7 +86,7 @@ func runShareRemove(ctx context.Context, args []string) error {
} }
name := args[0] name := args[0]
err := localClient.TailFSShareRemove(ctx, name) err := localClient.DriveShareRemove(ctx, name)
if err == nil { if err == nil {
fmt.Printf("Removed share %q\n", name) fmt.Printf("Removed share %q\n", name)
} }
@ -101,7 +101,7 @@ func runShareRename(ctx context.Context, args []string) error {
oldName := args[0] oldName := args[0]
newName := args[1] newName := args[1]
err := localClient.TailFSShareRename(ctx, oldName, newName) err := localClient.DriveShareRename(ctx, oldName, newName)
if err == nil { if err == nil {
fmt.Printf("Renamed share %q to %q\n", oldName, newName) fmt.Printf("Renamed share %q to %q\n", oldName, newName)
} }
@ -114,7 +114,7 @@ func runShareList(ctx context.Context, args []string) error {
return fmt.Errorf("usage: tailscale %v", shareListUsage) return fmt.Errorf("usage: tailscale %v", shareListUsage)
} }
shares, err := localClient.TailFSShareList(ctx) shares, err := localClient.DriveShareList(ctx)
if err != nil { if err != nil {
return err return err
} }

@ -145,7 +145,7 @@ var subCommands = map[string]*func([]string) error{
"uninstall-system-daemon": &uninstallSystemDaemon, "uninstall-system-daemon": &uninstallSystemDaemon,
"debug": &debugModeFunc, "debug": &debugModeFunc,
"be-child": &beChildFunc, "be-child": &beChildFunc,
"serve-tailfs": &serveTailFSFunc, "serve-tailfs": &serveDriveFunc,
} }
var beCLI func() // non-nil if CLI is linked in var beCLI func() // non-nil if CLI is linked in
@ -650,7 +650,7 @@ func tryEngine(logf logger.Logf, sys *tsd.System, name string) (onlyNetstack boo
Dialer: sys.Dialer.Get(), Dialer: sys.Dialer.Get(),
SetSubsystem: sys.Set, SetSubsystem: sys.Set,
ControlKnobs: sys.ControlKnobs(), ControlKnobs: sys.ControlKnobs(),
TailFSForLocal: driveimpl.NewFileSystemForLocal(logf), DriveForLocal: driveimpl.NewFileSystemForLocal(logf),
} }
onlyNetstack = name == "userspace-networking" onlyNetstack = name == "userspace-networking"
@ -753,7 +753,7 @@ func runDebugServer(mux *http.ServeMux, addr string) {
} }
func newNetstack(logf logger.Logf, sys *tsd.System) (*netstack.Impl, error) { func newNetstack(logf logger.Logf, sys *tsd.System) (*netstack.Impl, error) {
tfs, _ := sys.TailFSForLocal.GetOK() tfs, _ := sys.DriveForLocal.GetOK()
ret, err := netstack.Create(logf, ret, err := netstack.Create(logf,
sys.Tun.Get(), sys.Tun.Get(),
sys.Engine.Get(), sys.Engine.Get(),
@ -831,16 +831,16 @@ func beChild(args []string) error {
return f(args[1:]) return f(args[1:])
} }
var serveTailFSFunc = serveTailFS var serveDriveFunc = serveDrive
// serveTailFS serves one or more tailfs on localhost using the WebDAV // serveDrive serves one or more tailfs on localhost using the WebDAV
// protocol. On UNIX and MacOS tailscaled environment, tailfs spawns child // protocol. On UNIX and MacOS tailscaled environment, tailfs spawns child
// tailscaled processes in serve-tailfs mode in order to access the fliesystem // tailscaled processes in serve-tailfs mode in order to access the fliesystem
// as specific (usually unprivileged) users. // as specific (usually unprivileged) users.
// //
// serveTailFS prints the address on which it's listening to stdout so that the // serveDrive prints the address on which it's listening to stdout so that the
// parent process knows where to connect to. // parent process knows where to connect to.
func serveTailFS(args []string) error { func serveDrive(args []string) error {
if len(args) == 0 { if len(args) == 0 {
return errors.New("missing shares") return errors.New("missing shares")
} }

@ -41,7 +41,7 @@ func init() {
drive.DisallowShareAs = true drive.DisallowShareAs = true
} }
// The tests in this file simulate real-life TailFS scenarios, but without // The tests in this file simulate real-life Taildrive scenarios, but without
// going over the Tailscale network stack. // going over the Tailscale network stack.
func TestDirectoryListing(t *testing.T) { func TestDirectoryListing(t *testing.T) {
s := newSystem(t) s := newSystem(t)

@ -13,7 +13,7 @@ import (
) )
// FileServer is a standalone WebDAV server that dynamically serves up shares. // FileServer is a standalone WebDAV server that dynamically serves up shares.
// It's typically used in a separate process from the actual TailFS server to // It's typically used in a separate process from the actual Taildrive server to
// serve up files as an unprivileged user. // serve up files as an unprivileged user.
type FileServer struct { type FileServer struct {
l net.Listener l net.Listener

@ -42,8 +42,8 @@ func NewFileSystemForLocal(logf logger.Logf) *FileSystemForLocal {
return fs return fs
} }
// FileSystemForLocal is the TailFS filesystem exposed to local clients. It // FileSystemForLocal is the Taildrive filesystem exposed to local clients. It
// provides a unified WebDAV interface to remote TailFS shares on other nodes. // provides a unified WebDAV interface to remote Taildrive shares on other nodes.
type FileSystemForLocal struct { type FileSystemForLocal struct {
logf logger.Logf logf logger.Logf
h *compositedav.Handler h *compositedav.Handler

@ -44,7 +44,7 @@ func NewFileSystemForRemote(logf logger.Logf) *FileSystemForRemote {
return fs return fs
} }
// FileSystemForRemote implements tailfs.FileSystemForRemote. // FileSystemForRemote implements drive.FileSystemForRemote.
type FileSystemForRemote struct { type FileSystemForRemote struct {
logf logger.Logf logf logger.Logf
lockSystem webdav.LockSystem lockSystem webdav.LockSystem
@ -58,15 +58,15 @@ type FileSystemForRemote struct {
userServers map[string]*userServer userServers map[string]*userServer
} }
// SetFileServerAddr implements tailfs.FileSystemForRemote. // SetFileServerAddr implements drive.FileSystemForRemote.
func (s *FileSystemForRemote) SetFileServerAddr(addr string) { func (s *FileSystemForRemote) SetFileServerAddr(addr string) {
s.mu.Lock() s.mu.Lock()
s.fileServerAddr = addr s.fileServerAddr = addr
s.mu.Unlock() s.mu.Unlock()
} }
// SetShares implements tailfs.FileSystemForRemote. Shares must be sorted // SetShares implements drive.FileSystemForRemote. Shares must be sorted
// according to tailfs.CompareShares. // according to drive.CompareShares.
func (s *FileSystemForRemote) SetShares(shares []*drive.Share) { func (s *FileSystemForRemote) SetShares(shares []*drive.Share) {
userServers := make(map[string]*userServer) userServers := make(map[string]*userServer)
if drive.AllowShareAs() { if drive.AllowShareAs() {
@ -176,7 +176,7 @@ func (s *FileSystemForRemote) buildChild(share *drive.Share) *compositedav.Child
} }
} }
// ServeHTTPWithPerms implements tailfs.FileSystemForRemote. // ServeHTTPWithPerms implements drive.FileSystemForRemote.
func (s *FileSystemForRemote) ServeHTTPWithPerms(permissions drive.Permissions, w http.ResponseWriter, r *http.Request) { func (s *FileSystemForRemote) ServeHTTPWithPerms(permissions drive.Permissions, w http.ResponseWriter, r *http.Request) {
isWrite := writeMethods[r.Method] isWrite := writeMethods[r.Method]
if isWrite { if isWrite {
@ -228,7 +228,7 @@ func (s *FileSystemForRemote) closeChildren(children map[string]*compositedav.Ch
} }
} }
// Close() implements tailfs.FileSystemForRemote. // Close() implements drive.FileSystemForRemote.
func (s *FileSystemForRemote) Close() error { func (s *FileSystemForRemote) Close() error {
s.mu.Lock() s.mu.Lock()
userServers := s.userServers userServers := s.userServers

@ -1,7 +1,7 @@
// Copyright (c) Tailscale Inc & AUTHORS // Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
// Package shared contains types and functions shared by different tailfs // Package shared contains types and functions shared by different drive
// packages. // packages.
package shared package shared

@ -2,10 +2,10 @@
// SPDX-License-Identifier: BSD-3-Clause // SPDX-License-Identifier: BSD-3-Clause
// Package drive provides a filesystem that allows sharing folders between // Package drive provides a filesystem that allows sharing folders between
// Tailscale nodes using WebDAV. The actual implementation of the core drive // Tailscale nodes using WebDAV. The actual implementation of the core Taildrive
// functionality lives in package driveimpl. These packages are separated to // functionality lives in package driveimpl. These packages are separated to
// allow users of drive to refer to the interfaces without having a hard // allow users of Taildrive to refer to the interfaces without having a hard
// dependency on drive, so that programs which don't actually use drive can // dependency on Taildrive, so that programs which don't actually use Taildrive can
// avoid its transitive dependencies. // avoid its transitive dependencies.
package drive package drive
@ -14,15 +14,15 @@ import (
"net/http" "net/http"
) )
// Remote represents a remote TailFS node. // Remote represents a remote Taildrive node.
type Remote struct { type Remote struct {
Name string Name string
URL string URL string
Available func() bool Available func() bool
} }
// FileSystemForLocal is the TailFS filesystem exposed to local clients. It // FileSystemForLocal is the Taildrive filesystem exposed to local clients. It
// provides a unified WebDAV interface to remote TailFS shares on other nodes. // provides a unified WebDAV interface to remote Taildrive shares on other nodes.
type FileSystemForLocal interface { type FileSystemForLocal interface {
// HandleConn handles connections from local WebDAV clients // HandleConn handles connections from local WebDAV clients
HandleConn(conn net.Conn, remoteAddr net.Addr) error HandleConn(conn net.Conn, remoteAddr net.Addr) error

@ -22,7 +22,7 @@ func AllowShareAs() bool {
return !DisallowShareAs && doAllowShareAs() return !DisallowShareAs && doAllowShareAs()
} }
// Share configures a folder to be shared through TailFS. // Share configures a folder to be shared through drive.
type Share struct { type Share struct {
// Name is how this share appears on remote nodes. // Name is how this share appears on remote nodes.
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
@ -78,7 +78,7 @@ func CompareShares(a, b *Share) int {
return strings.Compare(a.Name, b.Name) return strings.Compare(a.Name, b.Name)
} }
// FileSystemForRemote is the TailFS filesystem exposed to remote nodes. It // FileSystemForRemote is the drive filesystem exposed to remote nodes. It
// provides a unified WebDAV interface to local directories that have been // provides a unified WebDAV interface to local directories that have been
// shared. // shared.
type FileSystemForRemote interface { type FileSystemForRemote interface {

@ -68,7 +68,7 @@ const (
NotifyInitialNetMap // if set, the first Notify message (sent immediately) will contain the current NetMap NotifyInitialNetMap // if set, the first Notify message (sent immediately) will contain the current NetMap
NotifyNoPrivateKeys // if set, private keys that would normally be sent in updates are zeroed out NotifyNoPrivateKeys // if set, private keys that would normally be sent in updates are zeroed out
NotifyInitialTailFSShares // if set, the first Notify message (sent immediately) will contain the current TailFS Shares NotifyInitialDriveShares // if set, the first Notify message (sent immediately) will contain the current Taildrive Shares
NotifyInitialOutgoingFiles // if set, the first Notify message (sent immediately) will contain the current Taildrop OutgoingFiles NotifyInitialOutgoingFiles // if set, the first Notify message (sent immediately) will contain the current Taildrop OutgoingFiles
) )
@ -130,13 +130,13 @@ type Notify struct {
// is available. // is available.
ClientVersion *tailcfg.ClientVersion `json:",omitempty"` ClientVersion *tailcfg.ClientVersion `json:",omitempty"`
// TailFSShares tracks the full set of current TailFSShares that we're // DriveShares tracks the full set of current DriveShares that we're
// publishing. Some client applications, like the MacOS and Windows clients, // publishing. Some client applications, like the MacOS and Windows clients,
// will listen for updates to this and handle serving these shares under // will listen for updates to this and handle serving these shares under
// the identity of the unprivileged user that is running the application. A // the identity of the unprivileged user that is running the application. A
// nil value here means that we're not broadcasting shares information, an // nil value here means that we're not broadcasting shares information, an
// empty value means that there are no shares. // empty value means that there are no shares.
TailFSShares views.SliceView[*drive.Share, drive.ShareView] DriveShares views.SliceView[*drive.Share, drive.ShareView]
// type is mirrored in xcode/Shared/IPN.swift // type is mirrored in xcode/Shared/IPN.swift
} }

@ -25,10 +25,10 @@ func (src *Prefs) Clone() *Prefs {
*dst = *src *dst = *src
dst.AdvertiseTags = append(src.AdvertiseTags[:0:0], src.AdvertiseTags...) dst.AdvertiseTags = append(src.AdvertiseTags[:0:0], src.AdvertiseTags...)
dst.AdvertiseRoutes = append(src.AdvertiseRoutes[:0:0], src.AdvertiseRoutes...) dst.AdvertiseRoutes = append(src.AdvertiseRoutes[:0:0], src.AdvertiseRoutes...)
if src.TailFSShares != nil { if src.DriveShares != nil {
dst.TailFSShares = make([]*drive.Share, len(src.TailFSShares)) dst.DriveShares = make([]*drive.Share, len(src.DriveShares))
for i := range dst.TailFSShares { for i := range dst.DriveShares {
dst.TailFSShares[i] = src.TailFSShares[i].Clone() dst.DriveShares[i] = src.DriveShares[i].Clone()
} }
} }
dst.Persist = src.Persist.Clone() dst.Persist = src.Persist.Clone()
@ -63,7 +63,7 @@ var _PrefsCloneNeedsRegeneration = Prefs(struct {
AppConnector AppConnectorPrefs AppConnector AppConnectorPrefs
PostureChecking bool PostureChecking bool
NetfilterKind string NetfilterKind string
TailFSShares []*drive.Share DriveShares []*drive.Share
Persist *persist.Persist Persist *persist.Persist
}{}) }{})

@ -92,8 +92,8 @@ func (v PrefsView) AutoUpdate() AutoUpdatePrefs { return v.ж.AutoUpda
func (v PrefsView) AppConnector() AppConnectorPrefs { return v.ж.AppConnector } func (v PrefsView) AppConnector() AppConnectorPrefs { return v.ж.AppConnector }
func (v PrefsView) PostureChecking() bool { return v.ж.PostureChecking } func (v PrefsView) PostureChecking() bool { return v.ж.PostureChecking }
func (v PrefsView) NetfilterKind() string { return v.ж.NetfilterKind } func (v PrefsView) NetfilterKind() string { return v.ж.NetfilterKind }
func (v PrefsView) TailFSShares() views.SliceView[*drive.Share, drive.ShareView] { func (v PrefsView) DriveShares() views.SliceView[*drive.Share, drive.ShareView] {
return views.SliceOfViews[*drive.Share, drive.ShareView](v.ж.TailFSShares) return views.SliceOfViews[*drive.Share, drive.ShareView](v.ж.DriveShares)
} }
func (v PrefsView) Persist() persist.PersistView { return v.ж.Persist.View() } func (v PrefsView) Persist() persist.PersistView { return v.ж.Persist.View() }
@ -125,7 +125,7 @@ var _PrefsViewNeedsRegeneration = Prefs(struct {
AppConnector AppConnectorPrefs AppConnector AppConnectorPrefs
PostureChecking bool PostureChecking bool
NetfilterKind string NetfilterKind string
TailFSShares []*drive.Share DriveShares []*drive.Share
Persist *persist.Persist Persist *persist.Persist
}{}) }{})

@ -19,61 +19,61 @@ import (
) )
const ( const (
// TailFSLocalPort is the port on which the TailFS listens for location // DriveLocalPort is the port on which the Taildrive listens for location
// connections on quad 100. // connections on quad 100.
TailFSLocalPort = 8080 DriveLocalPort = 8080
) )
var ( var (
shareNameRegex = regexp.MustCompile(`^[a-z0-9_\(\) ]+$`) shareNameRegex = regexp.MustCompile(`^[a-z0-9_\(\) ]+$`)
ErrTailFSNotEnabled = errors.New("TailFS not enabled") ErrDriveNotEnabled = errors.New("TailFS not enabled")
ErrInvalidShareName = errors.New("Share names may only contain the letters a-z, underscore _, parentheses (), or spaces") ErrInvalidShareName = errors.New("Share names may only contain the letters a-z, underscore _, parentheses (), or spaces")
) )
// TailFSSharingEnabled reports whether sharing to remote nodes via tailfs is // DriveSharingEnabled reports whether sharing to remote nodes via Taildrive is
// enabled. This is currently based on checking for the tailfs:share node // enabled. This is currently based on checking for the tailfs:share node
// attribute. // attribute.
func (b *LocalBackend) TailFSSharingEnabled() bool { func (b *LocalBackend) DriveSharingEnabled() bool {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
return b.tailFSSharingEnabledLocked() return b.driveSharingEnabledLocked()
} }
func (b *LocalBackend) tailFSSharingEnabledLocked() bool { func (b *LocalBackend) driveSharingEnabledLocked() bool {
return b.netMap != nil && b.netMap.SelfNode.HasCap(tailcfg.NodeAttrsTailFSShare) return b.netMap != nil && b.netMap.SelfNode.HasCap(tailcfg.NodeAttrsTailFSShare)
} }
// TailFSAccessEnabled reports whether accessing TailFS shares on remote nodes // DriveAccessEnabled reports whether accessing Taildrive shares on remote nodes
// is enabled. This is currently based on checking for the tailfs:access node // is enabled. This is currently based on checking for the tailfs:access node
// attribute. // attribute.
func (b *LocalBackend) TailFSAccessEnabled() bool { func (b *LocalBackend) DriveAccessEnabled() bool {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
return b.tailFSAccessEnabledLocked() return b.driveAccessEnabledLocked()
} }
func (b *LocalBackend) tailFSAccessEnabledLocked() bool { func (b *LocalBackend) driveAccessEnabledLocked() bool {
return b.netMap != nil && b.netMap.SelfNode.HasCap(tailcfg.NodeAttrsTailFSAccess) return b.netMap != nil && b.netMap.SelfNode.HasCap(tailcfg.NodeAttrsTailFSAccess)
} }
// TailFSSetFileServerAddr tells tailfs to use the given address for connecting // DriveSetServerAddr tells Taildrive to use the given address for connecting
// to the tailfs.FileServer that's exposing local files as an unprivileged // to the drive.FileServer that's exposing local files as an unprivileged
// user. // user.
func (b *LocalBackend) TailFSSetFileServerAddr(addr string) error { func (b *LocalBackend) DriveSetServerAddr(addr string) error {
fs, ok := b.sys.TailFSForRemote.GetOK() fs, ok := b.sys.DriveForRemote.GetOK()
if !ok { if !ok {
return ErrTailFSNotEnabled return ErrDriveNotEnabled
} }
fs.SetFileServerAddr(addr) fs.SetFileServerAddr(addr)
return nil return nil
} }
// TailFSSetShare adds the given share if no share with that name exists, or // DriveSetShare adds the given share if no share with that name exists, or
// replaces the existing share if one with the same name already exists. To // replaces the existing share if one with the same name already exists. To
// avoid potential incompatibilities across file systems, share names are // avoid potential incompatibilities across file systems, share names are
// limited to alphanumeric characters and the underscore _. // limited to alphanumeric characters and the underscore _.
func (b *LocalBackend) TailFSSetShare(share *drive.Share) error { func (b *LocalBackend) DriveSetShare(share *drive.Share) error {
var err error var err error
share.Name, err = normalizeShareName(share.Name) share.Name, err = normalizeShareName(share.Name)
if err != nil { if err != nil {
@ -81,13 +81,13 @@ func (b *LocalBackend) TailFSSetShare(share *drive.Share) error {
} }
b.mu.Lock() b.mu.Lock()
shares, err := b.tailFSSetShareLocked(share) shares, err := b.driveSetShareLocked(share)
b.mu.Unlock() b.mu.Unlock()
if err != nil { if err != nil {
return err return err
} }
b.tailFSNotifyShares(shares) b.driveNotifyShares(shares)
return nil return nil
} }
@ -108,12 +108,12 @@ func normalizeShareName(name string) (string, error) {
return name, nil return name, nil
} }
func (b *LocalBackend) tailFSSetShareLocked(share *drive.Share) (views.SliceView[*drive.Share, drive.ShareView], error) { func (b *LocalBackend) driveSetShareLocked(share *drive.Share) (views.SliceView[*drive.Share, drive.ShareView], error) {
existingShares := b.pm.prefs.TailFSShares() existingShares := b.pm.prefs.DriveShares()
fs, ok := b.sys.TailFSForRemote.GetOK() fs, ok := b.sys.DriveForRemote.GetOK()
if !ok { if !ok {
return existingShares, ErrTailFSNotEnabled return existingShares, ErrDriveNotEnabled
} }
addedShare := false addedShare := false
@ -133,23 +133,23 @@ func (b *LocalBackend) tailFSSetShareLocked(share *drive.Share) (views.SliceView
shares = append(shares, share) shares = append(shares, share)
} }
err := b.tailFSSetSharesLocked(shares) err := b.driveSetSharesLocked(shares)
if err != nil { if err != nil {
return existingShares, err return existingShares, err
} }
fs.SetShares(shares) fs.SetShares(shares)
return b.pm.prefs.TailFSShares(), nil return b.pm.prefs.DriveShares(), nil
} }
// TailFSRenameShare renames the share at old name to new name. To avoid // DriveRenameShare renames the share at old name to new name. To avoid
// potential incompatibilities across file systems, the new share name is // potential incompatibilities across file systems, the new share name is
// limited to alphanumeric characters and the underscore _. // limited to alphanumeric characters and the underscore _.
// Any of the following will result in an error. // Any of the following will result in an error.
// - no share found under old name // - no share found under old name
// - new share name contains disallowed characters // - new share name contains disallowed characters
// - share already exists under new name // - share already exists under new name
func (b *LocalBackend) TailFSRenameShare(oldName, newName string) error { func (b *LocalBackend) DriveRenameShare(oldName, newName string) error {
var err error var err error
newName, err = normalizeShareName(newName) newName, err = normalizeShareName(newName)
if err != nil { if err != nil {
@ -157,22 +157,22 @@ func (b *LocalBackend) TailFSRenameShare(oldName, newName string) error {
} }
b.mu.Lock() b.mu.Lock()
shares, err := b.tailFSRenameShareLocked(oldName, newName) shares, err := b.driveRenameShareLocked(oldName, newName)
b.mu.Unlock() b.mu.Unlock()
if err != nil { if err != nil {
return err return err
} }
b.tailFSNotifyShares(shares) b.driveNotifyShares(shares)
return nil return nil
} }
func (b *LocalBackend) tailFSRenameShareLocked(oldName, newName string) (views.SliceView[*drive.Share, drive.ShareView], error) { func (b *LocalBackend) driveRenameShareLocked(oldName, newName string) (views.SliceView[*drive.Share, drive.ShareView], error) {
existingShares := b.pm.prefs.TailFSShares() existingShares := b.pm.prefs.DriveShares()
fs, ok := b.sys.TailFSForRemote.GetOK() fs, ok := b.sys.DriveForRemote.GetOK()
if !ok { if !ok {
return existingShares, ErrTailFSNotEnabled return existingShares, ErrDriveNotEnabled
} }
found := false found := false
@ -197,18 +197,18 @@ func (b *LocalBackend) tailFSRenameShareLocked(oldName, newName string) (views.S
} }
slices.SortFunc(shares, drive.CompareShares) slices.SortFunc(shares, drive.CompareShares)
err := b.tailFSSetSharesLocked(shares) err := b.driveSetSharesLocked(shares)
if err != nil { if err != nil {
return existingShares, err return existingShares, err
} }
fs.SetShares(shares) fs.SetShares(shares)
return b.pm.prefs.TailFSShares(), nil return b.pm.prefs.DriveShares(), nil
} }
// TailFSRemoveShare removes the named share. Share names are forced to // DriveRemoveShare removes the named share. Share names are forced to
// lowercase. // lowercase.
func (b *LocalBackend) TailFSRemoveShare(name string) error { func (b *LocalBackend) DriveRemoveShare(name string) error {
// Force all share names to lowercase to avoid potential incompatibilities // Force all share names to lowercase to avoid potential incompatibilities
// with clients that don't support case-sensitive filenames. // with clients that don't support case-sensitive filenames.
var err error var err error
@ -218,22 +218,22 @@ func (b *LocalBackend) TailFSRemoveShare(name string) error {
} }
b.mu.Lock() b.mu.Lock()
shares, err := b.tailFSRemoveShareLocked(name) shares, err := b.driveRemoveShareLocked(name)
b.mu.Unlock() b.mu.Unlock()
if err != nil { if err != nil {
return err return err
} }
b.tailFSNotifyShares(shares) b.driveNotifyShares(shares)
return nil return nil
} }
func (b *LocalBackend) tailFSRemoveShareLocked(name string) (views.SliceView[*drive.Share, drive.ShareView], error) { func (b *LocalBackend) driveRemoveShareLocked(name string) (views.SliceView[*drive.Share, drive.ShareView], error) {
existingShares := b.pm.prefs.TailFSShares() existingShares := b.pm.prefs.DriveShares()
fs, ok := b.sys.TailFSForRemote.GetOK() fs, ok := b.sys.DriveForRemote.GetOK()
if !ok { if !ok {
return existingShares, ErrTailFSNotEnabled return existingShares, ErrDriveNotEnabled
} }
found := false found := false
@ -251,53 +251,53 @@ func (b *LocalBackend) tailFSRemoveShareLocked(name string) (views.SliceView[*dr
return existingShares, os.ErrNotExist return existingShares, os.ErrNotExist
} }
err := b.tailFSSetSharesLocked(shares) err := b.driveSetSharesLocked(shares)
if err != nil { if err != nil {
return existingShares, err return existingShares, err
} }
fs.SetShares(shares) fs.SetShares(shares)
return b.pm.prefs.TailFSShares(), nil return b.pm.prefs.DriveShares(), nil
} }
func (b *LocalBackend) tailFSSetSharesLocked(shares []*drive.Share) error { func (b *LocalBackend) driveSetSharesLocked(shares []*drive.Share) error {
prefs := b.pm.prefs.AsStruct() prefs := b.pm.prefs.AsStruct()
prefs.ApplyEdits(&ipn.MaskedPrefs{ prefs.ApplyEdits(&ipn.MaskedPrefs{
Prefs: ipn.Prefs{ Prefs: ipn.Prefs{
TailFSShares: shares, DriveShares: shares,
}, },
TailFSSharesSet: true, DriveSharesSet: true,
}) })
return b.pm.setPrefsLocked(prefs.View()) return b.pm.setPrefsLocked(prefs.View())
} }
// tailFSNotifyShares notifies IPN bus listeners (e.g. Mac Application process) // driveNotifyShares notifies IPN bus listeners (e.g. Mac Application process)
// about the latest list of shares. // about the latest list of shares.
func (b *LocalBackend) tailFSNotifyShares(shares views.SliceView[*drive.Share, drive.ShareView]) { func (b *LocalBackend) driveNotifyShares(shares views.SliceView[*drive.Share, drive.ShareView]) {
// Ensures shares is not nil to distinguish "no shares" from "not notifying shares" // Ensures shares is not nil to distinguish "no shares" from "not notifying shares"
if shares.IsNil() { if shares.IsNil() {
shares = views.SliceOfViews(make([]*drive.Share, 0)) shares = views.SliceOfViews(make([]*drive.Share, 0))
} }
b.send(ipn.Notify{TailFSShares: shares}) b.send(ipn.Notify{DriveShares: shares})
} }
// tailFSNotifyCurrentSharesLocked sends an ipn.Notify if the current set of // driveNotifyCurrentSharesLocked sends an ipn.Notify if the current set of
// shares has changed since the last notification. // shares has changed since the last notification.
func (b *LocalBackend) tailFSNotifyCurrentSharesLocked() { func (b *LocalBackend) driveNotifyCurrentSharesLocked() {
var shares views.SliceView[*drive.Share, drive.ShareView] var shares views.SliceView[*drive.Share, drive.ShareView]
if b.tailFSSharingEnabledLocked() { if b.driveSharingEnabledLocked() {
// Only populate shares if sharing is enabled. // Only populate shares if sharing is enabled.
shares = b.pm.prefs.TailFSShares() shares = b.pm.prefs.DriveShares()
} }
lastNotified := b.lastNotifiedTailFSShares.Load() lastNotified := b.lastNotifiedDriveShares.Load()
if lastNotified == nil || !tailFSShareViewsEqual(lastNotified, shares) { if lastNotified == nil || !driveShareViewsEqual(lastNotified, shares) {
// Do the below on a goroutine to avoid deadlocking on b.mu in b.send(). // Do the below on a goroutine to avoid deadlocking on b.mu in b.send().
go b.tailFSNotifyShares(shares) go b.driveNotifyShares(shares)
} }
} }
func tailFSShareViewsEqual(a *views.SliceView[*drive.Share, drive.ShareView], b views.SliceView[*drive.Share, drive.ShareView]) bool { func driveShareViewsEqual(a *views.SliceView[*drive.Share, drive.ShareView], b views.SliceView[*drive.Share, drive.ShareView]) bool {
if a == nil { if a == nil {
return false return false
} }
@ -315,35 +315,35 @@ func tailFSShareViewsEqual(a *views.SliceView[*drive.Share, drive.ShareView], b
return true return true
} }
// TailFSGetShares() gets the current list of TailFS shares, sorted by name. // DriveGetShares gets the current list of Taildrive shares, sorted by name.
func (b *LocalBackend) TailFSGetShares() views.SliceView[*drive.Share, drive.ShareView] { func (b *LocalBackend) DriveGetShares() views.SliceView[*drive.Share, drive.ShareView] {
b.mu.Lock() b.mu.Lock()
defer b.mu.Unlock() defer b.mu.Unlock()
return b.pm.prefs.TailFSShares() return b.pm.prefs.DriveShares()
} }
// updateTailFSPeersLocked sets all applicable peers from the netmap as tailfs // updateDrivePeersLocked sets all applicable peers from the netmap as Taildrive
// remotes. // remotes.
func (b *LocalBackend) updateTailFSPeersLocked(nm *netmap.NetworkMap) { func (b *LocalBackend) updateDrivePeersLocked(nm *netmap.NetworkMap) {
fs, ok := b.sys.TailFSForLocal.GetOK() fs, ok := b.sys.DriveForLocal.GetOK()
if !ok { if !ok {
return return
} }
var tailFSRemotes []*drive.Remote var driveRemotes []*drive.Remote
if b.tailFSAccessEnabledLocked() { if b.driveAccessEnabledLocked() {
// Only populate peers if access is enabled, otherwise leave blank. // Only populate peers if access is enabled, otherwise leave blank.
tailFSRemotes = b.tailFSRemotesFromPeers(nm) driveRemotes = b.driveRemotesFromPeers(nm)
} }
fs.SetRemotes(b.netMap.Domain, tailFSRemotes, &tailFSTransport{b: b}) fs.SetRemotes(b.netMap.Domain, driveRemotes, &driveTransport{b: b})
} }
func (b *LocalBackend) tailFSRemotesFromPeers(nm *netmap.NetworkMap) []*drive.Remote { func (b *LocalBackend) driveRemotesFromPeers(nm *netmap.NetworkMap) []*drive.Remote {
tailFSRemotes := make([]*drive.Remote, 0, len(nm.Peers)) driveRemotes := make([]*drive.Remote, 0, len(nm.Peers))
for _, p := range nm.Peers { for _, p := range nm.Peers {
// Exclude mullvad exit nodes from list of TailFS peers // Exclude mullvad exit nodes from list of Taildrive peers
// TODO(oxtoacart) - once we have a better mechanism for finding only accessible sharers // TODO(oxtoacart) - once we have a better mechanism for finding only accessible sharers
// (see below) we can remove this logic. // (see below) we can remove this logic.
if strings.HasSuffix(p.Name(), ".mullvad.ts.net.") { if strings.HasSuffix(p.Name(), ".mullvad.ts.net.") {
@ -352,7 +352,7 @@ func (b *LocalBackend) tailFSRemotesFromPeers(nm *netmap.NetworkMap) []*drive.Re
peerID := p.ID() peerID := p.ID()
url := fmt.Sprintf("%s/%s", peerAPIBase(nm, p), tailFSPrefix[1:]) url := fmt.Sprintf("%s/%s", peerAPIBase(nm, p), tailFSPrefix[1:])
tailFSRemotes = append(tailFSRemotes, &drive.Remote{ driveRemotes = append(driveRemotes, &drive.Remote{
Name: p.DisplayName(false), Name: p.DisplayName(false),
URL: url, URL: url,
Available: func() bool { Available: func() bool {
@ -381,5 +381,5 @@ func (b *LocalBackend) tailFSRemotesFromPeers(nm *netmap.NetworkMap) []*drive.Re
}, },
}) })
} }
return tailFSRemotes return driveRemotes
} }

@ -316,9 +316,9 @@ type LocalBackend struct {
// Last ClientVersion received in MapResponse, guarded by mu. // Last ClientVersion received in MapResponse, guarded by mu.
lastClientVersion *tailcfg.ClientVersion lastClientVersion *tailcfg.ClientVersion
// lastNotifiedTailFSShares keeps track of the last set of shares that we // lastNotifiedDriveShares keeps track of the last set of shares that we
// notified about. // notified about.
lastNotifiedTailFSShares atomic.Pointer[views.SliceView[*drive.Share, drive.ShareView]] lastNotifiedDriveShares atomic.Pointer[views.SliceView[*drive.Share, drive.ShareView]]
// outgoingFiles keeps track of Taildrop outgoing files keyed to their OutgoingFile.ID // outgoingFiles keeps track of Taildrop outgoing files keyed to their OutgoingFile.ID
outgoingFiles map[string]*ipn.OutgoingFile outgoingFiles map[string]*ipn.OutgoingFile
@ -442,10 +442,10 @@ func NewLocalBackend(logf logger.Logf, logID logid.PublicID, sys *tsd.System, lo
} }
} }
// initialize TailFS shares from saved state // initialize Taildrive shares from saved state
fs, ok := b.sys.TailFSForRemote.GetOK() fs, ok := b.sys.DriveForRemote.GetOK()
if ok { if ok {
currentShares := b.pm.prefs.TailFSShares() currentShares := b.pm.prefs.DriveShares()
if currentShares.Len() > 0 { if currentShares.Len() > 0 {
var shares []*drive.Share var shares []*drive.Share
for i := 0; i < currentShares.Len(); i++ { for i := 0; i < currentShares.Len(); i++ {
@ -2292,7 +2292,7 @@ func (b *LocalBackend) WatchNotifications(ctx context.Context, mask ipn.NotifyWa
b.mu.Lock() b.mu.Lock()
const initialBits = ipn.NotifyInitialState | ipn.NotifyInitialPrefs | ipn.NotifyInitialNetMap | ipn.NotifyInitialTailFSShares const initialBits = ipn.NotifyInitialState | ipn.NotifyInitialPrefs | ipn.NotifyInitialNetMap | ipn.NotifyInitialDriveShares
if mask&initialBits != 0 { if mask&initialBits != 0 {
ini = &ipn.Notify{Version: version.Long()} ini = &ipn.Notify{Version: version.Long()}
if mask&ipn.NotifyInitialState != 0 { if mask&ipn.NotifyInitialState != 0 {
@ -2308,8 +2308,8 @@ func (b *LocalBackend) WatchNotifications(ctx context.Context, mask ipn.NotifyWa
if mask&ipn.NotifyInitialNetMap != 0 { if mask&ipn.NotifyInitialNetMap != 0 {
ini.NetMap = b.netMap ini.NetMap = b.netMap
} }
if mask&ipn.NotifyInitialTailFSShares != 0 && b.tailFSSharingEnabledLocked() { if mask&ipn.NotifyInitialDriveShares != 0 && b.driveSharingEnabledLocked() {
ini.TailFSShares = b.pm.prefs.TailFSShares() ini.DriveShares = b.pm.prefs.DriveShares()
} }
} }
@ -3382,8 +3382,8 @@ func (b *LocalBackend) TCPHandlerForDst(src, dst netip.AddrPort) (handler func(c
return b.handleWebClientConn, opts return b.handleWebClientConn, opts
} }
return b.HandleQuad100Port80Conn, opts return b.HandleQuad100Port80Conn, opts
case TailFSLocalPort: case DriveLocalPort:
return b.handleTailFSConn, opts return b.handleDriveConn, opts
} }
} }
@ -3417,9 +3417,9 @@ func (b *LocalBackend) TCPHandlerForDst(src, dst netip.AddrPort) (handler func(c
return nil, nil return nil, nil
} }
func (b *LocalBackend) handleTailFSConn(conn net.Conn) error { func (b *LocalBackend) handleDriveConn(conn net.Conn) error {
fs, ok := b.sys.TailFSForLocal.GetOK() fs, ok := b.sys.DriveForLocal.GetOK()
if !ok || !b.TailFSAccessEnabled() { if !ok || !b.DriveAccessEnabled() {
conn.Close() conn.Close()
return nil return nil
} }
@ -4729,8 +4729,8 @@ func (b *LocalBackend) setNetMapLocked(nm *netmap.NetworkMap) {
} }
} }
b.updateTailFSPeersLocked(nm) b.updateDrivePeersLocked(nm)
b.tailFSNotifyCurrentSharesLocked() b.driveNotifyCurrentSharesLocked()
} }
func (b *LocalBackend) updatePeersFromNetmapLocked(nm *netmap.NetworkMap) { func (b *LocalBackend) updatePeersFromNetmapLocked(nm *netmap.NetworkMap) {
@ -4757,10 +4757,10 @@ func (b *LocalBackend) updatePeersFromNetmapLocked(nm *netmap.NetworkMap) {
} }
} }
// tailFSTransport is an http.RoundTripper that uses the latest value of // driveTransport is an http.RoundTripper that uses the latest value of
// b.Dialer().PeerAPITransport() for each round trip and imposes a short // b.Dialer().PeerAPITransport() for each round trip and imposes a short
// dial timeout to avoid hanging on connecting to offline/unreachable hosts. // dial timeout to avoid hanging on connecting to offline/unreachable hosts.
type tailFSTransport struct { type driveTransport struct {
b *LocalBackend b *LocalBackend
} }
@ -4817,7 +4817,7 @@ func (rbw *responseBodyWrapper) Close() error {
return err return err
} }
func (t *tailFSTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) { func (dt *driveTransport) RoundTrip(req *http.Request) (resp *http.Response, err error) {
bw := &requestBodyWrapper{} bw := &requestBodyWrapper{}
if req.Body != nil { if req.Body != nil {
bw.ReadCloser = req.Body bw.ReadCloser = req.Body
@ -4839,24 +4839,24 @@ func (t *tailFSTransport) RoundTrip(req *http.Request) (resp *http.Response, err
return return
} }
t.b.mu.Lock() dt.b.mu.Lock()
selfNodeKey := t.b.netMap.SelfNode.Key().ShortString() selfNodeKey := dt.b.netMap.SelfNode.Key().ShortString()
t.b.mu.Unlock() dt.b.mu.Unlock()
n, _, ok := t.b.WhoIs(netip.MustParseAddrPort(req.URL.Host)) n, _, ok := dt.b.WhoIs(netip.MustParseAddrPort(req.URL.Host))
shareNodeKey := "unknown" shareNodeKey := "unknown"
if ok { if ok {
shareNodeKey = string(n.Key().ShortString()) shareNodeKey = string(n.Key().ShortString())
} }
rbw := responseBodyWrapper{ rbw := responseBodyWrapper{
log: t.b.logf, log: dt.b.logf,
method: req.Method, method: req.Method,
bytesTx: int64(bw.bytesRead), bytesTx: int64(bw.bytesRead),
selfNodeKey: selfNodeKey, selfNodeKey: selfNodeKey,
shareNodeKey: shareNodeKey, shareNodeKey: shareNodeKey,
contentType: contentType, contentType: contentType,
contentLength: resp.ContentLength, contentLength: resp.ContentLength,
fileExtension: parseTailFSFileExtensionForLog(req.URL.Path), fileExtension: parseDriveFileExtensionForLog(req.URL.Path),
statusCode: resp.StatusCode, statusCode: resp.StatusCode,
ReadCloser: resp.Body, ReadCloser: resp.Body,
} }
@ -4873,7 +4873,7 @@ func (t *tailFSTransport) RoundTrip(req *http.Request) (resp *http.Response, err
// unreachable hosts. // unreachable hosts.
dialTimeout := 1 * time.Second // TODO(oxtoacart): tune this dialTimeout := 1 * time.Second // TODO(oxtoacart): tune this
tr := t.b.Dialer().PeerAPITransport().Clone() tr := dt.b.Dialer().PeerAPITransport().Clone()
dialContext := tr.DialContext dialContext := tr.DialContext
tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { tr.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
ctxWithTimeout, cancel := context.WithTimeout(ctx, dialTimeout) ctxWithTimeout, cancel := context.WithTimeout(ctx, dialTimeout)

@ -2295,12 +2295,12 @@ func TestTCPHandlerForDst(t *testing.T) {
intercept: false, intercept: false,
}, },
{ {
desc: "intercept port 8080 (TailFS) on quad100 IPv4", desc: "intercept port 8080 (Taildrive) on quad100 IPv4",
dst: "100.100.100.100:8080", dst: "100.100.100.100:8080",
intercept: true, intercept: true,
}, },
{ {
desc: "intercept port 8080 (TailFS) on quad100 IPv6", desc: "intercept port 8080 (Taildrive) on quad100 IPv6",
dst: "[fd7a:115c:a1e0::53]:8080", dst: "[fd7a:115c:a1e0::53]:8080",
intercept: true, intercept: true,
}, },
@ -2340,7 +2340,7 @@ func TestTCPHandlerForDst(t *testing.T) {
} }
} }
func TestTailFSManageShares(t *testing.T) { func TestDriveManageShares(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
disabled bool disabled bool
@ -2410,7 +2410,7 @@ func TestTailFSManageShares(t *testing.T) {
name: "add_disabled", name: "add_disabled",
disabled: true, disabled: true,
add: &drive.Share{Name: "a"}, add: &drive.Share{Name: "a"},
expect: ErrTailFSNotEnabled, expect: ErrDriveNotEnabled,
}, },
{ {
name: "remove", name: "remove",
@ -2439,7 +2439,7 @@ func TestTailFSManageShares(t *testing.T) {
name: "remove_disabled", name: "remove_disabled",
disabled: true, disabled: true,
remove: "b", remove: "b",
expect: ErrTailFSNotEnabled, expect: ErrDriveNotEnabled,
}, },
{ {
name: "rename", name: "rename",
@ -2480,7 +2480,7 @@ func TestTailFSManageShares(t *testing.T) {
name: "rename_disabled", name: "rename_disabled",
disabled: true, disabled: true,
rename: [2]string{"a", "c"}, rename: [2]string{"a", "c"},
expect: ErrTailFSNotEnabled, expect: ErrDriveNotEnabled,
}, },
} }
@ -2494,7 +2494,7 @@ func TestTailFSManageShares(t *testing.T) {
b := newTestBackend(t) b := newTestBackend(t)
b.mu.Lock() b.mu.Lock()
if tt.existing != nil { if tt.existing != nil {
b.tailFSSetSharesLocked(tt.existing) b.driveSetSharesLocked(tt.existing)
} }
if !tt.disabled { if !tt.disabled {
self := b.netMap.SelfNode.AsStruct() self := b.netMap.SelfNode.AsStruct()
@ -2517,7 +2517,7 @@ func TestTailFSManageShares(t *testing.T) {
func() { wg.Done() }, func() { wg.Done() },
func(n *ipn.Notify) bool { func(n *ipn.Notify) bool {
select { select {
case result <- n.TailFSShares: case result <- n.DriveShares:
default: default:
// //
} }
@ -2529,11 +2529,11 @@ func TestTailFSManageShares(t *testing.T) {
var err error var err error
switch { switch {
case tt.add != nil: case tt.add != nil:
err = b.TailFSSetShare(tt.add) err = b.DriveSetShare(tt.add)
case tt.remove != "": case tt.remove != "":
err = b.TailFSRemoveShare(tt.remove) err = b.DriveRemoveShare(tt.remove)
default: default:
err = b.TailFSRenameShare(tt.rename[0], tt.rename[1]) err = b.DriveRenameShare(tt.rename[0], tt.rename[1])
} }
switch e := tt.expect.(type) { switch e := tt.expect.(type) {

@ -325,7 +325,7 @@ func (h *peerAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return return
} }
if strings.HasPrefix(r.URL.Path, tailFSPrefix) { if strings.HasPrefix(r.URL.Path, tailFSPrefix) {
h.handleServeTailFS(w, r) h.handleServeDrive(w, r)
return return
} }
switch r.URL.Path { switch r.URL.Path {
@ -1141,23 +1141,23 @@ func (rbw *requestBodyWrapper) Read(b []byte) (int, error) {
return n, err return n, err
} }
func (h *peerAPIHandler) handleServeTailFS(w http.ResponseWriter, r *http.Request) { func (h *peerAPIHandler) handleServeDrive(w http.ResponseWriter, r *http.Request) {
if !h.ps.b.TailFSSharingEnabled() { if !h.ps.b.DriveSharingEnabled() {
h.logf("tailfs: not enabled") h.logf("tailfs: not enabled")
http.Error(w, "tailfs not enabled", http.StatusNotFound) http.Error(w, "tailfs not enabled", http.StatusNotFound)
return return
} }
capsMap := h.peerCaps() capsMap := h.peerCaps()
tailfsCaps, ok := capsMap[tailcfg.PeerCapabilityTailFS] driveCaps, ok := capsMap[tailcfg.PeerCapabilityTailFS]
if !ok { if !ok {
h.logf("tailfs: not permitted") h.logf("tailfs: not permitted")
http.Error(w, "tailfs not permitted", http.StatusForbidden) http.Error(w, "tailfs not permitted", http.StatusForbidden)
return return
} }
rawPerms := make([][]byte, 0, len(tailfsCaps)) rawPerms := make([][]byte, 0, len(driveCaps))
for _, cap := range tailfsCaps { for _, cap := range driveCaps {
rawPerms = append(rawPerms, []byte(cap)) rawPerms = append(rawPerms, []byte(cap))
} }
@ -1168,7 +1168,7 @@ func (h *peerAPIHandler) handleServeTailFS(w http.ResponseWriter, r *http.Reques
return return
} }
fs, ok := h.ps.b.sys.TailFSForRemote.GetOK() fs, ok := h.ps.b.sys.DriveForRemote.GetOK()
if !ok { if !ok {
h.logf("tailfs: not supported on platform") h.logf("tailfs: not supported on platform")
http.Error(w, "tailfs not supported on platform", http.StatusNotFound) http.Error(w, "tailfs not supported on platform", http.StatusNotFound)
@ -1193,7 +1193,7 @@ func (h *peerAPIHandler) handleServeTailFS(w http.ResponseWriter, r *http.Reques
contentType = ct contentType = ct
} }
h.logf("tailfs: share: %s from %s to %s: status-code=%d ext=%q content-type=%q tx=%.f rx=%.f", r.Method, h.peerNode.Key().ShortString(), h.selfNode.Key().ShortString(), wr.statusCode, parseTailFSFileExtensionForLog(r.URL.Path), contentType, roundTraffic(wr.contentLength), roundTraffic(bw.bytesRead)) h.logf("tailfs: share: %s from %s to %s: status-code=%d ext=%q content-type=%q tx=%.f rx=%.f", r.Method, h.peerNode.Key().ShortString(), h.selfNode.Key().ShortString(), wr.statusCode, parseDriveFileExtensionForLog(r.URL.Path), contentType, roundTraffic(wr.contentLength), roundTraffic(bw.bytesRead))
} }
}() }()
} }
@ -1202,13 +1202,13 @@ func (h *peerAPIHandler) handleServeTailFS(w http.ResponseWriter, r *http.Reques
fs.ServeHTTPWithPerms(p, wr, r) fs.ServeHTTPWithPerms(p, wr, r)
} }
// parseTailFSFileExtensionForLog parses the file extension, if available. // parseDriveFileExtensionForLog parses the file extension, if available.
// If a file extension is not present or parsable, the file extension is // If a file extension is not present or parsable, the file extension is
// set to "unknown". If the file extension contains a double quote, it is // set to "unknown". If the file extension contains a double quote, it is
// replaced with "removed". // replaced with "removed".
// All whitespace is removed from a parsed file extension. // All whitespace is removed from a parsed file extension.
// File extensions including the leading ., e.g. ".gif". // File extensions including the leading ., e.g. ".gif".
func parseTailFSFileExtensionForLog(path string) string { func parseDriveFileExtensionForLog(path string) string {
fileExt := "unknown" fileExt := "unknown"
if fe := filepath.Ext(path); fe != "" { if fe := filepath.Ext(path); fe != "" {
if strings.Contains(fe, "\"") { if strings.Contains(fe, "\"") {

@ -64,7 +64,7 @@ type serveHTTPContext struct {
// //
// This is not used in userspace-networking mode. // This is not used in userspace-networking mode.
// //
// localListener is used by tailscale serve (TCP only), the built-in web client and tailfs. // localListener is used by tailscale serve (TCP only), the built-in web client and Taildrive.
// Most serve traffic and peer traffic for the web client are intercepted by netstack. // Most serve traffic and peer traffic for the web client are intercepted by netstack.
// This listener exists purely for connections from the machine itself, as that goes via the kernel, // This listener exists purely for connections from the machine itself, as that goes via the kernel,
// so we need to be in the kernel's listening/routing tables. // so we need to be in the kernel's listening/routing tables.

@ -116,7 +116,7 @@ var handler = map[string]localAPIHandler{
"set-dns": (*Handler).serveSetDNS, "set-dns": (*Handler).serveSetDNS,
"set-expiry-sooner": (*Handler).serveSetExpirySooner, "set-expiry-sooner": (*Handler).serveSetExpirySooner,
"set-gui-visible": (*Handler).serveSetGUIVisible, "set-gui-visible": (*Handler).serveSetGUIVisible,
"tailfs/fileserver-address": (*Handler).serveTailFSFileServerAddr, "tailfs/fileserver-address": (*Handler).serveDriveServerAddr,
"tailfs/shares": (*Handler).serveShares, "tailfs/shares": (*Handler).serveShares,
"start": (*Handler).serveStart, "start": (*Handler).serveStart,
"status": (*Handler).serveStatus, "status": (*Handler).serveStatus,
@ -2735,8 +2735,8 @@ func (h *Handler) serveUpdateProgress(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(ups) json.NewEncoder(w).Encode(ups)
} }
// serveTailFSFileServerAddr handles updates of the tailfs file server address. // serveDriveServerAddr handles updates of the Taildrive file server address.
func (h *Handler) serveTailFSFileServerAddr(w http.ResponseWriter, r *http.Request) { func (h *Handler) serveDriveServerAddr(w http.ResponseWriter, r *http.Request) {
if r.Method != "PUT" { if r.Method != "PUT" {
http.Error(w, "only PUT allowed", http.StatusMethodNotAllowed) http.Error(w, "only PUT allowed", http.StatusMethodNotAllowed)
return return
@ -2748,18 +2748,18 @@ func (h *Handler) serveTailFSFileServerAddr(w http.ResponseWriter, r *http.Reque
return return
} }
h.b.TailFSSetFileServerAddr(string(b)) h.b.DriveSetServerAddr(string(b))
w.WriteHeader(http.StatusCreated) w.WriteHeader(http.StatusCreated)
} }
// serveShares handles the management of tailfs shares. // serveShares handles the management of Taildrive shares.
// //
// PUT - adds or updates an existing share // PUT - adds or updates an existing share
// DELETE - removes a share // DELETE - removes a share
// GET - gets a list of all shares, sorted by name // GET - gets a list of all shares, sorted by name
// POST - renames an existing share // POST - renames an existing share
func (h *Handler) serveShares(w http.ResponseWriter, r *http.Request) { func (h *Handler) serveShares(w http.ResponseWriter, r *http.Request) {
if !h.b.TailFSSharingEnabled() { if !h.b.DriveSharingEnabled() {
http.Error(w, `tailfs sharing not enabled, please add the attribute "tailfs:share" to this node in your ACLs' "nodeAttrs" section`, http.StatusForbidden) http.Error(w, `tailfs sharing not enabled, please add the attribute "tailfs:share" to this node in your ACLs' "nodeAttrs" section`, http.StatusForbidden)
return return
} }
@ -2790,7 +2790,7 @@ func (h *Handler) serveShares(w http.ResponseWriter, r *http.Request) {
} }
share.As = username share.As = username
} }
err = h.b.TailFSSetShare(&share) err = h.b.DriveSetShare(&share)
if err != nil { if err != nil {
if errors.Is(err, ipnlocal.ErrInvalidShareName) { if errors.Is(err, ipnlocal.ErrInvalidShareName) {
http.Error(w, "invalid share name", http.StatusBadRequest) http.Error(w, "invalid share name", http.StatusBadRequest)
@ -2806,7 +2806,7 @@ func (h *Handler) serveShares(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
err = h.b.TailFSRemoveShare(string(b)) err = h.b.DriveRemoveShare(string(b))
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
http.Error(w, "share not found", http.StatusNotFound) http.Error(w, "share not found", http.StatusNotFound)
@ -2823,7 +2823,7 @@ func (h *Handler) serveShares(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
err = h.b.TailFSRenameShare(names[0], names[1]) err = h.b.DriveRenameShare(names[0], names[1])
if err != nil { if err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
http.Error(w, "share not found", http.StatusNotFound) http.Error(w, "share not found", http.StatusNotFound)
@ -2842,7 +2842,7 @@ func (h *Handler) serveShares(w http.ResponseWriter, r *http.Request) {
} }
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
case "GET": case "GET":
shares := h.b.TailFSGetShares() shares := h.b.DriveGetShares()
err := json.NewEncoder(w).Encode(shares) err := json.NewEncoder(w).Encode(shares)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

@ -225,9 +225,9 @@ type Prefs struct {
// Linux-only. // Linux-only.
NetfilterKind string NetfilterKind string
// TailFSShares are the configured TailFSShares, stored in increasing order // DriveShares are the configured DriveShares, stored in increasing order
// by name. // by name.
TailFSShares []*drive.Share DriveShares []*drive.Share
// The Persist field is named 'Config' in the file for backward // The Persist field is named 'Config' in the file for backward
// compatibility with earlier versions. // compatibility with earlier versions.
@ -300,7 +300,7 @@ type MaskedPrefs struct {
AppConnectorSet bool `json:",omitempty"` AppConnectorSet bool `json:",omitempty"`
PostureCheckingSet bool `json:",omitempty"` PostureCheckingSet bool `json:",omitempty"`
NetfilterKindSet bool `json:",omitempty"` NetfilterKindSet bool `json:",omitempty"`
TailFSSharesSet bool `json:",omitempty"` DriveSharesSet bool `json:",omitempty"`
} }
type AutoUpdatePrefsMask struct { type AutoUpdatePrefsMask struct {
@ -564,7 +564,7 @@ func (p *Prefs) Equals(p2 *Prefs) bool {
p.AutoUpdate.Equals(p2.AutoUpdate) && p.AutoUpdate.Equals(p2.AutoUpdate) &&
p.AppConnector == p2.AppConnector && p.AppConnector == p2.AppConnector &&
p.PostureChecking == p2.PostureChecking && p.PostureChecking == p2.PostureChecking &&
slices.EqualFunc(p.TailFSShares, p2.TailFSShares, drive.SharesEqual) && slices.EqualFunc(p.DriveShares, p2.DriveShares, drive.SharesEqual) &&
p.NetfilterKind == p2.NetfilterKind p.NetfilterKind == p2.NetfilterKind
} }

@ -62,7 +62,7 @@ func TestPrefsEqual(t *testing.T) {
"AppConnector", "AppConnector",
"PostureChecking", "PostureChecking",
"NetfilterKind", "NetfilterKind",
"TailFSShares", "DriveShares",
"Persist", "Persist",
} }
if have := fieldsOf(reflect.TypeFor[Prefs]()); !reflect.DeepEqual(have, prefsHandles) { if have := fieldsOf(reflect.TypeFor[Prefs]()); !reflect.DeepEqual(have, prefsHandles) {

@ -1345,7 +1345,7 @@ const (
// PeerCapabilityWebUI grants the ability for a peer to edit features from the // PeerCapabilityWebUI grants the ability for a peer to edit features from the
// device Web UI. // device Web UI.
PeerCapabilityWebUI PeerCapability = "tailscale.com/cap/webui" PeerCapabilityWebUI PeerCapability = "tailscale.com/cap/webui"
// PeerCapabilityTailFS grants the ability for a peer to access tailfs shares. // PeerCapabilityTailFS grants the ability for a peer to access Taildrive shares.
PeerCapabilityTailFS PeerCapability = "tailscale.com/cap/tailfs" PeerCapabilityTailFS PeerCapability = "tailscale.com/cap/tailfs"
) )

@ -48,8 +48,8 @@ type System struct {
Tun SubSystem[*tstun.Wrapper] Tun SubSystem[*tstun.Wrapper]
StateStore SubSystem[ipn.StateStore] StateStore SubSystem[ipn.StateStore]
Netstack SubSystem[NetstackImpl] // actually a *netstack.Impl Netstack SubSystem[NetstackImpl] // actually a *netstack.Impl
TailFSForLocal SubSystem[drive.FileSystemForLocal] DriveForLocal SubSystem[drive.FileSystemForLocal]
TailFSForRemote SubSystem[drive.FileSystemForRemote] DriveForRemote SubSystem[drive.FileSystemForRemote]
// InitialConfig is initial server config, if any. // InitialConfig is initial server config, if any.
// It is nil if the node is not in declarative mode. // It is nil if the node is not in declarative mode.
@ -102,9 +102,9 @@ func (s *System) Set(v any) {
case NetstackImpl: case NetstackImpl:
s.Netstack.Set(v) s.Netstack.Set(v)
case drive.FileSystemForLocal: case drive.FileSystemForLocal:
s.TailFSForLocal.Set(v) s.DriveForLocal.Set(v)
case drive.FileSystemForRemote: case drive.FileSystemForRemote:
s.TailFSForRemote.Set(v) s.DriveForRemote.Set(v)
default: default:
panic(fmt.Sprintf("unknown type %T", v)) panic(fmt.Sprintf("unknown type %T", v))
} }

@ -529,7 +529,7 @@ func (s *Server) start() (reterr error) {
closePool.add(s.dialer) closePool.add(s.dialer)
sys.Set(eng) sys.Set(eng)
// TODO(oxtoacart): do we need to support TailFS on tsnet, and if so, how? // TODO(oxtoacart): do we need to support Taildrive on tsnet, and if so, how?
ns, err := netstack.Create(logf, sys.Tun.Get(), eng, sys.MagicSock.Get(), s.dialer, sys.DNSManager.Get(), sys.ProxyMapper(), nil) ns, err := netstack.Create(logf, sys.Tun.Get(), eng, sys.MagicSock.Get(), s.dialer, sys.DNSManager.Get(), sys.ProxyMapper(), nil)
if err != nil { if err != nil {
return fmt.Errorf("netstack.Create: %w", err) return fmt.Errorf("netstack.Create: %w", err)

@ -189,7 +189,7 @@ type Impl struct {
ctxCancel context.CancelFunc // called on Close ctxCancel context.CancelFunc // called on Close
lb *ipnlocal.LocalBackend // or nil lb *ipnlocal.LocalBackend // or nil
dns *dns.Manager dns *dns.Manager
tailFSForLocal drive.FileSystemForLocal // or nil driveForLocal drive.FileSystemForLocal // or nil
peerapiPort4Atomic atomic.Uint32 // uint16 port number for IPv4 peerapi peerapiPort4Atomic atomic.Uint32 // uint16 port number for IPv4 peerapi
peerapiPort6Atomic atomic.Uint32 // uint16 port number for IPv6 peerapi peerapiPort6Atomic atomic.Uint32 // uint16 port number for IPv6 peerapi
@ -248,7 +248,7 @@ const nicID = 1
const maxUDPPacketSize = tstun.MaxPacketSize const maxUDPPacketSize = tstun.MaxPacketSize
// Create creates and populates a new Impl. // Create creates and populates a new Impl.
func Create(logf logger.Logf, tundev *tstun.Wrapper, e wgengine.Engine, mc *magicsock.Conn, dialer *tsdial.Dialer, dns *dns.Manager, pm *proxymap.Mapper, tailFSForLocal drive.FileSystemForLocal) (*Impl, error) { func Create(logf logger.Logf, tundev *tstun.Wrapper, e wgengine.Engine, mc *magicsock.Conn, dialer *tsdial.Dialer, dns *dns.Manager, pm *proxymap.Mapper, driveForLocal drive.FileSystemForLocal) (*Impl, error) {
if mc == nil { if mc == nil {
return nil, errors.New("nil magicsock.Conn") return nil, errors.New("nil magicsock.Conn")
} }
@ -330,7 +330,7 @@ func Create(logf logger.Logf, tundev *tstun.Wrapper, e wgengine.Engine, mc *magi
connsInFlightByClient: make(map[netip.Addr]int), connsInFlightByClient: make(map[netip.Addr]int),
packetsInFlight: make(map[stack.TransportEndpointID]struct{}), packetsInFlight: make(map[stack.TransportEndpointID]struct{}),
dns: dns, dns: dns,
tailFSForLocal: tailFSForLocal, driveForLocal: driveForLocal,
} }
ns.ctx, ns.ctxCancel = context.WithCancel(context.Background()) ns.ctx, ns.ctxCancel = context.WithCancel(context.Background())
ns.atomicIsLocalIPFunc.Store(tsaddr.FalseContainsIPFunc()) ns.atomicIsLocalIPFunc.Store(tsaddr.FalseContainsIPFunc())
@ -670,7 +670,7 @@ func (ns *Impl) handleLocalPackets(p *packet.Parsed, t *tstun.Wrapper) filter.Re
return filter.DropSilently return filter.DropSilently
} }
// If it's not traffic to the service IP (e.g. magicDNS or TailFS) we don't // If it's not traffic to the service IP (e.g. magicDNS or Taildrive) we don't
// care; resume processing. // care; resume processing.
if dst := p.Dst.Addr(); dst != serviceIP && dst != serviceIPv6 { if dst := p.Dst.Addr(); dst != serviceIP && dst != serviceIPv6 {
return filter.Accept return filter.Accept

@ -204,9 +204,9 @@ type Config struct {
// SetSubsystem, if non-nil, is called for each new subsystem created, just before a successful return. // SetSubsystem, if non-nil, is called for each new subsystem created, just before a successful return.
SetSubsystem func(any) SetSubsystem func(any)
// TailFSForLocal, if populated, will cause the engine to expose a TailFS // DriveForLocal, if populated, will cause the engine to expose a Taildrive
// listener at 100.100.100.100:8080. // listener at 100.100.100.100:8080.
TailFSForLocal drive.FileSystemForLocal DriveForLocal drive.FileSystemForLocal
} }
// NewFakeUserspaceEngine returns a new userspace engine for testing. // NewFakeUserspaceEngine returns a new userspace engine for testing.
@ -468,8 +468,8 @@ func NewUserspaceEngine(logf logger.Logf, conf Config) (_ Engine, reterr error)
conf.SetSubsystem(conf.Router) conf.SetSubsystem(conf.Router)
conf.SetSubsystem(conf.Dialer) conf.SetSubsystem(conf.Dialer)
conf.SetSubsystem(e.netMon) conf.SetSubsystem(e.netMon)
if conf.TailFSForLocal != nil { if conf.DriveForLocal != nil {
conf.SetSubsystem(conf.TailFSForLocal) conf.SetSubsystem(conf.DriveForLocal)
} }
} }

Loading…
Cancel
Save