ipn/ipnlocal: always write files to partial files, even in buffered mode

The intention was always that files only get written to *.partial
files and renamed at the end once fully received, but somewhere in the
process that got lost in buffered mode and *.partial files were only
being used in direct receive mode. This fix prevents WaitingFiles
from returning files that are still being transferred.

Updates tailscale/corp#1626

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/1803/head
Brad Fitzpatrick 3 years ago
parent 5e268e6153
commit 138921ae40

@ -50,10 +50,14 @@ type peerAPIServer struct {
// download directory (as *.partial files), rather than making // download directory (as *.partial files), rather than making
// the frontend retrieve it over localapi HTTP and write it // the frontend retrieve it over localapi HTTP and write it
// somewhere itself. This is used on GUI macOS version. // somewhere itself. This is used on GUI macOS version.
// In directFileMode, the peerapi doesn't do the final rename
// from "foo.jpg.partial" to "foo.jpg".
directFileMode bool directFileMode bool
} }
const ( const (
// partialSuffix is the suffix appened to files while they're
// still in the process of being transferred.
partialSuffix = ".partial" partialSuffix = ".partial"
// deletedSuffix is the suffix for a deleted marker file // deletedSuffix is the suffix for a deleted marker file
@ -608,19 +612,18 @@ func (h *peerAPIHandler) handlePeerPut(w http.ResponseWriter, r *http.Request) {
http.Error(w, "bad filename", 400) http.Error(w, "bad filename", 400)
return return
} }
if h.ps.directFileMode { // TODO(bradfitz): prevent same filename being sent by two peers at once
dstFile += partialSuffix partialFile := dstFile + partialSuffix
} f, err := os.Create(partialFile)
f, err := os.Create(dstFile)
if err != nil { if err != nil {
h.logf("put Create error: %v", err) h.logf("put Create error: %v", redactErr(err))
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
var success bool var success bool
defer func() { defer func() {
if !success { if !success {
os.Remove(dstFile) os.Remove(partialFile)
} }
}() }()
var finalSize int64 var finalSize int64
@ -634,7 +637,7 @@ func (h *peerAPIHandler) handlePeerPut(w http.ResponseWriter, r *http.Request) {
ph: h, ph: h,
} }
if h.ps.directFileMode { if h.ps.directFileMode {
inFile.partialPath = dstFile inFile.partialPath = partialFile
} }
h.ps.b.registerIncomingFile(inFile, true) h.ps.b.registerIncomingFile(inFile, true)
defer h.ps.b.registerIncomingFile(inFile, false) defer h.ps.b.registerIncomingFile(inFile, false)
@ -656,6 +659,13 @@ func (h *peerAPIHandler) handlePeerPut(w http.ResponseWriter, r *http.Request) {
if inFile != nil { // non-zero length; TODO: notify even for zero length if inFile != nil { // non-zero length; TODO: notify even for zero length
inFile.markAndNotifyDone() inFile.markAndNotifyDone()
} }
} else {
if err := os.Rename(partialFile, dstFile); err != nil {
err = redactErr(err)
h.logf("put final rename: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
} }
h.logf("put of %s from %v/%v", approxSize(finalSize), h.remoteAddr.IP, h.peerNode.ComputedName) h.logf("put of %s from %v/%v", approxSize(finalSize), h.remoteAddr.IP, h.peerNode.ComputedName)

Loading…
Cancel
Save