Compare commits

...

1 Commits

Author SHA1 Message Date
Rhea Ghosh
7050b28885 taildrop macos testing 2023-10-22 19:04:11 -05:00
5 changed files with 104 additions and 57 deletions

View File

@@ -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.

View File

@@ -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() {

View File

@@ -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())
}

View File

@@ -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

View File

@@ -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
})