@ -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.
TailFS LocalPort = 8080
Drive LocalPort = 8080
)
)
var (
var (
shareNameRegex = regexp . MustCompile ( ` ^[a-z0-9_\(\) ]+$ ` )
shareNameRegex = regexp . MustCompile ( ` ^[a-z0-9_\(\) ]+$ ` )
Err TailFSNotEnabled = errors . New ( "TailFS not enabled" )
Err DriveNotEnabled = 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 ) TailFS SharingEnabled( ) bool {
func ( b * LocalBackend ) Drive SharingEnabled( ) bool {
b . mu . Lock ( )
b . mu . Lock ( )
defer b . mu . Unlock ( )
defer b . mu . Unlock ( )
return b . tailFS SharingEnabledLocked( )
return b . drive SharingEnabledLocked( )
}
}
func ( b * LocalBackend ) tailFS SharingEnabledLocked( ) bool {
func ( b * LocalBackend ) drive SharingEnabledLocked( ) 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 ) TailFS AccessEnabled( ) bool {
func ( b * LocalBackend ) Drive AccessEnabled( ) bool {
b . mu . Lock ( )
b . mu . Lock ( )
defer b . mu . Unlock ( )
defer b . mu . Unlock ( )
return b . tailFS AccessEnabledLocked( )
return b . drive AccessEnabledLocked( )
}
}
func ( b * LocalBackend ) tailFS AccessEnabledLocked( ) bool {
func ( b * LocalBackend ) drive AccessEnabledLocked( ) 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 ) TailFSSetFile ServerAddr( addr string ) error {
func ( b * LocalBackend ) DriveSet ServerAddr( addr string ) error {
fs , ok := b . sys . TailFS ForRemote. GetOK ( )
fs , ok := b . sys . Drive ForRemote. GetOK ( )
if ! ok {
if ! ok {
return Err TailFS NotEnabled
return Err Drive NotEnabled
}
}
fs . SetFileServerAddr ( addr )
fs . SetFileServerAddr ( addr )
return nil
return nil
}
}
// TailFS SetShare adds the given share if no share with that name exists, or
// Drive SetShare 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 ) TailFS SetShare( share * drive . Share ) error {
func ( b * LocalBackend ) Drive SetShare( 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 . tailFS SetShareLocked( share )
shares , err := b . drive SetShareLocked( share )
b . mu . Unlock ( )
b . mu . Unlock ( )
if err != nil {
if err != nil {
return err
return err
}
}
b . tailFS NotifyShares( shares )
b . drive NotifyShares( shares )
return nil
return nil
}
}
@ -108,12 +108,12 @@ func normalizeShareName(name string) (string, error) {
return name , nil
return name , nil
}
}
func ( b * LocalBackend ) tailFS SetShareLocked( share * drive . Share ) ( views . SliceView [ * drive . Share , drive . ShareView ] , error ) {
func ( b * LocalBackend ) drive SetShareLocked( share * drive . Share ) ( views . SliceView [ * drive . Share , drive . ShareView ] , error ) {
existingShares := b . pm . prefs . TailFS Shares( )
existingShares := b . pm . prefs . Drive Shares( )
fs , ok := b . sys . TailFS ForRemote. GetOK ( )
fs , ok := b . sys . Drive ForRemote. GetOK ( )
if ! ok {
if ! ok {
return existingShares , Err TailFS NotEnabled
return existingShares , Err Drive NotEnabled
}
}
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 . tailFS SetSharesLocked( shares )
err := b . drive SetSharesLocked( shares )
if err != nil {
if err != nil {
return existingShares , err
return existingShares , err
}
}
fs . SetShares ( shares )
fs . SetShares ( shares )
return b . pm . prefs . TailFS Shares( ) , nil
return b . pm . prefs . Drive Shares( ) , nil
}
}
// TailFS RenameShare renames the share at old name to new name. To avoid
// Drive RenameShare 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 ) TailFS RenameShare( oldName , newName string ) error {
func ( b * LocalBackend ) Drive RenameShare( 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 . tailFS RenameShareLocked( oldName , newName )
shares , err := b . drive RenameShareLocked( oldName , newName )
b . mu . Unlock ( )
b . mu . Unlock ( )
if err != nil {
if err != nil {
return err
return err
}
}
b . tailFS NotifyShares( shares )
b . drive NotifyShares( shares )
return nil
return nil
}
}
func ( b * LocalBackend ) tailFS RenameShareLocked( oldName , newName string ) ( views . SliceView [ * drive . Share , drive . ShareView ] , error ) {
func ( b * LocalBackend ) drive RenameShareLocked( oldName , newName string ) ( views . SliceView [ * drive . Share , drive . ShareView ] , error ) {
existingShares := b . pm . prefs . TailFS Shares( )
existingShares := b . pm . prefs . Drive Shares( )
fs , ok := b . sys . TailFS ForRemote. GetOK ( )
fs , ok := b . sys . Drive ForRemote. GetOK ( )
if ! ok {
if ! ok {
return existingShares , Err TailFS NotEnabled
return existingShares , Err Drive NotEnabled
}
}
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 . tailFS SetSharesLocked( shares )
err := b . drive SetSharesLocked( shares )
if err != nil {
if err != nil {
return existingShares , err
return existingShares , err
}
}
fs . SetShares ( shares )
fs . SetShares ( shares )
return b . pm . prefs . TailFS Shares( ) , nil
return b . pm . prefs . Drive Shares( ) , nil
}
}
// TailFS RemoveShare removes the named share. Share names are forced to
// Drive RemoveShare removes the named share. Share names are forced to
// lowercase.
// lowercase.
func ( b * LocalBackend ) TailFS RemoveShare( name string ) error {
func ( b * LocalBackend ) Drive RemoveShare( 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 . tailFS RemoveShareLocked( name )
shares , err := b . drive RemoveShareLocked( name )
b . mu . Unlock ( )
b . mu . Unlock ( )
if err != nil {
if err != nil {
return err
return err
}
}
b . tailFS NotifyShares( shares )
b . drive NotifyShares( shares )
return nil
return nil
}
}
func ( b * LocalBackend ) tailFS RemoveShareLocked( name string ) ( views . SliceView [ * drive . Share , drive . ShareView ] , error ) {
func ( b * LocalBackend ) drive RemoveShareLocked( name string ) ( views . SliceView [ * drive . Share , drive . ShareView ] , error ) {
existingShares := b . pm . prefs . TailFS Shares( )
existingShares := b . pm . prefs . Drive Shares( )
fs , ok := b . sys . TailFS ForRemote. GetOK ( )
fs , ok := b . sys . Drive ForRemote. GetOK ( )
if ! ok {
if ! ok {
return existingShares , Err TailFS NotEnabled
return existingShares , Err Drive NotEnabled
}
}
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 . tailFS SetSharesLocked( shares )
err := b . drive SetSharesLocked( shares )
if err != nil {
if err != nil {
return existingShares , err
return existingShares , err
}
}
fs . SetShares ( shares )
fs . SetShares ( shares )
return b . pm . prefs . TailFS Shares( ) , nil
return b . pm . prefs . Drive Shares( ) , nil
}
}
func ( b * LocalBackend ) tailFS SetSharesLocked( shares [ ] * drive . Share ) error {
func ( b * LocalBackend ) drive SetSharesLocked( 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 {
TailFS Shares: shares ,
Drive Shares: shares ,
} ,
} ,
TailFS SharesSet: true ,
Drive SharesSet: true ,
} )
} )
return b . pm . setPrefsLocked ( prefs . View ( ) )
return b . pm . setPrefsLocked ( prefs . View ( ) )
}
}
// tailFS NotifyShares notifies IPN bus listeners (e.g. Mac Application process)
// drive NotifyShares notifies IPN bus listeners (e.g. Mac Application process)
// about the latest list of shares.
// about the latest list of shares.
func ( b * LocalBackend ) tailFS NotifyShares( shares views . SliceView [ * drive . Share , drive . ShareView ] ) {
func ( b * LocalBackend ) drive NotifyShares( 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 { TailFS Shares: shares } )
b . send ( ipn . Notify { Drive Shares: shares } )
}
}
// tailFS NotifyCurrentSharesLocked sends an ipn.Notify if the current set of
// drive NotifyCurrentSharesLocked 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 ) tailFS NotifyCurrentSharesLocked( ) {
func ( b * LocalBackend ) drive NotifyCurrentSharesLocked( ) {
var shares views . SliceView [ * drive . Share , drive . ShareView ]
var shares views . SliceView [ * drive . Share , drive . ShareView ]
if b . tailFS SharingEnabledLocked( ) {
if b . drive SharingEnabledLocked( ) {
// Only populate shares if sharing is enabled.
// Only populate shares if sharing is enabled.
shares = b . pm . prefs . TailFS Shares( )
shares = b . pm . prefs . Drive Shares( )
}
}
lastNotified := b . lastNotified TailFS Shares. Load ( )
lastNotified := b . lastNotified Drive Shares. Load ( )
if lastNotified == nil || ! tailFS ShareViewsEqual( lastNotified , shares ) {
if lastNotified == nil || ! drive ShareViewsEqual( 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 . tailFS NotifyShares( shares )
go b . drive NotifyShares( shares )
}
}
}
}
func tailFS ShareViewsEqual( a * views . SliceView [ * drive . Share , drive . ShareView ] , b views . SliceView [ * drive . Share , drive . ShareView ] ) bool {
func drive ShareViewsEqual( 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 ) TailFS GetShares( ) views . SliceView [ * drive . Share , drive . ShareView ] {
func ( b * LocalBackend ) Drive GetShares( ) views . SliceView [ * drive . Share , drive . ShareView ] {
b . mu . Lock ( )
b . mu . Lock ( )
defer b . mu . Unlock ( )
defer b . mu . Unlock ( )
return b . pm . prefs . TailFS Shares( )
return b . pm . prefs . Drive Shares( )
}
}
// update TailFSPeersLocked sets all applicable peers from the netmap as tailfs
// update DrivePeersLocked sets all applicable peers from the netmap as Taildrive
// remotes.
// remotes.
func ( b * LocalBackend ) update TailFS PeersLocked( nm * netmap . NetworkMap ) {
func ( b * LocalBackend ) update Drive PeersLocked( nm * netmap . NetworkMap ) {
fs , ok := b . sys . TailFS ForLocal. GetOK ( )
fs , ok := b . sys . Drive ForLocal. GetOK ( )
if ! ok {
if ! ok {
return
return
}
}
var tailFS Remotes [ ] * drive . Remote
var drive Remotes [ ] * drive . Remote
if b . tailFS AccessEnabledLocked( ) {
if b . drive AccessEnabledLocked( ) {
// Only populate peers if access is enabled, otherwise leave blank.
// Only populate peers if access is enabled, otherwise leave blank.
tailFSRemotes = b . tailFS RemotesFromPeers( nm )
driveRemotes = b . drive RemotesFromPeers( nm )
}
}
fs . SetRemotes ( b . netMap . Domain , tailFSRemotes, & tailFS Transport{ b : b } )
fs . SetRemotes ( b . netMap . Domain , driveRemotes, & drive Transport{ b : b } )
}
}
func ( b * LocalBackend ) tailFS RemotesFromPeers( nm * netmap . NetworkMap ) [ ] * drive . Remote {
func ( b * LocalBackend ) drive RemotesFromPeers( nm * netmap . NetworkMap ) [ ] * drive . Remote {
tailFS Remotes := make ( [ ] * drive . Remote , 0 , len ( nm . Peers ) )
drive Remotes := make ( [ ] * drive . Remote , 0 , len ( nm . Peers ) )
for _ , p := range nm . Peers {
for _ , p := range nm . Peers {
// Exclude mullvad exit nodes from list of Tail FS peers
// Exclude mullvad exit nodes from list of Tail drive 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 ( tailFS Remotes, & drive . Remote {
driveRemotes = append ( drive Remotes, & 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 tailFS Remotes
return drive Remotes
}
}