summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRhea Ghosh <rhea@tailscale.com>2023-10-21 18:28:07 -0500
committerRhea Ghosh <rhea@tailscale.com>2023-10-22 19:04:11 -0500
commit7050b288853cd5f7894d89cf6f90abfbee4717d5 (patch)
treec9d272e18f93459db2c108f8fbbc6f9149d090c5
parentf398712c006a8afeb165942baae9e7c526de7a9e (diff)
downloadtailscale-rhea/apple-test.tar.xz
tailscale-rhea/apple-test.zip
taildrop macos testingrhea/apple-test
-rw-r--r--ipn/backend.go22
-rw-r--r--ipn/ipnlocal/local.go21
-rw-r--r--taildrop/delete.go2
-rw-r--r--taildrop/send.go90
-rw-r--r--taildrop/taildrop.go26
5 files changed, 104 insertions, 57 deletions
diff --git a/ipn/backend.go b/ipn/backend.go
index ad5dbd4bf..1cabc5788 100644
--- a/ipn/backend.go
+++ b/ipn/backend.go
@@ -6,10 +6,10 @@ package ipn
import (
"fmt"
"strings"
- "time"
"tailscale.com/ipn/ipnstate"
"tailscale.com/tailcfg"
+ "tailscale.com/taildrop"
"tailscale.com/types/empty"
"tailscale.com/types/key"
"tailscale.com/types/netmap"
@@ -109,7 +109,7 @@ type Notify struct {
// of being transferred.
//
// Deprecated: use LocalClient.AwaitWaitingFiles instead.
- IncomingFiles []PartialFile `json:",omitempty"`
+ IncomingFiles []taildrop.PartialFile `json:",omitempty"`
// LocalTCPPort, if non-nil, informs the UI frontend which
// (non-zero) localhost TCP port it's listening on.
@@ -164,24 +164,6 @@ func (n Notify) String() string {
return s[0:len(s)-1] + "}"
}
-// PartialFile represents an in-progress file transfer.
-type PartialFile struct {
- Name string // e.g. "foo.jpg"
- Started time.Time // time transfer started
- DeclaredSize int64 // or -1 if unknown
- Received int64 // bytes copied thus far
-
- // PartialPath is set non-empty in "direct" file mode to the
- // in-progress '*.partial' file's path when the peerapi isn't
- // being used; see LocalBackend.SetDirectFileRoot.
- PartialPath string `json:",omitempty"`
-
- // Done is set in "direct" mode when the partial file has been
- // closed and is ready for the caller to rename away the
- // ".partial" suffix.
- Done bool `json:",omitempty"`
-}
-
// StateKey is an opaque identifier for a set of LocalBackend state
// (preferences, private keys, etc.). It is also used as a key for
// the various LoginProfiles that the instance may be signed into.
diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go
index c4ed545a7..1357e7304 100644
--- a/ipn/ipnlocal/local.go
+++ b/ipn/ipnlocal/local.go
@@ -240,6 +240,7 @@ type LocalBackend struct {
peerAPIServer *peerAPIServer // or nil
peerAPIListeners []*peerAPIListener
loginFlags controlclient.LoginFlags
+ incomingFiles map[*taildrop.IncomingFile]bool
fileWaiters set.HandleSet[context.CancelFunc] // of wake-up funcs
notifyWatchers set.HandleSet[*watchSession]
lastStatusTime time.Time // status.AsOf value of the last processed status update
@@ -2278,7 +2279,12 @@ func (b *LocalBackend) sendFileNotify() {
// Make sure we always set n.IncomingFiles non-nil so it gets encoded
// in JSON to clients. They distinguish between empty and non-nil
// to know whether a Notify should be able about files.
- n.IncomingFiles = apiSrv.taildrop.IncomingFiles()
+
+ //// n.IncomingFiles = apiSrv.taildrop.IncomingFiles()
+ n.IncomingFiles = make([]taildrop.PartialFile, 0)
+ for f := range b.incomingFiles {
+ n.IncomingFiles = append(n.IncomingFiles, f.PartialFile())
+ }
b.mu.Unlock()
sort.Slice(n.IncomingFiles, func(i, j int) bool {
@@ -4657,6 +4663,19 @@ func (b *LocalBackend) SetDNS(ctx context.Context, name, value string) error {
return cc.SetDNS(ctx, req)
}
+func (b *LocalBackend) registerIncomingFile(inf *taildrop.IncomingFile, active bool) {
+ b.mu.Lock()
+ defer b.mu.Unlock()
+ if b.incomingFiles == nil {
+ b.incomingFiles = make(map[*taildrop.IncomingFile]bool)
+ }
+ if active {
+ b.incomingFiles[inf] = true
+ } else {
+ delete(b.incomingFiles, inf)
+ }
+}
+
func peerAPIPorts(peer tailcfg.NodeView) (p4, p6 uint16) {
svcs := peer.Hostinfo().Services()
for i := range svcs.LenIter() {
diff --git a/taildrop/delete.go b/taildrop/delete.go
index 12b95c020..bf55d3309 100644
--- a/taildrop/delete.go
+++ b/taildrop/delete.go
@@ -70,7 +70,7 @@ func (d *fileDeleter) Init(m *Manager, eventHook func(string)) {
nameID := strings.TrimSuffix(de.Name(), partialSuffix)
if i := strings.LastIndexByte(nameID, '.'); i > 0 {
key := incomingFileKey{ClientID(nameID[i+len("."):]), nameID[:i]}
- m.incomingFiles.LoadFunc(key, func(_ *incomingFile, loaded bool) {
+ m.incomingFiles.LoadFunc(key, func(_ *IncomingFile, loaded bool) {
if !loaded {
d.Insert(de.Name())
}
diff --git a/taildrop/send.go b/taildrop/send.go
index 42c223737..8fe6786a7 100644
--- a/taildrop/send.go
+++ b/taildrop/send.go
@@ -22,23 +22,69 @@ type incomingFileKey struct {
name string // e.g., "foo.jpeg"
}
-type incomingFile struct {
+type IncomingFile struct {
clock tstime.DefaultClock
- started time.Time
- size int64 // or -1 if unknown; never 0
- w io.Writer // underlying writer
+ Name string // "foo.jpg"
+ Started time.Time
+ Size int64 // or -1 if unknown; never 0
+ W io.Writer // underlying writer
sendFileNotify func() // called when done
- partialPath string // non-empty in direct mode
+ PartialPath string // non-empty in direct mode
- mu sync.Mutex
- copied int64
- done bool
+ Mu sync.Mutex
+ Copied int64
+ Done bool
lastNotify time.Time
}
-func (f *incomingFile) Write(p []byte) (n int, err error) {
- n, err = f.w.Write(p)
+// type incomingFile struct {
+// name string // "foo.jpg"
+// started time.Time
+// size int64 // or -1 if unknown; never 0
+// w io.Writer // underlying writer
+// ph *peerAPIHandler
+// partialPath string // non-empty in direct mode
+
+// mu sync.Mutex
+// copied int64
+// done bool
+// lastNotify time.Time
+// }
+
+func (f *IncomingFile) PartialFile() PartialFile {
+ f.Mu.Lock()
+ defer f.Mu.Unlock()
+ return PartialFile{
+ Name: f.Name,
+ Started: f.Started,
+ DeclaredSize: f.Size,
+ Received: f.Copied,
+ PartialPath: f.PartialPath,
+ Done: f.Done,
+ }
+}
+
+// PartialFile represents an in-progress file transfer.
+type PartialFile struct {
+ Name string // e.g. "foo.jpg"
+ Started time.Time // time transfer started
+ DeclaredSize int64 // or -1 if unknown
+ Received int64 // bytes copied thus far
+
+ // PartialPath is set non-empty in "direct" file mode to the
+ // in-progress '*.partial' file's path when the peerapi isn't
+ // being used; see LocalBackend.SetDirectFileRoot.
+ PartialPath string `json:",omitempty"`
+
+ // Done is set in "direct" mode when the partial file has been
+ // closed and is ready for the caller to rename away the
+ // ".partial" suffix.
+ Done bool `json:",omitempty"`
+}
+
+func (f *IncomingFile) Write(p []byte) (n int, err error) {
+ n, err = f.W.Write(p)
var needNotify bool
defer func() {
@@ -47,9 +93,9 @@ func (f *incomingFile) Write(p []byte) (n int, err error) {
}
}()
if n > 0 {
- f.mu.Lock()
- defer f.mu.Unlock()
- f.copied += int64(n)
+ f.Mu.Lock()
+ defer f.Mu.Unlock()
+ f.Copied += int64(n)
now := f.clock.Now()
if f.lastNotify.IsZero() || now.Sub(f.lastNotify) > time.Second {
f.lastNotify = now
@@ -101,15 +147,15 @@ func (m *Manager) PutFile(id ClientID, baseName string, r io.Reader, offset, len
// Check whether there is an in-progress transfer for the file.
partialPath := dstPath + id.partialSuffix()
inFileKey := incomingFileKey{id, baseName}
- inFile, loaded := m.incomingFiles.LoadOrInit(inFileKey, func() *incomingFile {
- inFile := &incomingFile{
+ inFile, loaded := m.incomingFiles.LoadOrInit(inFileKey, func() *IncomingFile {
+ inFile := &IncomingFile{
clock: m.opts.Clock,
- started: m.opts.Clock.Now(),
- size: length,
+ Started: m.opts.Clock.Now(),
+ Size: length,
sendFileNotify: m.opts.SendFileNotify,
}
if m.opts.DirectFileMode {
- inFile.partialPath = partialPath
+ inFile.PartialPath = partialPath
}
return inFile
})
@@ -134,7 +180,7 @@ func (m *Manager) PutFile(id ClientID, baseName string, r io.Reader, offset, len
m.deleter.Insert(filepath.Base(partialPath)) // mark partial file for eventual deletion
}
}()
- inFile.w = f
+ inFile.W = f
// A positive offset implies that we are resuming an existing file.
// Seek to the appropriate offset and truncate the file.
@@ -170,9 +216,9 @@ func (m *Manager) PutFile(id ClientID, baseName string, r io.Reader, offset, len
// Return early for avoidPartialRename since users of AvoidFinalRename
// are depending on the exact naming of partial files.
if avoidPartialRename {
- inFile.mu.Lock()
- inFile.done = true
- inFile.mu.Unlock()
+ inFile.Mu.Lock()
+ inFile.Done = true
+ inFile.Mu.Unlock()
m.totalReceived.Add(1)
m.opts.SendFileNotify()
return fileLength, nil
diff --git a/taildrop/taildrop.go b/taildrop/taildrop.go
index ceda9cd39..7d8daffaa 100644
--- a/taildrop/taildrop.go
+++ b/taildrop/taildrop.go
@@ -25,7 +25,6 @@ import (
"unicode"
"unicode/utf8"
- "tailscale.com/ipn"
"tailscale.com/syncs"
"tailscale.com/tstime"
"tailscale.com/types/logger"
@@ -110,7 +109,7 @@ type Manager struct {
opts ManagerOptions
// incomingFiles is a map of files actively being received.
- incomingFiles syncs.Map[incomingFileKey, *incomingFile]
+ incomingFiles syncs.Map[incomingFileKey, *IncomingFile]
// deleter managers asynchronous deletion of files.
deleter fileDeleter
@@ -229,21 +228,22 @@ func rangeDir(dir string, fn func(fs.DirEntry) bool) error {
}
// IncomingFiles returns a list of active incoming files.
-func (m *Manager) IncomingFiles() []ipn.PartialFile {
+
+func (m *Manager) IncomingFiles() []PartialFile {
// Make sure we always set n.IncomingFiles non-nil so it gets encoded
// in JSON to clients. They distinguish between empty and non-nil
// to know whether a Notify should be able about files.
- files := make([]ipn.PartialFile, 0)
- m.incomingFiles.Range(func(k incomingFileKey, f *incomingFile) bool {
- f.mu.Lock()
- defer f.mu.Unlock()
- files = append(files, ipn.PartialFile{
+ files := make([]PartialFile, 0)
+ m.incomingFiles.Range(func(k incomingFileKey, f *IncomingFile) bool {
+ f.Mu.Lock()
+ defer f.Mu.Unlock()
+ files = append(files, PartialFile{
Name: k.name,
- Started: f.started,
- DeclaredSize: f.size,
- Received: f.copied,
- PartialPath: f.partialPath,
- Done: f.done,
+ Started: f.Started,
+ DeclaredSize: f.Size,
+ Received: f.Copied,
+ PartialPath: f.PartialPath,
+ Done: f.Done,
})
return true
})