mirror of
https://github.com/golang/go
synced 2024-11-23 20:30:04 -07:00
archive/zip: error if using io/fs on zip with duplicate entries
Fixes #50390 Change-Id: I92787cdb3fa198ff88dcaadeccfcb49a3a6a88cf Reviewed-on: https://go-review.googlesource.com/c/go/+/374954 Reviewed-by: Heschi Kreinick <heschi@google.com> Reviewed-by: Joseph Tsai <joetsai@digital-static.net> Reviewed-by: Ian Lance Taylor <iant@google.com> Run-TryBot: Ian Lance Taylor <iant@google.com> Auto-Submit: Ian Lance Taylor <iant@google.com> TryBot-Result: Gopher Robot <gobot@golang.org>
This commit is contained in:
parent
960ffa98ce
commit
8d074f61b7
@ -662,6 +662,7 @@ type fileListEntry struct {
|
|||||||
name string
|
name string
|
||||||
file *File
|
file *File
|
||||||
isDir bool
|
isDir bool
|
||||||
|
isDup bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type fileInfoDirEntry interface {
|
type fileInfoDirEntry interface {
|
||||||
@ -669,11 +670,14 @@ type fileInfoDirEntry interface {
|
|||||||
fs.DirEntry
|
fs.DirEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *fileListEntry) stat() fileInfoDirEntry {
|
func (e *fileListEntry) stat() (fileInfoDirEntry, error) {
|
||||||
if !e.isDir {
|
if e.isDup {
|
||||||
return headerFileInfo{&e.file.FileHeader}
|
return nil, errors.New(e.name + ": duplicate entries in zip file")
|
||||||
}
|
}
|
||||||
return e
|
if !e.isDir {
|
||||||
|
return headerFileInfo{&e.file.FileHeader}, nil
|
||||||
|
}
|
||||||
|
return e, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only used for directories.
|
// Only used for directories.
|
||||||
@ -708,17 +712,37 @@ func toValidName(name string) string {
|
|||||||
|
|
||||||
func (r *Reader) initFileList() {
|
func (r *Reader) initFileList() {
|
||||||
r.fileListOnce.Do(func() {
|
r.fileListOnce.Do(func() {
|
||||||
|
// files and knownDirs map from a file/directory name
|
||||||
|
// to an index into the r.fileList entry that we are
|
||||||
|
// building. They are used to mark duplicate entries.
|
||||||
|
files := make(map[string]int)
|
||||||
|
knownDirs := make(map[string]int)
|
||||||
|
|
||||||
|
// dirs[name] is true if name is known to be a directory,
|
||||||
|
// because it appears as a prefix in a path.
|
||||||
dirs := make(map[string]bool)
|
dirs := make(map[string]bool)
|
||||||
knownDirs := make(map[string]bool)
|
|
||||||
for _, file := range r.File {
|
for _, file := range r.File {
|
||||||
isDir := len(file.Name) > 0 && file.Name[len(file.Name)-1] == '/'
|
isDir := len(file.Name) > 0 && file.Name[len(file.Name)-1] == '/'
|
||||||
name := toValidName(file.Name)
|
name := toValidName(file.Name)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if idx, ok := files[name]; ok {
|
||||||
|
r.fileList[idx].isDup = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if idx, ok := knownDirs[name]; ok {
|
||||||
|
r.fileList[idx].isDup = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
for dir := path.Dir(name); dir != "."; dir = path.Dir(dir) {
|
for dir := path.Dir(name); dir != "."; dir = path.Dir(dir) {
|
||||||
dirs[dir] = true
|
dirs[dir] = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idx := len(r.fileList)
|
||||||
entry := fileListEntry{
|
entry := fileListEntry{
|
||||||
name: name,
|
name: name,
|
||||||
file: file,
|
file: file,
|
||||||
@ -726,17 +750,23 @@ func (r *Reader) initFileList() {
|
|||||||
}
|
}
|
||||||
r.fileList = append(r.fileList, entry)
|
r.fileList = append(r.fileList, entry)
|
||||||
if isDir {
|
if isDir {
|
||||||
knownDirs[name] = true
|
knownDirs[name] = idx
|
||||||
|
} else {
|
||||||
|
files[name] = idx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for dir := range dirs {
|
for dir := range dirs {
|
||||||
if !knownDirs[dir] {
|
if _, ok := knownDirs[dir]; !ok {
|
||||||
entry := fileListEntry{
|
if idx, ok := files[dir]; ok {
|
||||||
name: dir,
|
r.fileList[idx].isDup = true
|
||||||
file: nil,
|
} else {
|
||||||
isDir: true,
|
entry := fileListEntry{
|
||||||
|
name: dir,
|
||||||
|
file: nil,
|
||||||
|
isDir: true,
|
||||||
|
}
|
||||||
|
r.fileList = append(r.fileList, entry)
|
||||||
}
|
}
|
||||||
r.fileList = append(r.fileList, entry)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -831,7 +861,7 @@ type openDir struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *openDir) Close() error { return nil }
|
func (d *openDir) Close() error { return nil }
|
||||||
func (d *openDir) Stat() (fs.FileInfo, error) { return d.e.stat(), nil }
|
func (d *openDir) Stat() (fs.FileInfo, error) { return d.e.stat() }
|
||||||
|
|
||||||
func (d *openDir) Read([]byte) (int, error) {
|
func (d *openDir) Read([]byte) (int, error) {
|
||||||
return 0, &fs.PathError{Op: "read", Path: d.e.name, Err: errors.New("is a directory")}
|
return 0, &fs.PathError{Op: "read", Path: d.e.name, Err: errors.New("is a directory")}
|
||||||
@ -850,7 +880,11 @@ func (d *openDir) ReadDir(count int) ([]fs.DirEntry, error) {
|
|||||||
}
|
}
|
||||||
list := make([]fs.DirEntry, n)
|
list := make([]fs.DirEntry, n)
|
||||||
for i := range list {
|
for i := range list {
|
||||||
list[i] = d.files[d.offset+i].stat()
|
s, err := d.files[d.offset+i].stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
list[i] = s
|
||||||
}
|
}
|
||||||
d.offset += n
|
d.offset += n
|
||||||
return list, nil
|
return list, nil
|
||||||
|
@ -505,6 +505,35 @@ var tests = []ZipTest{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "dupdir.zip",
|
||||||
|
File: []ZipTestFile{
|
||||||
|
{
|
||||||
|
Name: "a/",
|
||||||
|
Content: []byte{},
|
||||||
|
Mode: fs.ModeDir | 0666,
|
||||||
|
Modified: time.Date(2021, 12, 29, 0, 0, 0, 0, timeZone(0)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "a/b",
|
||||||
|
Content: []byte{},
|
||||||
|
Mode: 0666,
|
||||||
|
Modified: time.Date(2021, 12, 29, 0, 0, 0, 0, timeZone(0)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "a/b/",
|
||||||
|
Content: []byte{},
|
||||||
|
Mode: fs.ModeDir | 0666,
|
||||||
|
Modified: time.Date(2021, 12, 29, 0, 0, 0, 0, timeZone(0)),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "a/b/c",
|
||||||
|
Content: []byte{},
|
||||||
|
Mode: 0666,
|
||||||
|
Modified: time.Date(2021, 12, 29, 0, 0, 0, 0, timeZone(0)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestReader(t *testing.T) {
|
func TestReader(t *testing.T) {
|
||||||
@ -1141,6 +1170,7 @@ func TestFS(t *testing.T) {
|
|||||||
[]string{"a/b/c"},
|
[]string{"a/b/c"},
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
|
test := test
|
||||||
t.Run(test.file, func(t *testing.T) {
|
t.Run(test.file, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
z, err := OpenReader(test.file)
|
z, err := OpenReader(test.file)
|
||||||
@ -1155,6 +1185,60 @@ func TestFS(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFSWalk(t *testing.T) {
|
||||||
|
for _, test := range []struct {
|
||||||
|
file string
|
||||||
|
want []string
|
||||||
|
wantErr bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
file: "testdata/unix.zip",
|
||||||
|
want: []string{".", "dir", "dir/bar", "dir/empty", "hello", "readonly"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "testdata/subdir.zip",
|
||||||
|
want: []string{".", "a", "a/b", "a/b/c"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
file: "testdata/dupdir.zip",
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
test := test
|
||||||
|
t.Run(test.file, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
z, err := OpenReader(test.file)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
var files []string
|
||||||
|
sawErr := false
|
||||||
|
err = fs.WalkDir(z, ".", func(path string, d fs.DirEntry, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
if !test.wantErr {
|
||||||
|
t.Errorf("%s: %v", path, err)
|
||||||
|
}
|
||||||
|
sawErr = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
files = append(files, path)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("fs.WalkDir error: %v", err)
|
||||||
|
}
|
||||||
|
if test.wantErr && !sawErr {
|
||||||
|
t.Error("succeeded but want error")
|
||||||
|
} else if !test.wantErr && sawErr {
|
||||||
|
t.Error("unexpected error")
|
||||||
|
}
|
||||||
|
if test.want != nil && !reflect.DeepEqual(files, test.want) {
|
||||||
|
t.Errorf("got %v want %v", files, test.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFSModTime(t *testing.T) {
|
func TestFSModTime(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
z, err := OpenReader("testdata/subdir.zip")
|
z, err := OpenReader("testdata/subdir.zip")
|
||||||
|
BIN
src/archive/zip/testdata/dupdir.zip
vendored
Normal file
BIN
src/archive/zip/testdata/dupdir.zip
vendored
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user