diff --git a/ipn/ipnlocal/local.go b/ipn/ipnlocal/local.go index b3ddc4856..9ba182203 100644 --- a/ipn/ipnlocal/local.go +++ b/ipn/ipnlocal/local.go @@ -1994,6 +1994,16 @@ func (b *LocalBackend) WaitingFiles() ([]WaitingFile, error) { return apiSrv.WaitingFiles() } +func (b *LocalBackend) MoveFilesTo(dir string) (filesMoved []string, err error) { + b.mu.Lock() + apiSrv := b.peerAPIServer + b.mu.Unlock() + if apiSrv == nil { + return nil, errors.New("peerapi disabled") + } + return apiSrv.MoveFilesTo(dir) +} + func (b *LocalBackend) DeleteFile(name string) error { b.mu.Lock() apiSrv := b.peerAPIServer diff --git a/ipn/ipnlocal/peerapi.go b/ipn/ipnlocal/peerapi.go index 568c6a811..e4294a942 100644 --- a/ipn/ipnlocal/peerapi.go +++ b/ipn/ipnlocal/peerapi.go @@ -135,6 +135,51 @@ func (s *peerAPIServer) WaitingFiles() (ret []WaitingFile, err error) { return ret, nil } +func (s *peerAPIServer) MoveFilesTo(dir string) (filesMoved []string, err error) { + defer func() { + if err != nil { + err = fmt.Errorf("MoveFilesTo: %w", err) + } + }() + + if s.rootDir == "" { + return nil, errors.New("peerapi disabled; reconsider life choices TODO") + } + f, err := os.Open(s.rootDir) + if err != nil { + return nil, err + } + defer f.Close() + for { + des, err := f.ReadDir(10) + if err == io.EOF { + break + } + if err != nil { + return filesMoved, err + } + for _, de := range des { + if strings.HasSuffix(de.Name(), partialSuffix) { + continue + } + if !de.Type().IsRegular() { + continue + } + tsdir := filepath.Join(dir, "Tailscale") + if err := os.MkdirAll(tsdir, 0777); err != nil { + return filesMoved, err + } + dst := filepath.Join(tsdir, filepath.Base(de.Name())) + err = os.Rename(filepath.Join(s.rootDir, de.Name()), dst) + if err != nil { + return filesMoved, err + } + filesMoved = append(filesMoved, dst) + } + } + return filesMoved, nil +} + func (s *peerAPIServer) DeleteFile(baseName string) error { if s.rootDir == "" { return errors.New("peerapi disabled; no storage configured")