mirror of
https://github.com/tailscale/tailscale.git
synced 2025-08-21 10:27:30 +00:00
feature/taildrop: do not use m.opts.Dir for Android (#16316)
In Android, we are prompting the user to select a Taildrop directory when they first receive a Taildrop: we block writes on Taildrop dir selection. This means that we cannot use Dir inside managerOptions, since the http request would not get the new Taildrop extension. This PR removes, in the Android case, the reliance on m.opts.Dir, and instead has FileOps hold the correct directory. This expands FileOps to be the Taildrop interface for all file system operations. Updates tailscale/corp#29211 Signed-off-by: kari-ts <kari@tailscale.com> restore tstest
This commit is contained in:
@@ -12,8 +12,6 @@ package taildrop
|
||||
import (
|
||||
"errors"
|
||||
"hash/adler32"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
@@ -21,7 +19,6 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
@@ -72,11 +69,6 @@ type managerOptions struct {
|
||||
Clock tstime.DefaultClock // may be nil
|
||||
State ipn.StateStore // may be nil
|
||||
|
||||
// Dir is the directory to store received files.
|
||||
// This main either be the final location for the files
|
||||
// or just a temporary staging directory (see DirectFileMode).
|
||||
Dir string
|
||||
|
||||
// DirectFileMode reports whether we are writing files
|
||||
// directly to a download directory, rather than writing them to
|
||||
// a temporary staging directory.
|
||||
@@ -91,9 +83,10 @@ type managerOptions struct {
|
||||
// copy them out, and then delete them.
|
||||
DirectFileMode bool
|
||||
|
||||
FileOps FileOps
|
||||
|
||||
Mode PutMode
|
||||
// FileOps abstracts platform-specific file operations needed for file transfers.
|
||||
// Android's implementation uses the Storage Access Framework, and other platforms
|
||||
// use fsFileOps.
|
||||
fileOps FileOps
|
||||
|
||||
// SendFileNotify is called periodically while a file is actively
|
||||
// receiving the contents for the file. There is a final call
|
||||
@@ -111,9 +104,6 @@ type manager struct {
|
||||
// deleter managers asynchronous deletion of files.
|
||||
deleter fileDeleter
|
||||
|
||||
// renameMu is used to protect os.Rename calls so that they are atomic.
|
||||
renameMu sync.Mutex
|
||||
|
||||
// totalReceived counts the cumulative total of received files.
|
||||
totalReceived atomic.Int64
|
||||
// emptySince specifies that there were no waiting files
|
||||
@@ -137,11 +127,6 @@ func (opts managerOptions) New() *manager {
|
||||
return m
|
||||
}
|
||||
|
||||
// Dir returns the directory.
|
||||
func (m *manager) Dir() string {
|
||||
return m.opts.Dir
|
||||
}
|
||||
|
||||
// Shutdown shuts down the Manager.
|
||||
// It blocks until all spawned goroutines have stopped running.
|
||||
func (m *manager) Shutdown() {
|
||||
@@ -172,57 +157,29 @@ func isPartialOrDeleted(s string) bool {
|
||||
return strings.HasSuffix(s, deletedSuffix) || strings.HasSuffix(s, partialSuffix)
|
||||
}
|
||||
|
||||
func joinDir(dir, baseName string) (fullPath string, err error) {
|
||||
if !utf8.ValidString(baseName) {
|
||||
return "", ErrInvalidFileName
|
||||
}
|
||||
if strings.TrimSpace(baseName) != baseName {
|
||||
return "", ErrInvalidFileName
|
||||
}
|
||||
if len(baseName) > 255 {
|
||||
return "", ErrInvalidFileName
|
||||
func validateBaseName(name string) error {
|
||||
if !utf8.ValidString(name) ||
|
||||
strings.TrimSpace(name) != name ||
|
||||
len(name) > 255 {
|
||||
return ErrInvalidFileName
|
||||
}
|
||||
// TODO: validate unicode normalization form too? Varies by platform.
|
||||
clean := path.Clean(baseName)
|
||||
if clean != baseName ||
|
||||
clean == "." || clean == ".." ||
|
||||
isPartialOrDeleted(clean) {
|
||||
return "", ErrInvalidFileName
|
||||
clean := path.Clean(name)
|
||||
if clean != name || clean == "." || clean == ".." {
|
||||
return ErrInvalidFileName
|
||||
}
|
||||
for _, r := range baseName {
|
||||
if isPartialOrDeleted(name) {
|
||||
return ErrInvalidFileName
|
||||
}
|
||||
for _, r := range name {
|
||||
if !validFilenameRune(r) {
|
||||
return "", ErrInvalidFileName
|
||||
return ErrInvalidFileName
|
||||
}
|
||||
}
|
||||
if !filepath.IsLocal(baseName) {
|
||||
return "", ErrInvalidFileName
|
||||
}
|
||||
return filepath.Join(dir, baseName), nil
|
||||
}
|
||||
|
||||
// rangeDir iterates over the contents of a directory, calling fn for each entry.
|
||||
// It continues iterating while fn returns true.
|
||||
// It reports the number of entries seen.
|
||||
func rangeDir(dir string, fn func(fs.DirEntry) bool) error {
|
||||
f, err := os.Open(dir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
for {
|
||||
des, err := f.ReadDir(10)
|
||||
for _, de := range des {
|
||||
if !fn(de) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if !filepath.IsLocal(name) {
|
||||
return ErrInvalidFileName
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IncomingFiles returns a list of active incoming files.
|
||||
|
Reference in New Issue
Block a user