Merge pull request #2206 from garrmcnu/fs-stdin-directory

fs: Handle absolute pathname for --stdin-filename
This commit is contained in:
Alexander Neumann 2019-05-08 16:05:37 +02:00
commit 5bd5db4294
4 changed files with 101 additions and 16 deletions

View File

@ -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

View File

@ -8,6 +8,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"path/filepath" "path/filepath"
"strconv" "strconv"
"strings" "strings"
@ -523,13 +524,14 @@ func runBackup(opts BackupOptions, gopts GlobalOptions, term *termstatus.Termina
if !gopts.JSON { if !gopts.JSON {
p.V("read data from stdin") p.V("read data from stdin")
} }
filename := path.Join("/", opts.StdinFilename)
targetFS = &fs.Reader{ targetFS = &fs.Reader{
ModTime: timeStamp, ModTime: timeStamp,
Name: opts.StdinFilename, Name: filename,
Mode: 0644, Mode: 0644,
ReadCloser: os.Stdin, ReadCloser: os.Stdin,
} }
targets = []string{opts.StdinFilename} targets = []string{filename}
} }
sc := archiver.NewScanner(targetFS) sc := archiver.NewScanner(targetFS)

View File

@ -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. // describes the symbolic link. Lstat makes no attempt to follow the link.
// If there is an error, it will be of type *PathError. // If there is an error, it will be of type *PathError.
func (fs *Reader) Lstat(name string) (os.FileInfo, error) { 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 { switch name {
case fs.Name: case fs.Name:
return fs.fi(), nil return fs.fi(), nil
case "/", ".": case "/", ".":
fi := fakeFileInfo{ return getDirInfo(name), nil
name: name, }
size: 0,
mode: 0755, dir := fs.Dir(fs.Name)
modtime: time.Now(), for {
if dir == "/" || dir == "." {
break
} }
return fi, nil if name == dir {
return getDirInfo(name), nil
}
dir = fs.Dir(dir)
} }
return nil, os.ErrNotExist return nil, os.ErrNotExist

View File

@ -4,6 +4,7 @@ import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"os" "os"
"path"
"sort" "sort"
"strings" "strings"
"testing" "testing"
@ -151,8 +152,8 @@ func verifyDirectoryContentsFI(t testing.TB, fs FS, dir string, want []os.FileIn
} }
func checkFileInfo(t testing.TB, fi os.FileInfo, filename string, modtime time.Time, mode os.FileMode, isdir bool) { func checkFileInfo(t testing.TB, fi os.FileInfo, filename string, modtime time.Time, mode os.FileMode, isdir bool) {
if fi.IsDir() { if fi.IsDir() != isdir {
t.Errorf("IsDir returned true, want false") t.Errorf("IsDir returned %t, want %t", fi.IsDir(), isdir)
} }
if fi.Mode() != mode { if fi.Mode() != mode {
@ -163,8 +164,12 @@ func checkFileInfo(t testing.TB, fi os.FileInfo, filename string, modtime time.T
t.Errorf("ModTime() returned wrong value, want %v, got %v", modtime, fi.ModTime()) t.Errorf("ModTime() returned wrong value, want %v, got %v", modtime, fi.ModTime())
} }
if fi.Name() != filename { if path.Base(fi.Name()) != fi.Name() {
t.Errorf("Name() returned wrong value, want %q, got %q", filename, fi.Name()) t.Errorf("Name() returned is not base, want %q, got %q", path.Base(fi.Name()), fi.Name())
}
if fi.Name() != path.Base(filename) {
t.Errorf("Name() returned wrong value, want %q, got %q", path.Base(filename), fi.Name())
} }
} }
@ -265,7 +270,7 @@ func TestFSReader(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
checkFileInfo(t, fi, "/", time.Time{}, 0755, false) checkFileInfo(t, fi, "/", time.Time{}, os.ModeDir|0755, true)
}, },
}, },
{ {
@ -276,7 +281,16 @@ func TestFSReader(t *testing.T) {
t.Fatal(err) 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)
}
}, },
}, },
{ {
@ -287,7 +301,7 @@ func TestFSReader(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
checkFileInfo(t, fi, "/", time.Time{}, 0755, false) checkFileInfo(t, fi, "/", time.Time{}, os.ModeDir|0755, true)
}, },
}, },
{ {
@ -298,7 +312,7 @@ func TestFSReader(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
checkFileInfo(t, fi, ".", time.Time{}, 0755, false) checkFileInfo(t, fi, ".", time.Time{}, os.ModeDir|0755, true)
}, },
}, },
} }
@ -319,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) { func TestFSReaderMinFileSize(t *testing.T) {
var tests = []struct { var tests = []struct {
name string name string