Skip to content

Commit 8541e1f

Browse files
authored
Merge pull request #1807 from FabianKramm/master
fix: fix recursive symlinks
2 parents c38dabf + cf0ab76 commit 8541e1f

File tree

10 files changed

+164
-78
lines changed

10 files changed

+164
-78
lines changed

helper/server/downstream.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"compress/gzip"
66
"context"
77
"github.com/loft-sh/devspace/helper/util/pingtimeout"
8+
"github.com/loft-sh/devspace/pkg/util/fsutil"
89
logpkg "github.com/loft-sh/devspace/pkg/util/log"
910
"io"
1011
"io/ioutil"
@@ -526,6 +527,9 @@ func walkDir(basePath string, path string, ignoreMatcher ignoreparser.IgnorePars
526527

527528
for _, f := range files {
528529
absolutePath := filepath.Join(path, f.Name())
530+
if fsutil.IsRecursiveSymlink(f, absolutePath) {
531+
continue
532+
}
529533

530534
// Stat is necessary here, because readdir does not follow symlinks and
531535
// IsDir() returns false for symlinked folders

helper/server/tar.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package server
33
import (
44
"archive/tar"
55
"compress/gzip"
6+
"github.com/loft-sh/devspace/pkg/util/fsutil"
67
"io"
78
"io/ioutil"
89
"os"
@@ -213,6 +214,10 @@ func tarFolder(basePath string, fileInformation *fileInformation, writtenFiles m
213214

214215
if !skipContents {
215216
for _, f := range files {
217+
if fsutil.IsRecursiveSymlink(f, path.Join(fileInformation.Name, f.Name())) {
218+
continue
219+
}
220+
216221
if err := recursiveTar(basePath, path.Join(fileInformation.Name, f.Name()), writtenFiles, tw, skipContents); err != nil {
217222
return errors.Wrap(err, "recursive tar")
218223
}

helper/server/upstream.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/loft-sh/devspace/helper/util/crc32"
99
"github.com/loft-sh/devspace/helper/util/pingtimeout"
1010
"github.com/loft-sh/devspace/helper/util/stderrlog"
11+
"github.com/loft-sh/devspace/pkg/util/fsutil"
1112
logpkg "github.com/loft-sh/devspace/pkg/util/log"
1213
"github.com/pkg/errors"
1314
"google.golang.org/grpc"
@@ -191,6 +192,9 @@ func (u *Upstream) removeRecursive(absolutePath string) error {
191192
// Loop over directory contents and check if we should delete the contents
192193
for _, f := range files {
193194
absoluteChildPath := filepath.Join(absolutePath, f.Name())
195+
if fsutil.IsRecursiveSymlink(f, absoluteChildPath) {
196+
continue
197+
}
194198

195199
// Check if ignored
196200
if u.ignoreMatcher != nil && !u.ignoreMatcher.RequireFullScan() && u.ignoreMatcher.Matches(absolutePath[len(u.options.UploadPath):], f.IsDir()) {

pkg/devspace/hook/upload.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package hook
33
import (
44
"archive/tar"
55
"compress/gzip"
6+
"github.com/loft-sh/devspace/pkg/util/fsutil"
67
"io"
78
"io/ioutil"
89
"os"
@@ -129,6 +130,10 @@ func recursiveTar(srcBase, srcFile, destBase, destFile string, tw *tar.Writer) e
129130
}
130131
}
131132
for _, f := range files {
133+
if fsutil.IsRecursiveSymlink(f, path.Join(fpath, f.Name())) {
134+
continue
135+
}
136+
132137
if err := recursiveTar(srcBase, path.Join(srcFile, f.Name()), destBase, path.Join(destFile, f.Name()), tw); err != nil {
133138
return err
134139
}

pkg/devspace/sync/downstream.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package sync
33
import (
44
"context"
55
"fmt"
6+
"github.com/loft-sh/devspace/pkg/util/fsutil"
67
"io"
78
"io/ioutil"
89
"os"
10+
"path"
911
"path/filepath"
1012
"strings"
1113
"time"
@@ -437,6 +439,10 @@ func (d *downstream) deleteSafeRecursive(relativePath string, deleteChanges []*r
437439

438440
// Loop over directory contents and check if we should delete the contents
439441
for _, f := range files {
442+
if fsutil.IsRecursiveSymlink(f, path.Join(relativePath, f.Name())) {
443+
continue
444+
}
445+
440446
childRelativePath := filepath.ToSlash(filepath.Join(relativePath, f.Name()))
441447
childAbsFilepath := filepath.Join(d.sync.LocalPath, childRelativePath)
442448
if shouldRemoveLocal(childAbsFilepath, d.sync.fileIndex.fileMap[childRelativePath], d.sync, force) {

pkg/devspace/sync/evaluater.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,9 @@ func shouldUpload(s *Sync, fileInformation *FileInformation, log log.Logger) boo
5656
}
5757

5858
// Exclude changes on the exclude list
59-
if s.ignoreMatcher != nil {
60-
if s.ignoreMatcher.Matches(fileInformation.Name, fileInformation.IsDirectory) {
61-
log.Debugf("Don't upload %s because it is excluded", fileInformation.Name)
62-
return false
63-
}
59+
if s.ignoreMatcher != nil && s.ignoreMatcher.Matches(fileInformation.Name, fileInformation.IsDirectory) {
60+
log.Debugf("Don't upload %s because it is excluded", fileInformation.Name)
61+
return false
6462
}
6563

6664
// Check if we already tracked the path

pkg/devspace/sync/initial.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package sync
22

33
import (
44
"github.com/loft-sh/devspace/helper/server/ignoreparser"
5+
"github.com/loft-sh/devspace/pkg/util/fsutil"
56
"io/ioutil"
67
"os"
78
"path"
@@ -356,6 +357,11 @@ func (i *initialSyncer) calculateLocalDirState(absPath string, stat os.FileInfo,
356357
}
357358

358359
for _, f := range files {
360+
if fsutil.IsRecursiveSymlink(f, filepath.Join(absPath, f.Name())) {
361+
i.o.Log.Debugf("Found recursive symlink at %v", filepath.Join(absPath, f.Name()))
362+
continue
363+
}
364+
359365
err := i.CalculateLocalState(filepath.Join(absPath, f.Name()), localState, ignore)
360366
if err != nil {
361367
return errors.Wrap(err, f.Name())

pkg/devspace/sync/tar.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package sync
33
import (
44
"archive/tar"
55
"compress/gzip"
6+
"github.com/loft-sh/devspace/pkg/util/fsutil"
67
"io"
78
"io/ioutil"
89
"os"
@@ -283,8 +284,8 @@ func (a *Archiver) AddToArchive(relativePath string) error {
283284
}
284285

285286
func (a *Archiver) tarFolder(target *FileInformation, targetStat os.FileInfo) error {
286-
filepath := path.Join(a.basePath, target.Name)
287-
files, err := ioutil.ReadDir(filepath)
287+
filePath := path.Join(a.basePath, target.Name)
288+
files, err := ioutil.ReadDir(filePath)
288289
if err != nil {
289290
// config.Logf("[Upstream] Couldn't read dir %s: %s\n", filepath, err.Error())
290291
return nil
@@ -294,7 +295,7 @@ func (a *Archiver) tarFolder(target *FileInformation, targetStat os.FileInfo) er
294295
// check if not excluded
295296
if a.ignoreMatcher == nil || !a.ignoreMatcher.RequireFullScan() || !a.ignoreMatcher.Matches(target.Name, true) {
296297
// Case empty directory
297-
hdr, _ := tar.FileInfoHeader(targetStat, filepath)
298+
hdr, _ := tar.FileInfoHeader(targetStat, filePath)
298299
hdr.Uid = 0
299300
hdr.Gid = 0
300301
hdr.Mode = fillGo18FileTypeBits(int64(chmodTarEntry(os.FileMode(hdr.Mode))), targetStat)
@@ -308,6 +309,10 @@ func (a *Archiver) tarFolder(target *FileInformation, targetStat os.FileInfo) er
308309
}
309310

310311
for _, f := range files {
312+
if fsutil.IsRecursiveSymlink(f, path.Join(filePath, f.Name())) {
313+
continue
314+
}
315+
311316
if err := a.AddToArchive(path.Join(target.Name, f.Name())); err != nil {
312317
return errors.Wrap(err, "recursive tar "+f.Name())
313318
}
@@ -325,7 +330,7 @@ func (a *Archiver) tarFile(target *FileInformation, targetStat os.FileInfo) erro
325330
}
326331

327332
targetStat, err = os.Stat(filepath)
328-
if err != nil {
333+
if err != nil || targetStat.IsDir() {
329334
// We ignore open file and just treat it as okay
330335
return nil
331336
}

pkg/devspace/sync/upstream.go

Lines changed: 108 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ import (
66
"compress/gzip"
77
"context"
88
"fmt"
9+
"github.com/loft-sh/devspace/pkg/util/fsutil"
910
"io"
11+
"io/ioutil"
1012
"os"
1113
"path"
1214
"path/filepath"
@@ -412,101 +414,138 @@ func (u *upstream) getFileInformationFromEvent(events []notify.EventInfo) ([]*Fi
412414
continue
413415
}
414416

415-
relativePath := getRelativeFromFullPath(fullPath, u.sync.LocalPath)
416-
417417
// Determine what kind of change we got (Create or Remove)
418-
newChange, err := u.evaluateChange(relativePath, fullPath)
418+
relativePath := getRelativeFromFullPath(fullPath, u.sync.LocalPath)
419+
newChanges, err := u.evaluateChange(relativePath, fullPath)
419420
if err != nil {
420421
return nil, errors.Wrap(err, "evaluate change")
421422
}
422423

423-
if newChange != nil {
424-
changes = append(changes, newChange)
425-
}
424+
changes = append(changes, newChanges...)
426425
}
427426
}
428427

429428
return changes, nil
430429
}
431430

432-
func (u *upstream) evaluateChange(relativePath, fullpath string) (*FileInformation, error) {
433-
stat, err := os.Stat(fullpath)
431+
func (u *upstream) evaluateChange(relativePath, fullPath string) ([]*FileInformation, error) {
432+
stat, err := os.Stat(fullPath)
433+
if err != nil {
434+
// Remove symlinks
435+
u.RemoveSymlinks(fullPath)
434436

435-
// File / Folder exist -> Create File or Folder
436-
// if File / Folder does not exist, we create a new remove change
437-
if err == nil {
438-
// Exclude changes on the upload exclude list
439-
if u.sync.uploadIgnoreMatcher != nil {
440-
if u.sync.uploadIgnoreMatcher.Matches(relativePath, stat.IsDir()) {
441-
// Add to file map and prevent download if local file is newer than the remote one
442-
if u.sync.fileIndex.fileMap[relativePath] != nil && u.sync.fileIndex.fileMap[relativePath].Mtime < stat.ModTime().Unix() {
443-
// Add it to the fileMap
444-
u.sync.fileIndex.fileMap[relativePath] = &FileInformation{
445-
Name: relativePath,
446-
Mtime: stat.ModTime().Unix(),
447-
Mode: stat.Mode(),
448-
Size: stat.Size(),
449-
IsDirectory: stat.IsDir(),
450-
}
451-
}
437+
// Check if we should remove path remote
438+
if shouldRemoveRemote(relativePath, u.sync) {
439+
// New Remove Task
440+
return []*FileInformation{
441+
{
442+
Name: relativePath,
443+
},
444+
}, nil
445+
}
446+
447+
return nil, nil
448+
}
452449

453-
return nil, nil
450+
// Exclude changes on the upload exclude list
451+
if u.sync.uploadIgnoreMatcher != nil && u.sync.uploadIgnoreMatcher.Matches(relativePath, stat.IsDir()) {
452+
// Add to file map and prevent download if local file is newer than the remote one
453+
if u.sync.fileIndex.fileMap[relativePath] != nil && u.sync.fileIndex.fileMap[relativePath].Mtime < stat.ModTime().Unix() {
454+
// Add it to the fileMap
455+
u.sync.fileIndex.fileMap[relativePath] = &FileInformation{
456+
Name: relativePath,
457+
Mtime: stat.ModTime().Unix(),
458+
Mode: stat.Mode(),
459+
Size: stat.Size(),
460+
IsDirectory: stat.IsDir(),
454461
}
455462
}
456463

457-
// Check if symbolic link
458-
lstat, err := os.Lstat(fullpath)
459-
if err == nil && lstat.Mode()&os.ModeSymlink != 0 {
460-
_, symlinkExists := u.sync.upstream.symlinks[fullpath]
464+
return nil, nil
465+
}
466+
467+
// File / Folder exist -> Create File or Folder
468+
// if File / Folder does not exist, we create a new remove change
469+
// Check if symbolic link
470+
lstat, err := os.Lstat(fullPath)
471+
if err == nil && lstat.Mode()&os.ModeSymlink != 0 {
472+
_, symlinkExists := u.sync.upstream.symlinks[fullPath]
473+
474+
// Add symlink to map
475+
stat, err = u.sync.upstream.AddSymlink(relativePath, fullPath)
476+
if err != nil {
477+
return nil, errors.Wrap(err, "add symlink")
478+
}
479+
if stat == nil {
480+
return nil, nil
481+
}
461482

462-
// Add symlink to map
463-
stat, err = u.sync.upstream.AddSymlink(relativePath, fullpath)
483+
// Only crawl if symlink wasn't there before and it is a directory
484+
if !symlinkExists && stat.IsDir() {
485+
// Crawl all linked files & folders
486+
err = u.symlinks[fullPath].Crawl()
464487
if err != nil {
465-
return nil, errors.Wrap(err, "add symlink")
466-
}
467-
if stat == nil {
468-
return nil, nil
488+
return nil, errors.Wrap(err, "crawl symlink")
469489
}
490+
}
491+
} else if err != nil {
492+
u.sync.log.Debugf("Error in lstat %s: %v", fullPath, err)
493+
return nil, nil
494+
} else if stat == nil {
495+
return nil, nil
496+
}
470497

471-
// Only crawl if symlink wasn't there before and it is a directory
472-
if !symlinkExists && stat.IsDir() {
473-
// Crawl all linked files & folders
474-
err = u.symlinks[fullpath].Crawl()
475-
if err != nil {
476-
return nil, errors.Wrap(err, "crawl symlink")
477-
}
498+
fileInfo := &FileInformation{
499+
Name: relativePath,
500+
Mtime: stat.ModTime().Unix(),
501+
MtimeNano: stat.ModTime().UnixNano(),
502+
Size: stat.Size(),
503+
Mode: stat.Mode(),
504+
IsDirectory: stat.IsDir(),
505+
IsSymbolicLink: stat.Mode()&os.ModeSymlink != 0,
506+
}
507+
508+
// should we upload the file?
509+
if shouldUpload(u.sync, fileInfo, u.sync.log) {
510+
// New Create Task
511+
return []*FileInformation{fileInfo}, nil
512+
} else if stat.IsDir() {
513+
// if the change is a directory we walk the directory for other potential changes
514+
files, err := ioutil.ReadDir(fullPath)
515+
if err != nil {
516+
// Remove symlinks
517+
u.RemoveSymlinks(fullPath)
518+
519+
// Check if we should remove path remote
520+
if shouldRemoveRemote(relativePath, u.sync) {
521+
// New Remove Task
522+
return []*FileInformation{
523+
{
524+
Name: relativePath,
525+
},
526+
}, nil
478527
}
479-
} else if err != nil {
480-
u.sync.log.Debugf("Error in lstat %s: %v", fullpath, err)
481-
return nil, nil
482-
} else if stat == nil {
528+
483529
return nil, nil
484530
}
485531

486-
fileInfo := &FileInformation{
487-
Name: relativePath,
488-
Mtime: stat.ModTime().Unix(),
489-
MtimeNano: stat.ModTime().UnixNano(),
490-
Size: stat.Size(),
491-
Mode: stat.Mode(),
492-
IsDirectory: stat.IsDir(),
493-
IsSymbolicLink: stat.Mode()&os.ModeSymlink != 0,
494-
}
495-
if shouldUpload(u.sync, fileInfo, u.sync.log) {
496-
// New Create Task
497-
return fileInfo, nil
498-
}
499-
} else {
500-
// Remove symlinks
501-
u.RemoveSymlinks(fullpath)
532+
changes := []*FileInformation{}
533+
for _, f := range files {
534+
newFullPath := filepath.Join(fullPath, f.Name())
535+
newRelativePath := path.Join(relativePath, f.Name())
536+
if fsutil.IsRecursiveSymlink(f, newFullPath) {
537+
continue
538+
}
502539

503-
// Check if we should remove path remote
504-
if shouldRemoveRemote(relativePath, u.sync) {
505-
// New Remove Task
506-
return &FileInformation{
507-
Name: relativePath,
508-
}, nil
540+
otherChanges, err := u.evaluateChange(newRelativePath, newFullPath)
541+
if err != nil {
542+
return nil, errors.Wrap(err, "evaluate change")
543+
}
544+
545+
changes = append(changes, otherChanges...)
509546
}
547+
548+
return changes, nil
510549
}
511550

512551
return nil, nil

0 commit comments

Comments
 (0)