diff --git a/snap/squashfs/squashfs.go b/snap/squashfs/squashfs.go index c656de95354..6cc9451fa9c 100644 --- a/snap/squashfs/squashfs.go +++ b/snap/squashfs/squashfs.go @@ -294,6 +294,12 @@ func (sk skipper) Has(path string) bool { return false } +// pre-4.5 unsquashfs writes a funny header like: +// "Parallel unsquashfs: Using 1 processor" +// "1 inodes (1 blocks) to write" +// "" <-- empty line +var maybeHeaderRegex = regexp.MustCompile(`^(Parallel unsquashfs: Using .* processor.*|[0-9]+ inodes .* to write)$`) + // Walk (part of snap.Container) is like filepath.Walk, without the ordering guarantee. func (s *Snap) Walk(relative string, walkFn filepath.WalkFunc) error { relative = filepath.Clean(relative) @@ -321,16 +327,21 @@ func (s *Snap) Walk(relative string, walkFn filepath.WalkFunc) error { defer cmd.Process.Kill() scanner := bufio.NewScanner(stdout) - // skip the header - for scanner.Scan() { - if len(scanner.Bytes()) == 0 { - break - } - } - skipper := make(skipper) + seenHeader := false for scanner.Scan() { - st, err := fromRaw(scanner.Bytes()) + raw := scanner.Bytes() + if !seenHeader { + // try to match the header written by older (pre-4.5) + // squashfs tools + if len(scanner.Bytes()) == 0 || + maybeHeaderRegex.Match(raw) { + continue + } else { + seenHeader = true + } + } + st, err := fromRaw(raw) if err != nil { err = walkFn(relative, nil, err) if err != nil { diff --git a/snap/squashfs/squashfs_test.go b/snap/squashfs/squashfs_test.go index f510a4466dc..5618e5cc0e4 100644 --- a/snap/squashfs/squashfs_test.go +++ b/snap/squashfs/squashfs_test.go @@ -20,6 +20,7 @@ package squashfs_test import ( + "bytes" "errors" "fmt" "io/ioutil" @@ -348,7 +349,7 @@ func (s *SquashfsTestSuite) TestListDir(c *C) { c.Check(fileNames[2], Equals, "foo-hook") } -func (s *SquashfsTestSuite) TestWalk(c *C) { +func (s *SquashfsTestSuite) TestWalkNative(c *C) { sub := "." sn := makeSnap(c, "name: foo", "") sqw := map[string]os.FileInfo{} @@ -411,6 +412,86 @@ func (s *SquashfsTestSuite) TestWalk(c *C) { } +func (s *SquashfsTestSuite) testWalkMockedUnsquashfs(c *C) { + expectingNames := []string{ + ".", + "data.bin", + "food", + "meta", + "meta/hooks", + "meta/hooks/bar-hook", + "meta/hooks/dir", + "meta/hooks/dir/baz", + "meta/hooks/foo-hook", + "meta/snap.yaml", + } + sub := "." + sn := makeSnap(c, "name: foo", "") + var seen []string + sn.Walk(sub, func(path string, info os.FileInfo, err error) error { + c.Logf("got %v", path) + if err != nil { + return err + } + seen = append(seen, path) + if path == "food" { + return filepath.SkipDir + } + return nil + }) + c.Assert(len(seen), Equals, len(expectingNames)) + for idx, name := range seen { + c.Check(name, Equals, expectingNames[idx]) + } +} + +func (s *SquashfsTestSuite) TestWalkMockedUnsquashfs45(c *C) { + // mock behavior of squashfs-tools 4.5 and later + mockUnsquashfs := testutil.MockCommand(c, "unsquashfs", ` +cat <