Compare commits
1 Commits
bradfitz/a
...
rhea/apple
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7050b28885 |
@@ -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.
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user