ipn{,/ipnlocal}: in direct file receive mode, don't rename partial file

Let caller (macOS) do it so Finder progress bar can be dismissed
without races.

Updates tailscale/corp#1575

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/1749/head
Brad Fitzpatrick 3 years ago committed by David Crawshaw
parent 48e30bb8de
commit 3d0599fca0

@ -95,12 +95,15 @@ type PartialFile struct {
DeclaredSize int64 // or -1 if unknown
Received int64 // bytes copied thus far
// FinalPath is non-empty when the final has been completely
// written and renamed into place. This is then the complete
// path to the file post-rename. This is only set in
// "direct" file mode where the peerapi isn't being used; see
// LocalBackend.SetDirectFileRoot.
FinalPath string `json:",omitempty"`
// 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

@ -381,21 +381,22 @@ This is my Tailscale device. Your device is %v.
}
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
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
finalPath string // non-empty in direct mode, when file is done
}
func (f *incomingFile) markAndNotifyDone(finalPath string) {
func (f *incomingFile) markAndNotifyDone() {
f.mu.Lock()
f.finalPath = finalPath
f.done = true
f.mu.Unlock()
b := f.ph.ps.b
b.sendFileNotify()
@ -432,7 +433,8 @@ func (f *incomingFile) PartialFile() ipn.PartialFile {
Started: f.started,
DeclaredSize: f.size,
Received: f.copied,
FinalPath: f.finalPath,
PartialPath: f.partialPath,
Done: f.done,
}
}
@ -502,6 +504,9 @@ func (h *peerAPIHandler) handlePeerPut(w http.ResponseWriter, r *http.Request) {
w: f,
ph: h,
}
if h.ps.directFileMode {
inFile.partialPath = dstFile
}
h.ps.b.registerIncomingFile(inFile, true)
defer h.ps.b.registerIncomingFile(inFile, false)
n, err := io.Copy(inFile, r.Body)
@ -519,14 +524,8 @@ func (h *peerAPIHandler) handlePeerPut(w http.ResponseWriter, r *http.Request) {
return
}
if h.ps.directFileMode {
finalPath := strings.TrimSuffix(dstFile, partialSuffix)
if err := os.Rename(dstFile, finalPath); err != nil {
h.logf("Rename error: %v", err)
http.Error(w, "error renaming file", http.StatusInternalServerError)
return
}
if inFile != nil { // non-zero length; TODO: notify even for zero length
inFile.markAndNotifyDone(finalPath)
inFile.markAndNotifyDone()
}
}

Loading…
Cancel
Save