aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorn-peugnet <n.peugnet@free.fr>2021-09-30 11:42:33 +0200
committern-peugnet <n.peugnet@free.fr>2021-09-30 11:42:33 +0200
commit69294759ea36c8395a956e418820b236be7fd875 (patch)
treebaaeeb02cbd0f7c8ab5b621a595782f310c54e1c
parentfd9e3e1092637c1f18189d52fb802cf2985d918b (diff)
downloaddna-backup-69294759ea36c8395a956e418820b236be7fd875.tar.gz
dna-backup-69294759ea36c8395a956e418820b236be7fd875.zip
properly manage relative symlinks will CI pass ??
-rw-r--r--TODO.md2
-rw-r--r--repo.go62
-rw-r--r--repo_test.go11
3 files changed, 47 insertions, 28 deletions
diff --git a/TODO.md b/TODO.md
index 54de8f7..0e31070 100644
--- a/TODO.md
+++ b/TODO.md
@@ -50,7 +50,7 @@ priority 2
I stored it in the _files_ file so that we don't need to read the chunks
to get the content of a symlinked file and because it does not seem to
add a lot of weight to this file (after compression).
-- [ ] store and restore symlinks relatively if it was relative in source
+- [x] store and restore symlinks relatively if it was relative in source
directory.
- [ ] add quick progress bar to CLI
- [ ] `list` command to list versions
diff --git a/repo.go b/repo.go
index 05f3a00..543f6c3 100644
--- a/repo.go
+++ b/repo.go
@@ -194,7 +194,11 @@ func (r *Repo) Restore(destination string) {
dir := filepath.Dir(filePath)
os.MkdirAll(dir, 0775) // TODO: handle errors
if file.Link != "" {
- err := os.Symlink(filepath.Join(destination, file.Link), filePath)
+ link := file.Link
+ if filepath.IsAbs(link) {
+ filepath.Join(destination, file.Link)
+ }
+ err := os.Symlink(link, filePath)
if err != nil {
logger.Errorf("restored symlink ", err)
}
@@ -236,30 +240,15 @@ func listFiles(path string) []File {
if i.IsDir() {
return nil
}
- var link string
- var size = i.Size()
+ var file = File{Path: p, Size: i.Size()}
if i.Mode()&fs.ModeSymlink != 0 {
- target, err := filepath.EvalSymlinks(p)
+ file, err = cleanSymlink(path, p, i)
if err != nil {
- logger.Warning(err)
- return nil
- }
- if !strings.HasPrefix(target, path) {
- logger.Warningf("skipping external symlink %s -> %s", p, target)
- return nil
- }
- size = 0
- link, err = filepath.Rel(path, target)
- if err != nil {
- logger.Warning(err)
- return nil
- }
- if link == "" {
- logger.Warningf("skipping empty symlink %s", p)
+ logger.Warning("skipping symlink ", err)
return nil
}
}
- files = append(files, File{p, size, link})
+ files = append(files, file)
return nil
})
if err != nil {
@@ -268,6 +257,39 @@ func listFiles(path string) []File {
return files
}
+func cleanSymlink(root string, p string, i fs.FileInfo) (f File, err error) {
+ dir := filepath.Dir(p)
+ target, err := os.Readlink(p)
+ if err != nil {
+ return
+ }
+ isAbs := filepath.IsAbs(target)
+ cleaned := target
+ if !isAbs {
+ cleaned = filepath.Join(dir, cleaned)
+ }
+ cleaned = filepath.Clean(cleaned)
+ if !strings.HasPrefix(cleaned, root) {
+ err = fmt.Errorf("external %s -> %s", p, cleaned)
+ return
+ }
+ if isAbs {
+ f.Link, err = utils.Unprefix(cleaned, root)
+ } else {
+ f.Link, err = filepath.Rel(dir, filepath.Join(dir, target))
+ }
+ if err != nil {
+ return
+ }
+ if f.Link == "" {
+ err = fmt.Errorf("empty %s", p)
+ return
+ }
+ f.Path = p
+ f.Size = 0
+ return f, nil
+}
+
func unprefixFiles(files []File, prefix string) (ret []File) {
var err error
ret = make([]File, len(files))
diff --git a/repo_test.go b/repo_test.go
index 5a0abea..fda61f5 100644
--- a/repo_test.go
+++ b/repo_test.go
@@ -203,19 +203,16 @@ func TestSymlinks(t *testing.T) {
}
files := listFiles(tmpDir)
fmt.Println(files)
- testutils.AssertLen(t, 2, files, "Files")
+ testutils.AssertLen(t, 3, files, "Files")
if files[0].Link != "" {
- t.Error("linkexisting should not be a link, actual:", files[0].Link)
+ t.Error("existing should not be a link, actual:", files[0].Link)
}
- if files[1].Link != "existing" {
- t.Error("linkexisting should point to 'existing', actual:", files[1].Link)
+ if files[1].Link != "/existing" {
+ t.Error("linkexisting should point to '/existing', actual:", files[1].Link)
}
if !strings.Contains(output.String(), "linkexternal") {
t.Errorf("log should contain a warning for linkexternal, actual %q", &output)
}
- if !strings.Contains(output.String(), "notexisting") {
- t.Errorf("log should contain a warning for notexisting, actual %q", &output)
- }
}
func TestLoadChunks(t *testing.T) {