diff --git a/changelog/unreleased/issue-2063 b/changelog/unreleased/issue-2063 new file mode 100644 index 000000000..3840da5be --- /dev/null +++ b/changelog/unreleased/issue-2063 @@ -0,0 +1,6 @@ +Bugfix: Allow absolute path for filename when backing up from stdin + +When backing up from stdin, handle directory path for `--stdin-filename`. +This can be used to specify the full path for the backed-up file. + +https://github.com/restic/restic/issues/2063 diff --git a/internal/fs/fs_reader.go b/internal/fs/fs_reader.go index 27fc46eb6..91fedb263 100644 --- a/internal/fs/fs_reader.go +++ b/internal/fs/fs_reader.go @@ -103,17 +103,32 @@ func (fs *Reader) Stat(name string) (os.FileInfo, error) { // describes the symbolic link. Lstat makes no attempt to follow the link. // If there is an error, it will be of type *PathError. func (fs *Reader) Lstat(name string) (os.FileInfo, error) { + getDirInfo := func(name string) os.FileInfo { + fi := fakeFileInfo{ + name: fs.Base(name), + size: 0, + mode: os.ModeDir | 0755, + modtime: time.Now(), + } + return fi + } + switch name { case fs.Name: return fs.fi(), nil case "/", ".": - fi := fakeFileInfo{ - name: name, - size: 0, - mode: 0755, - modtime: time.Now(), + return getDirInfo(name), nil + } + + dir := fs.Dir(fs.Name) + for { + if dir == "/" || dir == "." { + break } - return fi, nil + if name == dir { + return getDirInfo(name), nil + } + dir = fs.Dir(dir) } return nil, os.ErrNotExist diff --git a/internal/fs/fs_reader_test.go b/internal/fs/fs_reader_test.go index 6d7bf4df3..bb45732f3 100644 --- a/internal/fs/fs_reader_test.go +++ b/internal/fs/fs_reader_test.go @@ -270,7 +270,7 @@ func TestFSReader(t *testing.T) { t.Fatal(err) } - checkFileInfo(t, fi, "/", time.Time{}, 0755, false) + checkFileInfo(t, fi, "/", time.Time{}, os.ModeDir|0755, true) }, }, { @@ -281,7 +281,16 @@ func TestFSReader(t *testing.T) { t.Fatal(err) } - checkFileInfo(t, fi, ".", time.Time{}, 0755, false) + checkFileInfo(t, fi, ".", time.Time{}, os.ModeDir|0755, true) + }, + }, + { + name: "dir/Lstat-error-not-exist", + f: func(t *testing.T, fs FS) { + _, err := fs.Lstat("other") + if err != os.ErrNotExist { + t.Fatal(err) + } }, }, { @@ -292,7 +301,7 @@ func TestFSReader(t *testing.T) { t.Fatal(err) } - checkFileInfo(t, fi, "/", time.Time{}, 0755, false) + checkFileInfo(t, fi, "/", time.Time{}, os.ModeDir|0755, true) }, }, { @@ -303,7 +312,7 @@ func TestFSReader(t *testing.T) { t.Fatal(err) } - checkFileInfo(t, fi, ".", time.Time{}, 0755, false) + checkFileInfo(t, fi, ".", time.Time{}, os.ModeDir|0755, true) }, }, } @@ -324,6 +333,54 @@ func TestFSReader(t *testing.T) { } } +func TestFSReaderDir(t *testing.T) { + data := test.Random(55, 1<<18+588) + now := time.Now() + + var tests = []struct { + name string + filename string + }{ + { + name: "Lstat-absolute", + filename: "/path/to/foobar", + }, + { + name: "Lstat-relative", + filename: "path/to/foobar", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + fs := &Reader{ + Name: test.filename, + ReadCloser: ioutil.NopCloser(bytes.NewReader(data)), + + Mode: 0644, + Size: int64(len(data)), + ModTime: now, + } + + dir := path.Dir(fs.Name) + for { + if dir == "/" || dir == "." { + break + } + + fi, err := fs.Lstat(dir) + if err != nil { + t.Fatal(err) + } + + checkFileInfo(t, fi, dir, time.Time{}, os.ModeDir|0755, true) + + dir = path.Dir(dir) + } + }) + } +} + func TestFSReaderMinFileSize(t *testing.T) { var tests = []struct { name string