mirror of
https://github.com/tailscale/tailscale.git
synced 2024-12-04 15:35:38 +00:00
ipn/{ipnserver,ipnlocal}: support incoming Taildrop on Synology
If the user has a "Taildrop" shared folder on startup and
the "tailscale" system user has read/write access to it,
then the user can "tailscale file cp" to their NAS.
Updates #2179 (would be fixes, but not super ideal/easy yet)
Change-Id: I68e59a99064b302abeb6d8cc84f7d2a09f764990
Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
(cherry picked from commit abc00e9c8d
)
This commit is contained in:
parent
2d6404609d
commit
6c44133d8f
@ -139,7 +139,11 @@ type LocalBackend struct {
|
|||||||
// same as the Network Extension lifetime and we can thus avoid
|
// same as the Network Extension lifetime and we can thus avoid
|
||||||
// double-copying files by writing them to the right location
|
// double-copying files by writing them to the right location
|
||||||
// immediately.
|
// immediately.
|
||||||
directFileRoot string
|
// It's also used on Synology, but in that case DoFinalRename is
|
||||||
|
// also set true, which moves the *.partial file to its final
|
||||||
|
// name on completion.
|
||||||
|
directFileRoot string
|
||||||
|
directFileDoFinalRename bool // false on macOS, true on Synology
|
||||||
|
|
||||||
// statusLock must be held before calling statusChanged.Wait() or
|
// statusLock must be held before calling statusChanged.Wait() or
|
||||||
// statusChanged.Broadcast().
|
// statusChanged.Broadcast().
|
||||||
@ -219,6 +223,17 @@ func (b *LocalBackend) SetDirectFileRoot(dir string) {
|
|||||||
b.directFileRoot = dir
|
b.directFileRoot = dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDirectFileDoFinalRename sets whether the peerapi file server should rename
|
||||||
|
// a received "name.partial" file to "name" when the download is complete.
|
||||||
|
//
|
||||||
|
// This only applies when SetDirectFileRoot is non-empty.
|
||||||
|
// The default is false.
|
||||||
|
func (b *LocalBackend) SetDirectFileDoFinalRename(v bool) {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
b.directFileDoFinalRename = v
|
||||||
|
}
|
||||||
|
|
||||||
// b.mu must be held.
|
// b.mu must be held.
|
||||||
func (b *LocalBackend) maybePauseControlClientLocked() {
|
func (b *LocalBackend) maybePauseControlClientLocked() {
|
||||||
if b.cc == nil {
|
if b.cc == nil {
|
||||||
@ -2145,11 +2160,12 @@ func (b *LocalBackend) initPeerAPIListener() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ps := &peerAPIServer{
|
ps := &peerAPIServer{
|
||||||
b: b,
|
b: b,
|
||||||
rootDir: fileRoot,
|
rootDir: fileRoot,
|
||||||
tunName: tunName,
|
selfNode: selfNode,
|
||||||
selfNode: selfNode,
|
tunName: tunName,
|
||||||
directFileMode: b.directFileRoot != "",
|
directFileMode: b.directFileRoot != "",
|
||||||
|
directFileDoFinalRename: b.directFileDoFinalRename,
|
||||||
}
|
}
|
||||||
b.peerAPIServer = ps
|
b.peerAPIServer = ps
|
||||||
|
|
||||||
|
@ -52,10 +52,17 @@ type peerAPIServer struct {
|
|||||||
// directFileMode is whether we're writing files directly to a
|
// directFileMode is whether we're writing files directly to a
|
||||||
// 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 the GUI macOS versions
|
||||||
|
// and on Synology.
|
||||||
// In directFileMode, the peerapi doesn't do the final rename
|
// In directFileMode, the peerapi doesn't do the final rename
|
||||||
// from "foo.jpg.partial" to "foo.jpg".
|
// from "foo.jpg.partial" to "foo.jpg" unless
|
||||||
|
// directFileDoFinalRename is set.
|
||||||
directFileMode bool
|
directFileMode bool
|
||||||
|
|
||||||
|
// directFileDoFinalRename is whether in directFileMode we
|
||||||
|
// additionally move the *.direct file to its final name after
|
||||||
|
// it's received.
|
||||||
|
directFileDoFinalRename bool
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -671,7 +678,7 @@ func (h *peerAPIHandler) handlePeerPut(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if h.ps.directFileMode {
|
if h.ps.directFileMode && !h.ps.directFileDoFinalRename {
|
||||||
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()
|
||||||
}
|
}
|
||||||
|
@ -757,6 +757,18 @@ func New(logf logger.Logf, logid string, store ipn.StateStore, eng wgengine.Engi
|
|||||||
b.SetDecompressor(func() (controlclient.Decompressor, error) {
|
b.SetDecompressor(func() (controlclient.Decompressor, error) {
|
||||||
return smallzstd.NewDecoder(nil)
|
return smallzstd.NewDecoder(nil)
|
||||||
})
|
})
|
||||||
|
if distro.Get() == distro.Synology {
|
||||||
|
// See if they have a "Taildrop" share.
|
||||||
|
// See https://github.com/tailscale/tailscale/issues/2179#issuecomment-982821319
|
||||||
|
path, err := findSynologyTaildropDir()
|
||||||
|
if err != nil {
|
||||||
|
logf("Synology Taildrop support: %v", err)
|
||||||
|
} else {
|
||||||
|
logf("Synology Taildrop: using %v", path)
|
||||||
|
b.SetDirectFileRoot(path)
|
||||||
|
b.SetDirectFileDoFinalRename(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if opts.AutostartStateKey == "" {
|
if opts.AutostartStateKey == "" {
|
||||||
autoStartKey, err := store.ReadState(ipn.ServerModeStartKey)
|
autoStartKey, err := store.ReadState(ipn.ServerModeStartKey)
|
||||||
@ -1112,3 +1124,17 @@ func (ln *listenerWithReadyConn) Accept() (net.Conn, error) {
|
|||||||
}
|
}
|
||||||
return ln.Listener.Accept()
|
return ln.Listener.Accept()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// findSynologyTaildropDir looks for the first volume containing a
|
||||||
|
// "Taildrop" directory. We'd run "synoshare --get Taildrop" command
|
||||||
|
// but on DSM7 at least, we lack permissions to run that.
|
||||||
|
func findSynologyTaildropDir() (dir string, err error) {
|
||||||
|
const name = "Taildrop"
|
||||||
|
for i := 1; i <= 16; i++ {
|
||||||
|
dir = fmt.Sprintf("/volume%v/%s", i, name)
|
||||||
|
if fi, err := os.Stat(dir); err == nil && fi.IsDir() {
|
||||||
|
return dir, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("shared folder %q not found", name)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user