diff --git a/client/tailscale/localclient.go b/client/tailscale/localclient.go index dc8eebf30..2bc4fa16d 100644 --- a/client/tailscale/localclient.go +++ b/client/tailscale/localclient.go @@ -1422,7 +1422,7 @@ func (lc *LocalClient) CheckUpdate(ctx context.Context) (*tailcfg.ClientVersion, // the filesystem. This is used on platforms like Windows and MacOS to let // Taildrive know to use the file server running in the GUI app. 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/drive/fileserver-address", http.StatusCreated, strings.NewReader(addr)) return err } @@ -1430,7 +1430,7 @@ func (lc *LocalClient) DriveSetServerAddr(ctx context.Context, addr string) erro // Taildrive will serve to remote nodes. If a share with the same name already // exists, the existing share is replaced/updated. 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/drive/shares", http.StatusCreated, jsonBody(share)) return err } @@ -1440,7 +1440,7 @@ func (lc *LocalClient) DriveShareRemove(ctx context.Context, name string) error _, err := lc.send( ctx, "DELETE", - "/localapi/v0/tailfs/shares", + "/localapi/v0/drive/shares", http.StatusNoContent, strings.NewReader(name)) return err @@ -1451,7 +1451,7 @@ func (lc *LocalClient) DriveShareRename(ctx context.Context, oldName, newName st _, err := lc.send( ctx, "POST", - "/localapi/v0/tailfs/shares", + "/localapi/v0/drive/shares", http.StatusNoContent, jsonBody([2]string{oldName, newName})) return err @@ -1460,7 +1460,7 @@ func (lc *LocalClient) DriveShareRename(ctx context.Context, oldName, newName st // DriveShareList returns the list of shares that drive is currently serving // to remote nodes. 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/drive/shares") if err != nil { return nil, err } diff --git a/cmd/tailscale/cli/share.go b/cmd/tailscale/cli/share.go index 0e6b35919..ae2e2b925 100644 --- a/cmd/tailscale/cli/share.go +++ b/cmd/tailscale/cli/share.go @@ -153,9 +153,9 @@ func buildShareLongHelp() string { var shareLongHelpBase = `Tailscale share allows you to share directories with other machines on your tailnet. -In order to share folders, your node needs to have the node attribute "tailfs:share". +In order to share folders, your node needs to have the node attribute "drive:share". -In order to access shares, your node needs to have the node attribute "tailfs:access". +In order to access shares, your node needs to have the node attribute "drive:access". For example, to enable sharing and accessing shares for all member nodes: @@ -163,8 +163,8 @@ For example, to enable sharing and accessing shares for all member nodes: { "target": ["autogroup:member"], "attr": [ - "tailfs:share", - "tailfs:access", + "drive:share", + "drive:access", ], }] @@ -191,7 +191,7 @@ Permissions to access shares are controlled via ACLs. For example, to give yours "src": ["mylogin@domain.com"], "dst": ["mylaptop's ip address"], "app": { - "tailscale.com/cap/tailfs": [{ + "tailscale.com/cap/drive": [{ "shares": ["docs"], "access": "rw" }] @@ -201,7 +201,7 @@ Permissions to access shares are controlled via ACLs. For example, to give yours "src": ["group:home"], "dst": ["mylaptop"], "app": { - "tailscale.com/cap/tailfs": [{ + "tailscale.com/cap/drive": [{ "shares": ["docs"], "access": "ro" }] @@ -215,7 +215,7 @@ To categorically give yourself access to all your shares, you can use the below "src": ["autogroup:member"], "dst": ["autogroup:self"], "app": { - "tailscale.com/cap/tailfs": [{ + "tailscale.com/cap/drive": [{ "shares": ["*"], "access": "rw" }] diff --git a/cmd/tailscaled/tailscaled.go b/cmd/tailscaled/tailscaled.go index 8bfc4e1f1..6279dc850 100644 --- a/cmd/tailscaled/tailscaled.go +++ b/cmd/tailscaled/tailscaled.go @@ -145,7 +145,7 @@ var subCommands = map[string]*func([]string) error{ "uninstall-system-daemon": &uninstallSystemDaemon, "debug": &debugModeFunc, "be-child": &beChildFunc, - "serve-tailfs": &serveDriveFunc, + "serve-taildrive": &serveDriveFunc, } var beCLI func() // non-nil if CLI is linked in @@ -833,9 +833,9 @@ func beChild(args []string) error { var serveDriveFunc = serveDrive -// serveDrive serves one or more tailfs on localhost using the WebDAV -// protocol. On UNIX and MacOS tailscaled environment, tailfs spawns child -// tailscaled processes in serve-tailfs mode in order to access the fliesystem +// serveDrive serves one or more Taildrives on localhost using the WebDAV +// protocol. On UNIX and MacOS tailscaled environment, Taildrive spawns child +// tailscaled processes in serve-taildrive mode in order to access the fliesystem // as specific (usually unprivileged) users. // // serveDrive prints the address on which it's listening to stdout so that the @@ -849,7 +849,7 @@ func serveDrive(args []string) error { } s, err := driveimpl.NewFileServer() if err != nil { - return fmt.Errorf("unable to start tailfs FileServer: %v", err) + return fmt.Errorf("unable to start Taildrive file server: %v", err) } shares := make(map[string]string) for i := 0; i < len(args); i += 2 { diff --git a/drive/driveimpl/remote_impl.go b/drive/driveimpl/remote_impl.go index 83a6a3a41..9f45dac05 100644 --- a/drive/driveimpl/remote_impl.go +++ b/drive/driveimpl/remote_impl.go @@ -217,7 +217,7 @@ func (s *FileSystemForRemote) ServeHTTPWithPerms(permissions drive.Permissions, func (s *FileSystemForRemote) stopUserServers(userServers map[string]*userServer) { for _, server := range userServers { if err := server.Close(); err != nil { - s.logf("error closing tailfs user server: %v", err) + s.logf("error closing taildrive user server: %v", err) } } } @@ -242,7 +242,7 @@ func (s *FileSystemForRemote) Close() error { return nil } -// userServer runs tailscaled serve-tailfs to serve webdav content for the +// userServer runs tailscaled serve-taildrive to serve webdav content for the // given Shares. All Shares are assumed to have the same Share.As, and the // content is served as that Share.As user. type userServer struct { @@ -306,13 +306,13 @@ func (s *userServer) runLoop() { // userServers anyway. func (s *userServer) run() error { // set up the command - args := []string{"serve-tailfs"} + args := []string{"serve-taildrive"} for _, s := range s.shares { args = append(args, s.Name, s.Path) } var cmd *exec.Cmd if s.canSudo() { - s.logf("starting TailFS file server as user %q", s.username) + s.logf("starting taildrive file server as user %q", s.username) allArgs := []string{"-n", "-u", s.username, s.executable} allArgs = append(allArgs, args...) cmd = exec.Command("sudo", allArgs...) @@ -324,7 +324,7 @@ func (s *userServer) run() error { if err != nil { return err } - s.logf("starting TailFS file server as ourselves") + s.logf("starting taildrive file server as ourselves") cmd = exec.Command(s.executable, args...) } stdout, err := cmd.StdoutPipe() @@ -356,13 +356,13 @@ func (s *userServer) run() error { // send the rest of stdout and stderr to logger to avoid blocking go func() { for stdoutScanner.Scan() { - s.logf("tailscaled serve-tailfs stdout: %v", stdoutScanner.Text()) + s.logf("tailscaled serve-taildrive stdout: %v", stdoutScanner.Text()) } }() stderrScanner := bufio.NewScanner(stderr) go func() { for stderrScanner.Scan() { - s.logf("tailscaled serve-tailfs stderr: %v", stderrScanner.Text()) + s.logf("tailscaled serve-taildrive stderr: %v", stderrScanner.Text()) } }() s.mu.Lock() diff --git a/ipn/ipnlocal/drive.go b/ipn/ipnlocal/drive.go index 89347df2c..7b98c135e 100644 --- a/ipn/ipnlocal/drive.go +++ b/ipn/ipnlocal/drive.go @@ -26,12 +26,12 @@ const ( var ( shareNameRegex = regexp.MustCompile(`^[a-z0-9_\(\) ]+$`) - ErrDriveNotEnabled = errors.New("TailFS not enabled") + ErrDriveNotEnabled = errors.New("Taildrive not enabled") ErrInvalidShareName = errors.New("Share names may only contain the letters a-z, underscore _, parentheses (), or spaces") ) // 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 drive:share node // attribute. func (b *LocalBackend) DriveSharingEnabled() bool { b.mu.Lock() @@ -40,11 +40,11 @@ func (b *LocalBackend) DriveSharingEnabled() 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.NodeAttrsTaildriveShare) } // 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 drive:access node // attribute. func (b *LocalBackend) DriveAccessEnabled() bool { b.mu.Lock() @@ -53,7 +53,7 @@ func (b *LocalBackend) DriveAccessEnabled() 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.NodeAttrsTaildriveAccess) } // DriveSetServerAddr tells Taildrive to use the given address for connecting @@ -351,7 +351,7 @@ func (b *LocalBackend) driveRemotesFromPeers(nm *netmap.NetworkMap) []*drive.Rem } peerID := p.ID() - url := fmt.Sprintf("%s/%s", peerAPIBase(nm, p), tailFSPrefix[1:]) + url := fmt.Sprintf("%s/%s", peerAPIBase(nm, p), taildrivePrefix[1:]) driveRemotes = append(driveRemotes, &drive.Remote{ Name: p.DisplayName(false), URL: url, @@ -359,7 +359,7 @@ func (b *LocalBackend) driveRemotesFromPeers(nm *netmap.NetworkMap) []*drive.Rem // TODO(oxtoacart): need to figure out a performant and reliable way to only // show the peers that have shares to which we have access // This will require work on the control server to transmit the inverse - // of the "tailscale.com/cap/tailfs" capability. + // of the "tailscale.com/cap/drive" capability. // For now, at least limit it only to nodes that are online. // Note, we have to iterate the latest netmap because the peer we got from the first iteration may not be it b.mu.Lock() diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 2ae020df7..f4865d9f4 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -4780,7 +4780,7 @@ type responseBodyWrapper struct { contentLength int64 } -// logAccess logs the tailfs: access: log line. If the logger is nil, +// logAccess logs the taildrive: access: log line. If the logger is nil, // the log will not be written. func (rbw *responseBodyWrapper) logAccess(err string) { if rbw.log == nil { @@ -4790,7 +4790,7 @@ func (rbw *responseBodyWrapper) logAccess(err string) { // Some operating systems create and copy lots of 0 length hidden files for // tracking various states. Omit these to keep logs from being too verbose. if rbw.contentLength > 0 { - rbw.log("tailfs: access: %s from %s to %s: status-code=%d ext=%q content-type=%q content-length=%.f tx=%.f rx=%.f err=%q", rbw.method, rbw.selfNodeKey, rbw.shareNodeKey, rbw.statusCode, rbw.fileExtension, rbw.contentType, roundTraffic(rbw.contentLength), roundTraffic(rbw.bytesTx), roundTraffic(rbw.bytesRx), err) + rbw.log("taildrive: access: %s from %s to %s: status-code=%d ext=%q content-type=%q content-length=%.f tx=%.f rx=%.f err=%q", rbw.method, rbw.selfNodeKey, rbw.shareNodeKey, rbw.statusCode, rbw.fileExtension, rbw.contentType, roundTraffic(rbw.contentLength), roundTraffic(rbw.bytesTx), roundTraffic(rbw.bytesRx), err) } } diff --git a/ipn/ipnlocal/local_test.go b/ipn/ipnlocal/local_test.go index 21cde99a7..387604333 100644 --- a/ipn/ipnlocal/local_test.go +++ b/ipn/ipnlocal/local_test.go @@ -2498,7 +2498,7 @@ func TestDriveManageShares(t *testing.T) { } if !tt.disabled { self := b.netMap.SelfNode.AsStruct() - self.CapMap = tailcfg.NodeCapMap{tailcfg.NodeAttrsTailFSShare: nil} + self.CapMap = tailcfg.NodeCapMap{tailcfg.NodeAttrsTaildriveShare: nil} b.netMap.SelfNode = self.View() b.sys.Set(driveimpl.NewFileSystemForRemote(b.logf)) } diff --git a/ipn/ipnlocal/peerapi.go b/ipn/ipnlocal/peerapi.go index f4bd42057..8fc4d11e6 100644 --- a/ipn/ipnlocal/peerapi.go +++ b/ipn/ipnlocal/peerapi.go @@ -48,7 +48,7 @@ import ( ) const ( - tailFSPrefix = "/v0/tailfs" + taildrivePrefix = "/v0/drive" ) var initListenConfig func(*net.ListenConfig, netip.Addr, *interfaces.State, string) error @@ -324,7 +324,7 @@ func (h *peerAPIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { h.handleDNSQuery(w, r) return } - if strings.HasPrefix(r.URL.Path, tailFSPrefix) { + if strings.HasPrefix(r.URL.Path, taildrivePrefix) { h.handleServeDrive(w, r) return } @@ -1143,16 +1143,16 @@ func (rbw *requestBodyWrapper) Read(b []byte) (int, error) { func (h *peerAPIHandler) handleServeDrive(w http.ResponseWriter, r *http.Request) { if !h.ps.b.DriveSharingEnabled() { - h.logf("tailfs: not enabled") - http.Error(w, "tailfs not enabled", http.StatusNotFound) + h.logf("taildrive: not enabled") + http.Error(w, "taildrive not enabled", http.StatusNotFound) return } capsMap := h.peerCaps() - driveCaps, ok := capsMap[tailcfg.PeerCapabilityTailFS] + driveCaps, ok := capsMap[tailcfg.PeerCapabilityTaildrive] if !ok { - h.logf("tailfs: not permitted") - http.Error(w, "tailfs not permitted", http.StatusForbidden) + h.logf("taildrive: not permitted") + http.Error(w, "taildrive not permitted", http.StatusForbidden) return } @@ -1163,15 +1163,15 @@ func (h *peerAPIHandler) handleServeDrive(w http.ResponseWriter, r *http.Request p, err := drive.ParsePermissions(rawPerms) if err != nil { - h.logf("tailfs: error parsing permissions: %w", err.Error()) + h.logf("taildrive: error parsing permissions: %w", err.Error()) http.Error(w, err.Error(), http.StatusInternalServerError) return } fs, ok := h.ps.b.sys.DriveForRemote.GetOK() if !ok { - h.logf("tailfs: not supported on platform") - http.Error(w, "tailfs not supported on platform", http.StatusNotFound) + h.logf("taildrive: not supported on platform") + http.Error(w, "taildrive not supported on platform", http.StatusNotFound) return } wr := &httpResponseWrapper{ @@ -1193,12 +1193,12 @@ func (h *peerAPIHandler) handleServeDrive(w http.ResponseWriter, r *http.Request 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, parseDriveFileExtensionForLog(r.URL.Path), contentType, roundTraffic(wr.contentLength), roundTraffic(bw.bytesRead)) + h.logf("taildrive: 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)) } }() } - r.URL.Path = strings.TrimPrefix(r.URL.Path, tailFSPrefix) + r.URL.Path = strings.TrimPrefix(r.URL.Path, taildrivePrefix) fs.ServeHTTPWithPerms(p, wr, r) } diff --git a/ipn/localapi/localapi.go b/ipn/localapi/localapi.go index 0449c488d..a387e3156 100644 --- a/ipn/localapi/localapi.go +++ b/ipn/localapi/localapi.go @@ -116,8 +116,8 @@ var handler = map[string]localAPIHandler{ "set-dns": (*Handler).serveSetDNS, "set-expiry-sooner": (*Handler).serveSetExpirySooner, "set-gui-visible": (*Handler).serveSetGUIVisible, - "tailfs/fileserver-address": (*Handler).serveDriveServerAddr, - "tailfs/shares": (*Handler).serveShares, + "drive/fileserver-address": (*Handler).serveDriveServerAddr, + "drive/shares": (*Handler).serveShares, "start": (*Handler).serveStart, "status": (*Handler).serveStatus, "tka/init": (*Handler).serveTKAInit, @@ -2760,7 +2760,7 @@ func (h *Handler) serveDriveServerAddr(w http.ResponseWriter, r *http.Request) { // POST - renames an existing share func (h *Handler) serveShares(w http.ResponseWriter, r *http.Request) { 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, `taildrive sharing not enabled, please add the attribute "drive:share" to this node in your ACLs' "nodeAttrs" section`, http.StatusForbidden) return } switch r.Method { diff --git a/tailcfg/tailcfg.go b/tailcfg/tailcfg.go index 36ff7472e..c8dcec297 100644 --- a/tailcfg/tailcfg.go +++ b/tailcfg/tailcfg.go @@ -130,7 +130,8 @@ type CapabilityVersion int // - 87: 2024-02-11: UserProfile.Groups removed (added in 66) // - 88: 2024-03-05: Client understands NodeAttrSuggestExitNode // - 89: 2024-03-23: Client no longer respects deleted PeerChange.Capabilities (use CapMap) -const CurrentCapabilityVersion CapabilityVersion = 89 +// - 90: 2024-04-03: Client understands PeerCapabilityTaildrive. +const CurrentCapabilityVersion CapabilityVersion = 90 type StableID string @@ -1345,8 +1346,8 @@ const ( // PeerCapabilityWebUI grants the ability for a peer to edit features from the // device Web UI. PeerCapabilityWebUI PeerCapability = "tailscale.com/cap/webui" - // PeerCapabilityTailFS grants the ability for a peer to access Taildrive shares. - PeerCapabilityTailFS PeerCapability = "tailscale.com/cap/tailfs" + // PeerCapabilityTaildrive grants the ability for a peer to access Taildrive shares. + PeerCapabilityTaildrive PeerCapability = "tailscale.com/cap/drive" ) // NodeCapMap is a map of capabilities to their optional values. It is valid for @@ -2218,11 +2219,11 @@ const ( // tail end of an active direct connection in magicsock. NodeAttrProbeUDPLifetime NodeCapability = "probe-udp-lifetime" - // NodeAttrsTailFSShare enables sharing via TailFS. - NodeAttrsTailFSShare NodeCapability = "tailfs:share" + // NodeAttrsTaildriveShare enables sharing via Taildrive. + NodeAttrsTaildriveShare NodeCapability = "drive:share" - // NodeAttrsTailFSAccess enables accessing shares via TailFS. - NodeAttrsTailFSAccess NodeCapability = "tailfs:access" + // NodeAttrsTaildriveAccess enables accessing shares via Taildrive. + NodeAttrsTaildriveAccess NodeCapability = "drive:access" // NodeAttrSuggestExitNode is applied to each exit node which the control plane has determined // is a recommended exit node.