From c18dc57861d8005126b53bdf42a3e44f346d4e7b Mon Sep 17 00:00:00 2001 From: Todd Neal Date: Mon, 6 Dec 2021 22:33:45 -0600 Subject: [PATCH] ipn/{ipnserver,ipnlocal}: support incoming Taildrop on TrueNAS Signed-off-by: Todd Neal --- ipn/ipnlocal/local.go | 6 ++--- ipn/ipnserver/server.go | 49 ++++++++++++++++++++++++++++++++++++----- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index 26ddd46df..29d1dea5b 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -142,11 +142,11 @@ type LocalBackend struct { // same as the Network Extension lifetime and we can thus avoid // double-copying files by writing them to the right location // immediately. - // It's also used on Synology, but in that case DoFinalRename is - // also set true, which moves the *.partial file to its final + // It's also used on Synology & TrueNAS, 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 + directFileDoFinalRename bool // false on macOS, true on Synology & TrueNAS // statusLock must be held before calling statusChanged.Wait() or // statusChanged.Broadcast(). diff --git a/ipn/ipnserver/server.go b/ipn/ipnserver/server.go index 60bf7ee87..ad16621c3 100644 --- a/ipn/ipnserver/server.go +++ b/ipn/ipnserver/server.go @@ -759,17 +759,21 @@ func New(logf logger.Logf, logid string, store ipn.StateStore, eng wgengine.Engi b.SetDecompressor(func() (controlclient.Decompressor, error) { return smallzstd.NewDecoder(nil) }) - if distro.Get() == distro.Synology { + + dg := distro.Get() + switch dg { + case distro.Synology, distro.TrueNAS: // See if they have a "Taildrop" share. // See https://github.com/tailscale/tailscale/issues/2179#issuecomment-982821319 - path, err := findSynologyTaildropDir() + path, err := findTaildropDir(dg) if err != nil { - logf("Synology Taildrop support: %v", err) + logf("%s Taildrop support: %v", dg, err) } else { - logf("Synology Taildrop: using %v", path) + logf("%s Taildrop: using %v", dg, path) b.SetDirectFileRoot(path) b.SetDirectFileDoFinalRename(true) } + } if opts.AutostartStateKey == "" { @@ -1127,11 +1131,21 @@ func (ln *listenerWithReadyConn) Accept() (net.Conn, error) { return ln.Listener.Accept() } +func findTaildropDir(dg distro.Distro) (string, error) { + const name = "Taildrop" + switch dg { + case distro.Synology: + return findSynologyTaildropDir(name) + case distro.TrueNAS: + return findTrueNASTaildropDir(name) + } + return "", fmt.Errorf("%s is an unsupported distro for Taildrop dir", dg) +} + // 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" +func findSynologyTaildropDir(name string) (dir string, err error) { for i := 1; i <= 16; i++ { dir = fmt.Sprintf("/volume%v/%s", i, name) if fi, err := os.Stat(dir); err == nil && fi.IsDir() { @@ -1140,3 +1154,26 @@ func findSynologyTaildropDir() (dir string, err error) { } return "", fmt.Errorf("shared folder %q not found", name) } + +// findTrueNASTaildropDir returns the first matching directory of +// /mnt/{name} or /mnt/*/{name} +func findTrueNASTaildropDir(name string) (dir string, err error) { + // If we're running in a jail, a mount point could just be added at /mnt/Taildrop + dir = fmt.Sprintf("/mnt/%s", name) + if fi, err := os.Stat(dir); err == nil && fi.IsDir() { + return dir, nil + } + + // but if running on the host, it may be something like /mnt/Primary/Taildrop + fis, err := ioutil.ReadDir("/mnt") + if err != nil { + return "", fmt.Errorf("error reading /mnt: %w", err) + } + for _, fi := range fis { + dir = fmt.Sprintf("/mnt/%s/%s", fi.Name(), name) + if fi, err := os.Stat(dir); err == nil && fi.IsDir() { + return dir, nil + } + } + return "", fmt.Errorf("shared folder %q not found", name) +}