Starting on writing the library
Currently just parses the superblock (but that works!)
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
# GoSquashfs
|
# GoSquashfs
|
||||||
My playground to mess around with Squashfs in Go. Might turn into an actual library someday. Mainly for AppImage
|
My playground to mess around with Squashfs in Go. Might turn into an actual library someday. Mainly for AppImage
|
||||||
|
|
||||||
|
Right Now it's mostly based on [distri's squashfs library](https://github.com/distr1/distri/tree/master/internal/squashfs)
|
||||||
|
|
||||||
# Ideas
|
# Ideas
|
||||||
* Link directly to squashfs-tool using cgo
|
* Link directly to squashfs-tool using cgo
|
||||||
* cgo can be a butt
|
* cgo can be a butt
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package squashfs
|
||||||
|
|
||||||
|
//CompressionOptions
|
||||||
|
type CompressionOptions interface {
|
||||||
|
Decompress([]byte) []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: Allow creation of options for compression.
|
||||||
|
|
||||||
|
type gzipOptionsRaw struct {
|
||||||
|
compressionLevel int32
|
||||||
|
windowSize int16
|
||||||
|
strategies int16
|
||||||
|
}
|
||||||
|
|
||||||
|
//GzipOptions is the options used for gzip compression. Backed by the raw format, with strategies parsed.
|
||||||
|
type GzipOptions struct {
|
||||||
|
CompressionOptions
|
||||||
|
raw *gzipOptionsRaw
|
||||||
|
DefaultStrategy bool
|
||||||
|
FilteredStrategy bool
|
||||||
|
HuffmanOnlyStrategy bool
|
||||||
|
RunLengthEncodedStrategy bool
|
||||||
|
FixedStretegy bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type xzOptionsRaw struct {
|
||||||
|
dictionarySize int32
|
||||||
|
executableFilters int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type lz4OptionsRaw struct {
|
||||||
|
version int32
|
||||||
|
flags int32
|
||||||
|
}
|
||||||
|
|
||||||
|
//ZstdOptions is the options set for zstdOptions
|
||||||
|
type ZstdOptions struct {
|
||||||
|
CompressionLevel int32 //CompressionLevel should be between 1 and 22
|
||||||
|
}
|
||||||
|
|
||||||
|
type lzoOptionsRaw struct {
|
||||||
|
algorithm int32
|
||||||
|
compressionLevel int32
|
||||||
|
}
|
||||||
+553
-553
File diff suppressed because it is too large
Load Diff
+328
-328
@@ -1,373 +1,373 @@
|
|||||||
package squashfs
|
package squashfs
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"crypto/md5"
|
// "crypto/md5"
|
||||||
"fmt"
|
// "fmt"
|
||||||
"io"
|
// "io"
|
||||||
"os"
|
// "os"
|
||||||
"testing"
|
// "testing"
|
||||||
"time"
|
// "time"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
// "github.com/google/go-cmp/cmp"
|
||||||
)
|
// )
|
||||||
|
|
||||||
func cmpFileInfo(got os.FileInfo, want FileInfo) error {
|
// func cmpFileInfo(got os.FileInfo, want FileInfo) error {
|
||||||
if got, want := got.Name(), want.name; got != want {
|
// if got, want := got.Name(), want.name; got != want {
|
||||||
return fmt.Errorf("unexpected file name: got %q, want %q", got, want)
|
// return fmt.Errorf("unexpected file name: got %q, want %q", got, want)
|
||||||
}
|
// }
|
||||||
if got, want := got.Size(), want.size; got != want {
|
// if got, want := got.Size(), want.size; got != want {
|
||||||
return fmt.Errorf("unexpected size: got %d, want %d", got, want)
|
// return fmt.Errorf("unexpected size: got %d, want %d", got, want)
|
||||||
}
|
// }
|
||||||
if got, want := got.IsDir(), want.mode.IsDir(); got != want {
|
// if got, want := got.IsDir(), want.mode.IsDir(); got != want {
|
||||||
return fmt.Errorf("IsDir: got %v, want %v", got, want)
|
// return fmt.Errorf("IsDir: got %v, want %v", got, want)
|
||||||
}
|
// }
|
||||||
// TODO: re-enable when it’s no longer just a change detector
|
// // TODO: re-enable when it’s no longer just a change detector
|
||||||
// if got, want := got.ModTime(), want.modTime; !got.Equal(want) {
|
// // if got, want := got.ModTime(), want.modTime; !got.Equal(want) {
|
||||||
// return fmt.Errorf("IsDir: got %v, want %v", got, want)
|
// // return fmt.Errorf("IsDir: got %v, want %v", got, want)
|
||||||
// }
|
// // }
|
||||||
|
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func TestReaddir(t *testing.T) {
|
// func TestReaddir(t *testing.T) {
|
||||||
t.Parallel()
|
// t.Parallel()
|
||||||
// TODO: ship testdata files generated by mksquashfs
|
// // TODO: ship testdata files generated by mksquashfs
|
||||||
pwd, err := os.Getwd()
|
// pwd, err := os.Getwd()
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
fmt.Println(pwd + "/testdata/testing.squashfs")
|
// fmt.Println(pwd + "/testdata/testing.squashfs")
|
||||||
f, err := os.Open(pwd + "/testdata/testing.squashfs")
|
// f, err := os.Open(pwd + "/testdata/testing.squashfs")
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
defer f.Close()
|
// defer f.Close()
|
||||||
rd, err := NewReader(f)
|
// rd, err := NewReader(f)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
fis, err := rd.Readdir(rd.RootInode())
|
// fis, err := rd.Readdir(rd.RootInode())
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
for _, fi := range fis {
|
// for _, fi := range fis {
|
||||||
fmt.Println(fi.Name())
|
// fmt.Println(fi.Name())
|
||||||
}
|
// }
|
||||||
rdr, err := rd.FileReader(fis[0].Sys().(*FileInfo).Inode)
|
// rdr, err := rd.FileReader(fis[0].Sys().(*FileInfo).Inode)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
err = os.Remove(pwd + "/testdata/Magisk.zip")
|
// err = os.Remove(pwd + "/testdata/Magisk.zip")
|
||||||
if !os.IsNotExist(err) && err != nil {
|
// if !os.IsNotExist(err) && err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
// rdrzlib, err := zlib.NewReader(rdr)
|
// // rdrzlib, err := zlib.NewReader(rdr)
|
||||||
magFil, err := os.Create(pwd + "/testdata/Magisk.zip")
|
// magFil, err := os.Create(pwd + "/testdata/Magisk.zip")
|
||||||
io.Copy(magFil, rdr)
|
// io.Copy(magFil, rdr)
|
||||||
if got, want := len(fis), 3; got != want {
|
// if got, want := len(fis), 3; got != want {
|
||||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := cmpFileInfo(fis[0], FileInfo{
|
// if err := cmpFileInfo(fis[0], FileInfo{
|
||||||
name: "bin",
|
// name: "bin",
|
||||||
size: 26,
|
// size: 26,
|
||||||
mode: 0555 | os.ModeDir,
|
// mode: 0555 | os.ModeDir,
|
||||||
modTime: time.Unix(1581275131, 0), // stat -c %Y /ro/ack-amd64-2.24/bin
|
// modTime: time.Unix(1581275131, 0), // stat -c %Y /ro/ack-amd64-2.24/bin
|
||||||
}); err != nil {
|
// }); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := cmpFileInfo(fis[1], FileInfo{
|
// if err := cmpFileInfo(fis[1], FileInfo{
|
||||||
name: "lib",
|
// name: "lib",
|
||||||
size: 3,
|
// size: 3,
|
||||||
mode: 0555 | os.ModeDir,
|
// mode: 0555 | os.ModeDir,
|
||||||
modTime: time.Unix(1581275131, 0), // stat -c %Y /ro/ack-amd64-2.24/lib
|
// modTime: time.Unix(1581275131, 0), // stat -c %Y /ro/ack-amd64-2.24/lib
|
||||||
}); err != nil {
|
// }); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := cmpFileInfo(fis[2], FileInfo{
|
// if err := cmpFileInfo(fis[2], FileInfo{
|
||||||
name: "out",
|
// name: "out",
|
||||||
size: 48,
|
// size: 48,
|
||||||
mode: 0555 | os.ModeDir,
|
// mode: 0555 | os.ModeDir,
|
||||||
modTime: time.Unix(1581275130, 0), // stat -c %Y /ro/ack-amd64-2.24/out
|
// modTime: time.Unix(1581275130, 0), // stat -c %Y /ro/ack-amd64-2.24/out
|
||||||
}); err != nil {
|
// }); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fis, err = rd.Readdir(fis[0].Sys().(*FileInfo).Inode)
|
// fis, err = rd.Readdir(fis[0].Sys().(*FileInfo).Inode)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if got, want := len(fis), 1; got != want {
|
// if got, want := len(fis), 1; got != want {
|
||||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := cmpFileInfo(fis[0], FileInfo{
|
// if err := cmpFileInfo(fis[0], FileInfo{
|
||||||
name: "ack",
|
// name: "ack",
|
||||||
size: 38400,
|
// size: 38400,
|
||||||
mode: 0755,
|
// mode: 0755,
|
||||||
modTime: time.Unix(1581275131, 0), // stat -c %Y /ro/ack-amd64-2.24/bin/ack
|
// modTime: time.Unix(1581275131, 0), // stat -c %Y /ro/ack-amd64-2.24/bin/ack
|
||||||
}); err != nil {
|
// }); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// TestReaddirSmoke is a smoke-test, reading the root directories of SquashFS
|
// // TestReaddirSmoke is a smoke-test, reading the root directories of SquashFS
|
||||||
// images which are known to trigger code paths which were buggy.
|
// // images which are known to trigger code paths which were buggy.
|
||||||
func TestReaddirSmoke(t *testing.T) {
|
// func TestReaddirSmoke(t *testing.T) {
|
||||||
t.Parallel()
|
// t.Parallel()
|
||||||
|
|
||||||
for _, fn := range []string{
|
// for _, fn := range []string{
|
||||||
// bash exercises the code path where an inode is split across metadata
|
// // bash exercises the code path where an inode is split across metadata
|
||||||
// blocks.
|
// // blocks.
|
||||||
"/home/michael/distri/_build/distri/pkg/bash-amd64-5.0-4.squashfs",
|
// "/home/michael/distri/_build/distri/pkg/bash-amd64-5.0-4.squashfs",
|
||||||
|
|
||||||
// cmake exercises the code path where the root directory entries are
|
// // cmake exercises the code path where the root directory entries are
|
||||||
// located outside of the first block.
|
// // located outside of the first block.
|
||||||
"/home/michael/distri/_build/distri/pkg/cmake-amd64-3.12.4-8.squashfs",
|
// "/home/michael/distri/_build/distri/pkg/cmake-amd64-3.12.4-8.squashfs",
|
||||||
} {
|
// } {
|
||||||
// TODO: ship testdata files generated by mksquashfs
|
// // TODO: ship testdata files generated by mksquashfs
|
||||||
f, err := os.Open(fn)
|
// f, err := os.Open(fn)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
defer f.Close()
|
// defer f.Close()
|
||||||
rd, err := NewReader(f)
|
// rd, err := NewReader(f)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fis, err := rd.Readdir(rd.RootInode())
|
// fis, err := rd.Readdir(rd.RootInode())
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if got, want := len(fis), 4; got != want {
|
// if got, want := len(fis), 4; got != want {
|
||||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func TestReaddirEmpty(t *testing.T) {
|
// func TestReaddirEmpty(t *testing.T) {
|
||||||
t.Parallel()
|
// t.Parallel()
|
||||||
// TODO: ship testdata files generated by mksquashfs
|
// // TODO: ship testdata files generated by mksquashfs
|
||||||
f, err := os.Open("/home/michael/distri/_build/distri/pkg/zlib-amd64-1.2.11-4.squashfs")
|
// f, err := os.Open("/home/michael/distri/_build/distri/pkg/zlib-amd64-1.2.11-4.squashfs")
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
defer f.Close()
|
// defer f.Close()
|
||||||
rd, err := NewReader(f)
|
// rd, err := NewReader(f)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fis, err := rd.Readdir(rd.RootInode())
|
// fis, err := rd.Readdir(rd.RootInode())
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if got, want := len(fis), 4; got != want {
|
// if got, want := len(fis), 4; got != want {
|
||||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := cmpFileInfo(fis[0], FileInfo{
|
// if err := cmpFileInfo(fis[0], FileInfo{
|
||||||
name: "bin",
|
// name: "bin",
|
||||||
size: 3,
|
// size: 3,
|
||||||
mode: 0555 | os.ModeDir,
|
// mode: 0555 | os.ModeDir,
|
||||||
modTime: time.Unix(1583085224, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/bin
|
// modTime: time.Unix(1583085224, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/bin
|
||||||
}); err != nil {
|
// }); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fis, err = rd.Readdir(fis[0].Sys().(*FileInfo).Inode)
|
// fis, err = rd.Readdir(fis[0].Sys().(*FileInfo).Inode)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if got, want := len(fis), 0; got != want {
|
// if got, want := len(fis), 0; got != want {
|
||||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func TestReaddirSymlink(t *testing.T) {
|
// func TestReaddirSymlink(t *testing.T) {
|
||||||
t.Parallel()
|
// t.Parallel()
|
||||||
// TODO: ship testdata files generated by mksquashfs
|
// // TODO: ship testdata files generated by mksquashfs
|
||||||
f, err := os.Open("/home/michael/distri/_build/distri/pkg/zlib-amd64-1.2.11-4.squashfs")
|
// f, err := os.Open("/home/michael/distri/_build/distri/pkg/zlib-amd64-1.2.11-4.squashfs")
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
defer f.Close()
|
// defer f.Close()
|
||||||
rd, err := NewReader(f)
|
// rd, err := NewReader(f)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fis, err := rd.Readdir(rd.RootInode())
|
// fis, err := rd.Readdir(rd.RootInode())
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if got, want := len(fis), 4; got != want {
|
// if got, want := len(fis), 4; got != want {
|
||||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := cmpFileInfo(fis[3], FileInfo{
|
// if err := cmpFileInfo(fis[3], FileInfo{
|
||||||
name: "out",
|
// name: "out",
|
||||||
size: 54,
|
// size: 54,
|
||||||
mode: 0555 | os.ModeDir,
|
// mode: 0555 | os.ModeDir,
|
||||||
modTime: time.Unix(1583085224, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/out
|
// modTime: time.Unix(1583085224, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/out
|
||||||
}); err != nil {
|
// }); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fis, err = rd.Readdir(fis[3].Sys().(*FileInfo).Inode)
|
// fis, err = rd.Readdir(fis[3].Sys().(*FileInfo).Inode)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if got, want := len(fis), 3; got != want {
|
// if got, want := len(fis), 3; got != want {
|
||||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := cmpFileInfo(fis[1], FileInfo{
|
// if err := cmpFileInfo(fis[1], FileInfo{
|
||||||
name: "lib",
|
// name: "lib",
|
||||||
size: 83,
|
// size: 83,
|
||||||
mode: 0555 | os.ModeDir,
|
// mode: 0555 | os.ModeDir,
|
||||||
modTime: time.Unix(1583085224, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/out/lib
|
// modTime: time.Unix(1583085224, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/out/lib
|
||||||
}); err != nil {
|
// }); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fis, err = rd.Readdir(fis[1].Sys().(*FileInfo).Inode)
|
// fis, err = rd.Readdir(fis[1].Sys().(*FileInfo).Inode)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if got, want := len(fis), 4; got != want {
|
// if got, want := len(fis), 4; got != want {
|
||||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := cmpFileInfo(fis[1], FileInfo{
|
// if err := cmpFileInfo(fis[1], FileInfo{
|
||||||
name: "libz.so",
|
// name: "libz.so",
|
||||||
size: 9,
|
// size: 9,
|
||||||
mode: 0555 | os.ModeSymlink,
|
// mode: 0555 | os.ModeSymlink,
|
||||||
modTime: time.Unix(1583085223, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/out/lib/libz.so
|
// modTime: time.Unix(1583085223, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/out/lib/libz.so
|
||||||
}); err != nil {
|
// }); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// TODO: readlink
|
// // TODO: readlink
|
||||||
target, err := rd.ReadLink(fis[1].Sys().(*FileInfo).Inode)
|
// target, err := rd.ReadLink(fis[1].Sys().(*FileInfo).Inode)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
if got, want := target, "libz.so.1"; got != want {
|
// if got, want := target, "libz.so.1"; got != want {
|
||||||
t.Fatalf("ReadLink(libz.so): got %q, want %q", got, want)
|
// t.Fatalf("ReadLink(libz.so): got %q, want %q", got, want)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func TestReadfile(t *testing.T) {
|
// func TestReadfile(t *testing.T) {
|
||||||
t.Parallel()
|
// t.Parallel()
|
||||||
// TODO: ship testdata files generated by mksquashfs
|
// // TODO: ship testdata files generated by mksquashfs
|
||||||
f, err := os.Open("/home/michael/distri/_build/distri/pkg/ack-amd64-3.3.1-7.squashfs")
|
// f, err := os.Open("/home/michael/distri/_build/distri/pkg/ack-amd64-3.3.1-7.squashfs")
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
defer f.Close()
|
// defer f.Close()
|
||||||
rd, err := NewReader(f)
|
// rd, err := NewReader(f)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fis, err := rd.Readdir(rd.RootInode())
|
// fis, err := rd.Readdir(rd.RootInode())
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if got, want := len(fis), 3; got != want {
|
// if got, want := len(fis), 3; got != want {
|
||||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fis, err = rd.Readdir(fis[0].Sys().(*FileInfo).Inode)
|
// fis, err = rd.Readdir(fis[0].Sys().(*FileInfo).Inode)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if got, want := len(fis), 1; got != want {
|
// if got, want := len(fis), 1; got != want {
|
||||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||||
}
|
// }
|
||||||
|
|
||||||
r, err := rd.FileReader(fis[0].Sys().(*FileInfo).Inode)
|
// r, err := rd.FileReader(fis[0].Sys().(*FileInfo).Inode)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
for i := 0; i < 2; i++ {
|
// for i := 0; i < 2; i++ {
|
||||||
if _, err := r.Seek(0, io.SeekStart); err != nil {
|
// if _, err := r.Seek(0, io.SeekStart); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
h := md5.New()
|
// h := md5.New()
|
||||||
if _, err := io.Copy(h, r); err != nil {
|
// if _, err := io.Copy(h, r); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
sum := fmt.Sprintf("%x", h.Sum(nil))
|
// sum := fmt.Sprintf("%x", h.Sum(nil))
|
||||||
if got, want := sum, "c6c9b5d4d2a49f1b8b5e501f0f827a5c"; got != want {
|
// if got, want := sum, "c6c9b5d4d2a49f1b8b5e501f0f827a5c"; got != want {
|
||||||
t.Fatalf("md5(bin/ack): got %s, want %s", got, want)
|
// t.Fatalf("md5(bin/ack): got %s, want %s", got, want)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
// TODO: add test exercising ldirInodeHeader, e.g. '/mnt/loop/ca-certificates-3.39/buildoutput/etc/ssl'
|
// // TODO: add test exercising ldirInodeHeader, e.g. '/mnt/loop/ca-certificates-3.39/buildoutput/etc/ssl'
|
||||||
|
|
||||||
func TestReadXattr(t *testing.T) {
|
// func TestReadXattr(t *testing.T) {
|
||||||
t.Parallel()
|
// t.Parallel()
|
||||||
|
|
||||||
// TODO: generate a smaller version of this file
|
// // TODO: generate a smaller version of this file
|
||||||
f, err := os.Open("testdata/xattr.squashfs")
|
// f, err := os.Open("testdata/xattr.squashfs")
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
defer f.Close()
|
// defer f.Close()
|
||||||
|
|
||||||
rd, err := NewReader(f)
|
// rd, err := NewReader(f)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
for _, tt := range []struct {
|
// for _, tt := range []struct {
|
||||||
Path string
|
// Path string
|
||||||
Want []Xattr
|
// Want []Xattr
|
||||||
}{
|
// }{
|
||||||
{
|
// {
|
||||||
Path: "mtr-packet",
|
// Path: "mtr-packet",
|
||||||
Want: []Xattr{
|
// Want: []Xattr{
|
||||||
{
|
// {
|
||||||
Type: XattrTypeSecurity,
|
// Type: XattrTypeSecurity,
|
||||||
FullName: "security.capability",
|
// FullName: "security.capability",
|
||||||
Value: []byte{1, 0, 0, 2, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
// Value: []byte{1, 0, 0, 2, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
{
|
// {
|
||||||
Path: "gnome-keyring-daemon",
|
// Path: "gnome-keyring-daemon",
|
||||||
Want: []Xattr{
|
// Want: []Xattr{
|
||||||
{
|
// {
|
||||||
Type: XattrTypeSecurity,
|
// Type: XattrTypeSecurity,
|
||||||
FullName: "security.capability",
|
// FullName: "security.capability",
|
||||||
Value: []byte{1, 0, 0, 2, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
// Value: []byte{1, 0, 0, 2, 0, 0x40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||||
},
|
// },
|
||||||
},
|
// },
|
||||||
} {
|
// } {
|
||||||
inode, err := rd.LookupPath(tt.Path)
|
// inode, err := rd.LookupPath(tt.Path)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
xattrs, err := rd.ReadXattrs(inode)
|
// xattrs, err := rd.ReadXattrs(inode)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatalf("ReadXattrs(%v): %v", inode, err)
|
// t.Fatalf("ReadXattrs(%v): %v", inode, err)
|
||||||
}
|
// }
|
||||||
if diff := cmp.Diff(tt.Want, xattrs); diff != "" {
|
// if diff := cmp.Diff(tt.Want, xattrs); diff != "" {
|
||||||
t.Fatalf("unexpected ReadXattrs result: diff (-want +got):\n%s", diff)
|
// t.Fatalf("unexpected ReadXattrs result: diff (-want +got):\n%s", diff)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
+23
-2
@@ -1,4 +1,4 @@
|
|||||||
package squashfs_test
|
package squashfs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
@@ -15,8 +15,29 @@ const (
|
|||||||
squashfsName = "Code_OSS.Squashfs"
|
squashfsName = "Code_OSS.Squashfs"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCreateSquashFromAppImage(t *testing.T) {
|
func TestAppImageSquash(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
wd, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
squashFil, err := os.Open(wd + "/testing/" + squashfsName)
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
TestCreateSquashFromAppImage(t)
|
||||||
|
squashFil, err = os.Open(wd + "/testing/" + squashfsName)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer squashFil.Close()
|
||||||
|
squash, err := NewSquashfs(squashFil)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
t.Fatal("Testing")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateSquashFromAppImage(t *testing.T) {
|
||||||
wd, err := os.Getwd()
|
wd, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
|
|||||||
+55
-20
@@ -1,24 +1,59 @@
|
|||||||
package squashfs
|
package squashfs
|
||||||
|
|
||||||
|
//Superblock is a raw representation of a squashfs
|
||||||
//Descriptions provided by https://dr-emann.github.io/squashfs/
|
//Descriptions provided by https://dr-emann.github.io/squashfs/
|
||||||
type superblock struct {
|
type Superblock struct {
|
||||||
Magic uint32 //Magic will be 0x73717368 if it's a legit Squashfs filesystem
|
Magic uint32 //Magic will be 0x73717368 if it's a legit Squashfs filesystem
|
||||||
Inodes uint32 //Inodes is the number of inodes in the inodes table
|
Inodes uint32 //Inodes is the number of inodes in the inodes table
|
||||||
MkfsTime int32 //MkfsTime is when archive was created
|
MkfsTime uint32 //MkfsTime is when archive was created
|
||||||
BlockSize uint32 //BlockSize is the size of data blocks in bytes
|
BlockSize uint32 //BlockSize is the size of data blocks in bytes
|
||||||
Fragments uint32 //Fragments is the number of entries in fragment table
|
Fragments uint32 //Fragments is the number of entries in fragment table
|
||||||
Compression uint16 //Compression is what type of compression is used
|
Compression uint16 //Compression is what type of compression is used
|
||||||
BlockLog uint16 //BlockLog should be log base 2 of BlockSize. If not then the squash might be corrupt
|
BlockLog uint16 //BlockLog should be log base 2 of BlockSize. If not then the squash might be corrupt
|
||||||
Flags uint16 //Flags are the superblock's flags
|
Flags uint16 //Flags are the superblock's flags
|
||||||
IDCount uint16 //IDCount is the number of IDs in the id lookup table
|
IDCount uint16 //IDCount is the number of IDs in the id lookup table
|
||||||
Major uint16
|
Major uint16 //Major version of squashfs format
|
||||||
Minor uint16
|
Minor uint16 //Minor version of squashfs format
|
||||||
RootInode Inode
|
RootInode uint64 //RootInode is a reference to the root of the squashfs
|
||||||
BytesUsed int64
|
BytesUsed uint64 //BytesUsed is how many bytes the archive is. squashfs archives are often padded to 4KB.
|
||||||
IDTableStart int64
|
IDTableOffset uint64 //IDTableOff is the byte offset of the IDTable
|
||||||
XattrIDTableStart int64
|
XattrIDTableOffset uint64 //XattrIDTableOffset is the byte offset of the xattr id table
|
||||||
InodeTableStart int64
|
InodeTableOffset uint64 //InodeTableOffset is the byte offset of the inode table
|
||||||
DirectoryTableStart int64
|
DirectoryTableOffset uint64 //DirectoryTableOffset is the byte offset of the directory table
|
||||||
FragmentTableStart int64
|
FragmentTableOffset uint64 //FragmentTableOffset is the byte offset of the fragment table
|
||||||
LookupTableStart int64
|
ExportTableOffset uint64 //ExportTableOffset is the byte offset of the export table
|
||||||
|
}
|
||||||
|
|
||||||
|
//SuperblockFlags is a parsed list of options set in Superblock.Flags
|
||||||
|
type SuperblockFlags struct {
|
||||||
|
UncompressedInodes bool
|
||||||
|
UncompressedData bool
|
||||||
|
Check bool //Check is unused in current versions of squashfs
|
||||||
|
UncompressedFragments bool
|
||||||
|
NoFragments bool
|
||||||
|
AlwaysFragments bool
|
||||||
|
Duplicates bool //Identical files are stored only once
|
||||||
|
Exportable bool
|
||||||
|
UncompressedXattrs bool
|
||||||
|
NoXattrs bool
|
||||||
|
CompressorOptions bool
|
||||||
|
UncompressedIDs bool
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetFlags returns the Flags parsed into a SuperblockFlags
|
||||||
|
func (s *Superblock) GetFlags() SuperblockFlags {
|
||||||
|
return SuperblockFlags{
|
||||||
|
UncompressedInodes: s.Flags&0x1 == 0x1,
|
||||||
|
UncompressedData: s.Flags&0x2 == 0x2,
|
||||||
|
Check: s.Flags&0x4 == 0x4,
|
||||||
|
UncompressedFragments: s.Flags&0x8 == 0x8,
|
||||||
|
NoFragments: s.Flags&0x10 == 0x10,
|
||||||
|
AlwaysFragments: s.Flags&0x20 == 0x20,
|
||||||
|
Duplicates: s.Flags&0x40 == 0x40,
|
||||||
|
Exportable: s.Flags&0x80 == 0x80,
|
||||||
|
UncompressedXattrs: s.Flags&0x100 == 0x100,
|
||||||
|
NoXattrs: s.Flags&0x200 == 0x200,
|
||||||
|
CompressorOptions: s.Flags&0x400 == 0x400,
|
||||||
|
UncompressedIDs: s.Flags&0x800 == 0x800,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
package squashfs
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
func uncompressData(data []byte, compressionType int) []byte {
|
||||||
|
//TODO: check compression type and uncompress the data
|
||||||
|
return make([]byte, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
//same os uncompressData, but uses a reader instead. reader's seek will be
|
||||||
|
func uncompressReaderData(reader *io.Reader, compressionType int) []byte {
|
||||||
|
//TODO: check compression type and uncompress the data
|
||||||
|
return make([]byte, 0)
|
||||||
|
}
|
||||||
+24
-3
@@ -1,9 +1,30 @@
|
|||||||
package squashfs
|
package squashfs
|
||||||
|
|
||||||
import "io"
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
//Squashfs is a squashfs backed by a reader.
|
//Squashfs is a squashfs backed by a ReadSeeker.
|
||||||
type Squashfs struct {
|
type Squashfs struct {
|
||||||
rdr *io.Reader //underlyting reader
|
rdr *io.ReaderAt //underlying reader
|
||||||
super Superblock
|
super Superblock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//NewSquashfs creates a new Squashfs backed by the given reader
|
||||||
|
func NewSquashfs(reader io.ReaderAt) (*Squashfs, error) {
|
||||||
|
var superblock Superblock
|
||||||
|
err := binary.Read(io.NewSectionReader(reader, 0, int64(binary.Size(superblock))), binary.LittleEndian, &superblock)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Squashfs{
|
||||||
|
rdr: &reader,
|
||||||
|
super: superblock,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetFlags return the SuperblockFlags of the Squashfs
|
||||||
|
func (s *Squashfs) GetFlags() SuperblockFlags {
|
||||||
|
return s.super.GetFlags()
|
||||||
|
}
|
||||||
|
|||||||
+911
-911
File diff suppressed because it is too large
Load Diff
+277
-277
@@ -1,310 +1,310 @@
|
|||||||
package squashfs
|
package squashfs
|
||||||
|
|
||||||
import (
|
// import (
|
||||||
"bytes"
|
// "bytes"
|
||||||
"flag"
|
// "flag"
|
||||||
"fmt"
|
// "fmt"
|
||||||
"io"
|
// "io"
|
||||||
"io/ioutil"
|
// "io/ioutil"
|
||||||
"os"
|
// "os"
|
||||||
"os/exec"
|
// "os/exec"
|
||||||
"path/filepath"
|
// "path/filepath"
|
||||||
"strings"
|
// "strings"
|
||||||
"testing"
|
// "testing"
|
||||||
"time"
|
// "time"
|
||||||
|
|
||||||
"github.com/distr1/distri"
|
// "github.com/distr1/distri"
|
||||||
// "github.com/distr1/distri/internal/distritest"
|
// // "github.com/distr1/distri/internal/distritest"
|
||||||
"github.com/google/go-cmp/cmp"
|
// "github.com/google/go-cmp/cmp"
|
||||||
"github.com/orcaman/writerseeker"
|
// "github.com/orcaman/writerseeker"
|
||||||
"golang.org/x/sys/unix"
|
// "golang.org/x/sys/unix"
|
||||||
)
|
// )
|
||||||
|
|
||||||
var fsImagePath = flag.String("fs_image_path", "", "Store the SquashFS test file system in the specified path for manual inspection")
|
// var fsImagePath = flag.String("fs_image_path", "", "Store the SquashFS test file system in the specified path for manual inspection")
|
||||||
|
|
||||||
func writeTestImage(iow io.WriteSeeker, xattr bool) error {
|
// func writeTestImage(iow io.WriteSeeker, xattr bool) error {
|
||||||
w, err := NewWriter(iow, time.Now())
|
// w, err := NewWriter(iow, time.Now())
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
var xattrs []Xattr
|
// var xattrs []Xattr
|
||||||
if xattr {
|
// if xattr {
|
||||||
xattrs = append(xattrs, Xattr{
|
// xattrs = append(xattrs, Xattr{
|
||||||
Type: 2,
|
// Type: 2,
|
||||||
FullName: "capability",
|
// FullName: "capability",
|
||||||
Value: []byte{1, 0, 0, 2, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
// Value: []byte{1, 0, 0, 2, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
ff, err := w.Root.File("hellö wörld", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH, xattrs)
|
// ff, err := w.Root.File("hellö wörld", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH, xattrs)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if _, err := ff.Write([]byte("hello world!")); err != nil {
|
// if _, err := ff.Write([]byte("hello world!")); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if err := ff.Close(); err != nil {
|
// if err := ff.Close(); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
ff, err = w.Root.File("leer", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH, nil)
|
// ff, err = w.Root.File("leer", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH, nil)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if err := ff.Close(); err != nil {
|
// if err := ff.Close(); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
ff, err = w.Root.File("second file", time.Now(), unix.S_IRUSR|unix.S_IXUSR|
|
// ff, err = w.Root.File("second file", time.Now(), unix.S_IRUSR|unix.S_IXUSR|
|
||||||
unix.S_IRGRP|unix.S_IXGRP|
|
// unix.S_IRGRP|unix.S_IXGRP|
|
||||||
unix.S_IROTH|unix.S_IXOTH,
|
// unix.S_IROTH|unix.S_IXOTH,
|
||||||
nil)
|
// nil)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if _, err := ff.Write([]byte("NON.\n")); err != nil {
|
// if _, err := ff.Write([]byte("NON.\n")); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if err := ff.Close(); err != nil {
|
// if err := ff.Close(); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := w.Root.Symlink("second file", "second link", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH); err != nil {
|
// if err := w.Root.Symlink("second file", "second link", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
subdir := w.Root.Directory("subdir", time.Now())
|
// subdir := w.Root.Directory("subdir", time.Now())
|
||||||
|
|
||||||
subsubdir := subdir.Directory("deep", time.Now())
|
// subsubdir := subdir.Directory("deep", time.Now())
|
||||||
ff, err = subsubdir.File("yo", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH, nil)
|
// ff, err = subsubdir.File("yo", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH, nil)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if _, err := ff.Write([]byte("foo\n")); err != nil {
|
// if _, err := ff.Write([]byte("foo\n")); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if err := ff.Close(); err != nil {
|
// if err := ff.Close(); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if err := subsubdir.Flush(); err != nil {
|
// if err := subsubdir.Flush(); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
// TODO: write another file in subdir now, will result in invalid parent inode
|
// // TODO: write another file in subdir now, will result in invalid parent inode
|
||||||
|
|
||||||
ff, err = subdir.File("third file (in subdir)", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH, nil)
|
// ff, err = subdir.File("third file (in subdir)", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH, nil)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if _, err := ff.Write([]byte("contents\n")); err != nil {
|
// if _, err := ff.Write([]byte("contents\n")); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if err := ff.Close(); err != nil {
|
// if err := ff.Close(); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := subdir.Flush(); err != nil {
|
// if err := subdir.Flush(); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
ff, err = w.Root.File("testbin", time.Now(), unix.S_IRUSR|unix.S_IXUSR|
|
// ff, err = w.Root.File("testbin", time.Now(), unix.S_IRUSR|unix.S_IXUSR|
|
||||||
unix.S_IRGRP|unix.S_IXGRP|
|
// unix.S_IRGRP|unix.S_IXGRP|
|
||||||
unix.S_IROTH|unix.S_IXOTH,
|
// unix.S_IROTH|unix.S_IXOTH,
|
||||||
nil)
|
// nil)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
zf, err := os.Open(os.Args[0])
|
// zf, err := os.Open(os.Args[0])
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
defer zf.Close()
|
// defer zf.Close()
|
||||||
if _, err := io.Copy(ff, zf); err != nil {
|
// if _, err := io.Copy(ff, zf); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if err := ff.Close(); err != nil {
|
// if err := ff.Close(); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := w.Root.Flush(); err != nil {
|
// if err := w.Root.Flush(); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
if err := w.Flush(); err != nil {
|
// if err := w.Flush(); err != nil {
|
||||||
return err
|
// return err
|
||||||
}
|
// }
|
||||||
return nil
|
// return nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func TestUnsquashfs(t *testing.T) {
|
// func TestUnsquashfs(t *testing.T) {
|
||||||
t.Parallel()
|
// t.Parallel()
|
||||||
|
|
||||||
ctx, canc := distri.InterruptibleContext()
|
// ctx, canc := distri.InterruptibleContext()
|
||||||
defer canc()
|
// defer canc()
|
||||||
|
|
||||||
if _, err := exec.LookPath("unsquashfs"); err != nil {
|
// if _, err := exec.LookPath("unsquashfs"); err != nil {
|
||||||
t.Skip("unsquashfs not found in $PATH")
|
// t.Skip("unsquashfs not found in $PATH")
|
||||||
}
|
// }
|
||||||
|
|
||||||
for _, xattr := range []bool{false, true} {
|
// for _, xattr := range []bool{false, true} {
|
||||||
t.Run(fmt.Sprintf("xattr %v", xattr), func(t *testing.T) {
|
// t.Run(fmt.Sprintf("xattr %v", xattr), func(t *testing.T) {
|
||||||
var (
|
// var (
|
||||||
f *os.File
|
// f *os.File
|
||||||
err error
|
// err error
|
||||||
)
|
// )
|
||||||
if *fsImagePath != "" {
|
// if *fsImagePath != "" {
|
||||||
f, err = os.Create(*fsImagePath + fmt.Sprintf("-xattr-%v", xattr))
|
// f, err = os.Create(*fsImagePath + fmt.Sprintf("-xattr-%v", xattr))
|
||||||
} else {
|
// } else {
|
||||||
f, err = ioutil.TempFile("", fmt.Sprintf("squashfs-xattr-%v", xattr))
|
// f, err = ioutil.TempFile("", fmt.Sprintf("squashfs-xattr-%v", xattr))
|
||||||
if err == nil {
|
// if err == nil {
|
||||||
defer os.Remove(f.Name())
|
// defer os.Remove(f.Name())
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := writeTestImage(f, xattr); err != nil {
|
// if err := writeTestImage(f, xattr); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if err := f.Close(); err != nil {
|
// if err := f.Close(); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Extract our generated file system using unsquashfs(1)
|
// // Extract our generated file system using unsquashfs(1)
|
||||||
out, err := ioutil.TempDir("", fmt.Sprintf("unsquashfs-xattr-%v", xattr))
|
// out, err := ioutil.TempDir("", fmt.Sprintf("unsquashfs-xattr-%v", xattr))
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
// defer distritest.RemoveAll(t, out)
|
// // defer distritest.RemoveAll(t, out)
|
||||||
cmd := exec.CommandContext(ctx, "unsquashfs", "-no-xattrs", "-d", filepath.Join(out, "x"), f.Name())
|
// cmd := exec.CommandContext(ctx, "unsquashfs", "-no-xattrs", "-d", filepath.Join(out, "x"), f.Name())
|
||||||
cmd.Stderr = os.Stderr
|
// cmd.Stderr = os.Stderr
|
||||||
if err := cmd.Run(); err != nil {
|
// if err := cmd.Run(); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fbin, err := os.Open(os.Args[0])
|
// fbin, err := os.Open(os.Args[0])
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Verify the extracted files match our expectations.
|
// // Verify the extracted files match our expectations.
|
||||||
for _, entry := range []struct {
|
// for _, entry := range []struct {
|
||||||
path string
|
// path string
|
||||||
contents io.Reader
|
// contents io.Reader
|
||||||
}{
|
// }{
|
||||||
{"leer", strings.NewReader("")},
|
// {"leer", strings.NewReader("")},
|
||||||
{"hellö wörld", strings.NewReader("hello world!")},
|
// {"hellö wörld", strings.NewReader("hello world!")},
|
||||||
{"testbin", fbin},
|
// {"testbin", fbin},
|
||||||
{"subdir/third file (in subdir)", strings.NewReader("contents\n")},
|
// {"subdir/third file (in subdir)", strings.NewReader("contents\n")},
|
||||||
} {
|
// } {
|
||||||
entry := entry // copy
|
// entry := entry // copy
|
||||||
t.Run(entry.path, func(t *testing.T) {
|
// t.Run(entry.path, func(t *testing.T) {
|
||||||
in, err := os.Open(filepath.Join(out, "x", entry.path))
|
// in, err := os.Open(filepath.Join(out, "x", entry.path))
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
got, err := ioutil.ReadAll(in)
|
// got, err := ioutil.ReadAll(in)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
want, err := ioutil.ReadAll(entry.contents)
|
// want, err := ioutil.ReadAll(entry.contents)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
if !bytes.Equal(got, want) {
|
// if !bytes.Equal(got, want) {
|
||||||
t.Fatalf("path %q differs", entry.path)
|
// t.Fatalf("path %q differs", entry.path)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
func TestReader(t *testing.T) {
|
// func TestReader(t *testing.T) {
|
||||||
t.Parallel()
|
// t.Parallel()
|
||||||
|
|
||||||
for _, xattr := range []bool{false, true} {
|
// for _, xattr := range []bool{false, true} {
|
||||||
t.Run(fmt.Sprintf("xattr %v", xattr), func(t *testing.T) {
|
// t.Run(fmt.Sprintf("xattr %v", xattr), func(t *testing.T) {
|
||||||
var err error
|
// var err error
|
||||||
buf := &writerseeker.WriterSeeker{}
|
// buf := &writerseeker.WriterSeeker{}
|
||||||
if err := writeTestImage(buf, xattr); err != nil {
|
// if err := writeTestImage(buf, xattr); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if _, err := buf.Seek(0, io.SeekCurrent); err != nil {
|
// if _, err := buf.Seek(0, io.SeekCurrent); err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
rd, err := NewReader(buf.BytesReader())
|
// rd, err := NewReader(buf.BytesReader())
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fbin, err := os.Open(os.Args[0])
|
// fbin, err := os.Open(os.Args[0])
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
// Verify the extracted files match our expectations.
|
// // Verify the extracted files match our expectations.
|
||||||
for _, entry := range []struct {
|
// for _, entry := range []struct {
|
||||||
path string
|
// path string
|
||||||
contents io.Reader
|
// contents io.Reader
|
||||||
}{
|
// }{
|
||||||
{"leer", strings.NewReader("")},
|
// {"leer", strings.NewReader("")},
|
||||||
{"hellö wörld", strings.NewReader("hello world!")},
|
// {"hellö wörld", strings.NewReader("hello world!")},
|
||||||
{"testbin", fbin},
|
// {"testbin", fbin},
|
||||||
{"subdir/third file (in subdir)", strings.NewReader("contents\n")},
|
// {"subdir/third file (in subdir)", strings.NewReader("contents\n")},
|
||||||
} {
|
// } {
|
||||||
entry := entry // copy
|
// entry := entry // copy
|
||||||
t.Run(entry.path, func(t *testing.T) {
|
// t.Run(entry.path, func(t *testing.T) {
|
||||||
// TODO: is this t.Parallel()-safe?
|
// // TODO: is this t.Parallel()-safe?
|
||||||
inode, err := rd.LookupPath(entry.path)
|
// inode, err := rd.LookupPath(entry.path)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
in, err := rd.FileReader(inode)
|
// in, err := rd.FileReader(inode)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
got, err := ioutil.ReadAll(in)
|
// got, err := ioutil.ReadAll(in)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
want, err := ioutil.ReadAll(entry.contents)
|
// want, err := ioutil.ReadAll(entry.contents)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
if !bytes.Equal(got, want) {
|
// if !bytes.Equal(got, want) {
|
||||||
t.Fatalf("path %q differs", entry.path)
|
// t.Fatalf("path %q differs", entry.path)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
|
|
||||||
if xattr {
|
// if xattr {
|
||||||
t.Run("xattrs", func(t *testing.T) {
|
// t.Run("xattrs", func(t *testing.T) {
|
||||||
inode, err := rd.LookupPath("hellö wörld")
|
// inode, err := rd.LookupPath("hellö wörld")
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
xattrs, err := rd.ReadXattrs(inode)
|
// xattrs, err := rd.ReadXattrs(inode)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
t.Fatal(err)
|
// t.Fatal(err)
|
||||||
}
|
// }
|
||||||
|
|
||||||
if got, want := len(xattrs), 1; got != want {
|
// if got, want := len(xattrs), 1; got != want {
|
||||||
t.Fatalf("unexpected number of extended attributes: got %d, want %d", got, want)
|
// t.Fatalf("unexpected number of extended attributes: got %d, want %d", got, want)
|
||||||
}
|
// }
|
||||||
wantXattr := Xattr{
|
// wantXattr := Xattr{
|
||||||
Type: 2,
|
// Type: 2,
|
||||||
FullName: "security.capability",
|
// FullName: "security.capability",
|
||||||
Value: []byte{1, 0, 0, 2, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
// Value: []byte{1, 0, 0, 2, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
}
|
// }
|
||||||
if diff := cmp.Diff(wantXattr, xattrs[0]); diff != "" {
|
// if diff := cmp.Diff(wantXattr, xattrs[0]); diff != "" {
|
||||||
t.Errorf("unexpected extended attribute: diff (-want +got):\n%s", diff)
|
// t.Errorf("unexpected extended attribute: diff (-want +got):\n%s", diff)
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
})
|
// })
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|||||||
Reference in New Issue
Block a user