Starting on writing the library
Currently just parses the superblock (but that works!)
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
# GoSquashfs
|
||||
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
|
||||
* Link directly to squashfs-tool using cgo
|
||||
* 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
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
// import (
|
||||
// "crypto/md5"
|
||||
// "fmt"
|
||||
// "io"
|
||||
// "os"
|
||||
// "testing"
|
||||
// "time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
)
|
||||
// "github.com/google/go-cmp/cmp"
|
||||
// )
|
||||
|
||||
func cmpFileInfo(got os.FileInfo, want FileInfo) error {
|
||||
if got, want := got.Name(), want.name; got != want {
|
||||
return fmt.Errorf("unexpected file name: got %q, want %q", got, want)
|
||||
}
|
||||
if got, want := got.Size(), want.size; got != want {
|
||||
return fmt.Errorf("unexpected size: got %d, want %d", got, want)
|
||||
}
|
||||
if got, want := got.IsDir(), want.mode.IsDir(); got != want {
|
||||
return fmt.Errorf("IsDir: got %v, want %v", got, want)
|
||||
}
|
||||
// TODO: re-enable when it’s no longer just a change detector
|
||||
// if got, want := got.ModTime(), want.modTime; !got.Equal(want) {
|
||||
// return fmt.Errorf("IsDir: got %v, want %v", got, want)
|
||||
// }
|
||||
// func cmpFileInfo(got os.FileInfo, want FileInfo) error {
|
||||
// if got, want := got.Name(), want.name; got != want {
|
||||
// return fmt.Errorf("unexpected file name: got %q, want %q", got, want)
|
||||
// }
|
||||
// if got, want := got.Size(), want.size; got != want {
|
||||
// return fmt.Errorf("unexpected size: got %d, want %d", got, want)
|
||||
// }
|
||||
// if got, want := got.IsDir(), want.mode.IsDir(); got != want {
|
||||
// return fmt.Errorf("IsDir: got %v, want %v", got, want)
|
||||
// }
|
||||
// // TODO: re-enable when it’s no longer just a change detector
|
||||
// // if got, want := got.ModTime(), want.modTime; !got.Equal(want) {
|
||||
// // return fmt.Errorf("IsDir: got %v, want %v", got, want)
|
||||
// // }
|
||||
|
||||
return nil
|
||||
}
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func TestReaddir(t *testing.T) {
|
||||
t.Parallel()
|
||||
// TODO: ship testdata files generated by mksquashfs
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(pwd + "/testdata/testing.squashfs")
|
||||
f, err := os.Open(pwd + "/testdata/testing.squashfs")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
rd, err := NewReader(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fis, err := rd.Readdir(rd.RootInode())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, fi := range fis {
|
||||
fmt.Println(fi.Name())
|
||||
}
|
||||
rdr, err := rd.FileReader(fis[0].Sys().(*FileInfo).Inode)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = os.Remove(pwd + "/testdata/Magisk.zip")
|
||||
if !os.IsNotExist(err) && err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// rdrzlib, err := zlib.NewReader(rdr)
|
||||
magFil, err := os.Create(pwd + "/testdata/Magisk.zip")
|
||||
io.Copy(magFil, rdr)
|
||||
if got, want := len(fis), 3; got != want {
|
||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
}
|
||||
// func TestReaddir(t *testing.T) {
|
||||
// t.Parallel()
|
||||
// // TODO: ship testdata files generated by mksquashfs
|
||||
// pwd, err := os.Getwd()
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// fmt.Println(pwd + "/testdata/testing.squashfs")
|
||||
// f, err := os.Open(pwd + "/testdata/testing.squashfs")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer f.Close()
|
||||
// rd, err := NewReader(f)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// fis, err := rd.Readdir(rd.RootInode())
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// for _, fi := range fis {
|
||||
// fmt.Println(fi.Name())
|
||||
// }
|
||||
// rdr, err := rd.FileReader(fis[0].Sys().(*FileInfo).Inode)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// err = os.Remove(pwd + "/testdata/Magisk.zip")
|
||||
// if !os.IsNotExist(err) && err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// // rdrzlib, err := zlib.NewReader(rdr)
|
||||
// magFil, err := os.Create(pwd + "/testdata/Magisk.zip")
|
||||
// io.Copy(magFil, rdr)
|
||||
// if got, want := len(fis), 3; got != want {
|
||||
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
// }
|
||||
|
||||
if err := cmpFileInfo(fis[0], FileInfo{
|
||||
name: "bin",
|
||||
size: 26,
|
||||
mode: 0555 | os.ModeDir,
|
||||
modTime: time.Unix(1581275131, 0), // stat -c %Y /ro/ack-amd64-2.24/bin
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if err := cmpFileInfo(fis[0], FileInfo{
|
||||
// name: "bin",
|
||||
// size: 26,
|
||||
// mode: 0555 | os.ModeDir,
|
||||
// modTime: time.Unix(1581275131, 0), // stat -c %Y /ro/ack-amd64-2.24/bin
|
||||
// }); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if err := cmpFileInfo(fis[1], FileInfo{
|
||||
name: "lib",
|
||||
size: 3,
|
||||
mode: 0555 | os.ModeDir,
|
||||
modTime: time.Unix(1581275131, 0), // stat -c %Y /ro/ack-amd64-2.24/lib
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if err := cmpFileInfo(fis[1], FileInfo{
|
||||
// name: "lib",
|
||||
// size: 3,
|
||||
// mode: 0555 | os.ModeDir,
|
||||
// modTime: time.Unix(1581275131, 0), // stat -c %Y /ro/ack-amd64-2.24/lib
|
||||
// }); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if err := cmpFileInfo(fis[2], FileInfo{
|
||||
name: "out",
|
||||
size: 48,
|
||||
mode: 0555 | os.ModeDir,
|
||||
modTime: time.Unix(1581275130, 0), // stat -c %Y /ro/ack-amd64-2.24/out
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if err := cmpFileInfo(fis[2], FileInfo{
|
||||
// name: "out",
|
||||
// size: 48,
|
||||
// mode: 0555 | os.ModeDir,
|
||||
// modTime: time.Unix(1581275130, 0), // stat -c %Y /ro/ack-amd64-2.24/out
|
||||
// }); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
fis, err = rd.Readdir(fis[0].Sys().(*FileInfo).Inode)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// fis, err = rd.Readdir(fis[0].Sys().(*FileInfo).Inode)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if got, want := len(fis), 1; got != want {
|
||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
}
|
||||
// if got, want := len(fis), 1; got != want {
|
||||
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
// }
|
||||
|
||||
if err := cmpFileInfo(fis[0], FileInfo{
|
||||
name: "ack",
|
||||
size: 38400,
|
||||
mode: 0755,
|
||||
modTime: time.Unix(1581275131, 0), // stat -c %Y /ro/ack-amd64-2.24/bin/ack
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
// if err := cmpFileInfo(fis[0], FileInfo{
|
||||
// name: "ack",
|
||||
// size: 38400,
|
||||
// mode: 0755,
|
||||
// modTime: time.Unix(1581275131, 0), // stat -c %Y /ro/ack-amd64-2.24/bin/ack
|
||||
// }); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// }
|
||||
|
||||
// TestReaddirSmoke is a smoke-test, reading the root directories of SquashFS
|
||||
// images which are known to trigger code paths which were buggy.
|
||||
func TestReaddirSmoke(t *testing.T) {
|
||||
t.Parallel()
|
||||
// // TestReaddirSmoke is a smoke-test, reading the root directories of SquashFS
|
||||
// // images which are known to trigger code paths which were buggy.
|
||||
// func TestReaddirSmoke(t *testing.T) {
|
||||
// t.Parallel()
|
||||
|
||||
for _, fn := range []string{
|
||||
// bash exercises the code path where an inode is split across metadata
|
||||
// blocks.
|
||||
"/home/michael/distri/_build/distri/pkg/bash-amd64-5.0-4.squashfs",
|
||||
// for _, fn := range []string{
|
||||
// // bash exercises the code path where an inode is split across metadata
|
||||
// // blocks.
|
||||
// "/home/michael/distri/_build/distri/pkg/bash-amd64-5.0-4.squashfs",
|
||||
|
||||
// cmake exercises the code path where the root directory entries are
|
||||
// located outside of the first block.
|
||||
"/home/michael/distri/_build/distri/pkg/cmake-amd64-3.12.4-8.squashfs",
|
||||
} {
|
||||
// TODO: ship testdata files generated by mksquashfs
|
||||
f, err := os.Open(fn)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
rd, err := NewReader(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// // cmake exercises the code path where the root directory entries are
|
||||
// // located outside of the first block.
|
||||
// "/home/michael/distri/_build/distri/pkg/cmake-amd64-3.12.4-8.squashfs",
|
||||
// } {
|
||||
// // TODO: ship testdata files generated by mksquashfs
|
||||
// f, err := os.Open(fn)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer f.Close()
|
||||
// rd, err := NewReader(f)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
fis, err := rd.Readdir(rd.RootInode())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// fis, err := rd.Readdir(rd.RootInode())
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if got, want := len(fis), 4; got != want {
|
||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
// if got, want := len(fis), 4; got != want {
|
||||
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestReaddirEmpty(t *testing.T) {
|
||||
t.Parallel()
|
||||
// TODO: ship testdata files generated by mksquashfs
|
||||
f, err := os.Open("/home/michael/distri/_build/distri/pkg/zlib-amd64-1.2.11-4.squashfs")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
rd, err := NewReader(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// func TestReaddirEmpty(t *testing.T) {
|
||||
// t.Parallel()
|
||||
// // TODO: ship testdata files generated by mksquashfs
|
||||
// f, err := os.Open("/home/michael/distri/_build/distri/pkg/zlib-amd64-1.2.11-4.squashfs")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer f.Close()
|
||||
// rd, err := NewReader(f)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
fis, err := rd.Readdir(rd.RootInode())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// fis, err := rd.Readdir(rd.RootInode())
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if got, want := len(fis), 4; got != want {
|
||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
}
|
||||
// if got, want := len(fis), 4; got != want {
|
||||
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
// }
|
||||
|
||||
if err := cmpFileInfo(fis[0], FileInfo{
|
||||
name: "bin",
|
||||
size: 3,
|
||||
mode: 0555 | os.ModeDir,
|
||||
modTime: time.Unix(1583085224, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/bin
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if err := cmpFileInfo(fis[0], FileInfo{
|
||||
// name: "bin",
|
||||
// size: 3,
|
||||
// mode: 0555 | os.ModeDir,
|
||||
// modTime: time.Unix(1583085224, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/bin
|
||||
// }); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
fis, err = rd.Readdir(fis[0].Sys().(*FileInfo).Inode)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// fis, err = rd.Readdir(fis[0].Sys().(*FileInfo).Inode)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if got, want := len(fis), 0; got != want {
|
||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
}
|
||||
}
|
||||
// if got, want := len(fis), 0; got != want {
|
||||
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestReaddirSymlink(t *testing.T) {
|
||||
t.Parallel()
|
||||
// TODO: ship testdata files generated by mksquashfs
|
||||
f, err := os.Open("/home/michael/distri/_build/distri/pkg/zlib-amd64-1.2.11-4.squashfs")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
rd, err := NewReader(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// func TestReaddirSymlink(t *testing.T) {
|
||||
// t.Parallel()
|
||||
// // TODO: ship testdata files generated by mksquashfs
|
||||
// f, err := os.Open("/home/michael/distri/_build/distri/pkg/zlib-amd64-1.2.11-4.squashfs")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer f.Close()
|
||||
// rd, err := NewReader(f)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
fis, err := rd.Readdir(rd.RootInode())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// fis, err := rd.Readdir(rd.RootInode())
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if got, want := len(fis), 4; got != want {
|
||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
}
|
||||
// if got, want := len(fis), 4; got != want {
|
||||
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
// }
|
||||
|
||||
if err := cmpFileInfo(fis[3], FileInfo{
|
||||
name: "out",
|
||||
size: 54,
|
||||
mode: 0555 | os.ModeDir,
|
||||
modTime: time.Unix(1583085224, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/out
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if err := cmpFileInfo(fis[3], FileInfo{
|
||||
// name: "out",
|
||||
// size: 54,
|
||||
// mode: 0555 | os.ModeDir,
|
||||
// modTime: time.Unix(1583085224, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/out
|
||||
// }); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
fis, err = rd.Readdir(fis[3].Sys().(*FileInfo).Inode)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// fis, err = rd.Readdir(fis[3].Sys().(*FileInfo).Inode)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if got, want := len(fis), 3; got != want {
|
||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
}
|
||||
// if got, want := len(fis), 3; got != want {
|
||||
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
// }
|
||||
|
||||
if err := cmpFileInfo(fis[1], FileInfo{
|
||||
name: "lib",
|
||||
size: 83,
|
||||
mode: 0555 | os.ModeDir,
|
||||
modTime: time.Unix(1583085224, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/out/lib
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if err := cmpFileInfo(fis[1], FileInfo{
|
||||
// name: "lib",
|
||||
// size: 83,
|
||||
// mode: 0555 | os.ModeDir,
|
||||
// modTime: time.Unix(1583085224, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/out/lib
|
||||
// }); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
fis, err = rd.Readdir(fis[1].Sys().(*FileInfo).Inode)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// fis, err = rd.Readdir(fis[1].Sys().(*FileInfo).Inode)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if got, want := len(fis), 4; got != want {
|
||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
}
|
||||
// if got, want := len(fis), 4; got != want {
|
||||
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
// }
|
||||
|
||||
if err := cmpFileInfo(fis[1], FileInfo{
|
||||
name: "libz.so",
|
||||
size: 9,
|
||||
mode: 0555 | os.ModeSymlink,
|
||||
modTime: time.Unix(1583085223, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/out/lib/libz.so
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if err := cmpFileInfo(fis[1], FileInfo{
|
||||
// name: "libz.so",
|
||||
// size: 9,
|
||||
// mode: 0555 | os.ModeSymlink,
|
||||
// modTime: time.Unix(1583085223, 0), // stat -c %Y /ro/zlib-amd64-1.2.11/out/lib/libz.so
|
||||
// }); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// TODO: readlink
|
||||
target, err := rd.ReadLink(fis[1].Sys().(*FileInfo).Inode)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, want := target, "libz.so.1"; got != want {
|
||||
t.Fatalf("ReadLink(libz.so): got %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
// // TODO: readlink
|
||||
// target, err := rd.ReadLink(fis[1].Sys().(*FileInfo).Inode)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if got, want := target, "libz.so.1"; got != want {
|
||||
// t.Fatalf("ReadLink(libz.so): got %q, want %q", got, want)
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestReadfile(t *testing.T) {
|
||||
t.Parallel()
|
||||
// TODO: ship testdata files generated by mksquashfs
|
||||
f, err := os.Open("/home/michael/distri/_build/distri/pkg/ack-amd64-3.3.1-7.squashfs")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
rd, err := NewReader(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// func TestReadfile(t *testing.T) {
|
||||
// t.Parallel()
|
||||
// // TODO: ship testdata files generated by mksquashfs
|
||||
// f, err := os.Open("/home/michael/distri/_build/distri/pkg/ack-amd64-3.3.1-7.squashfs")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer f.Close()
|
||||
// rd, err := NewReader(f)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
fis, err := rd.Readdir(rd.RootInode())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// fis, err := rd.Readdir(rd.RootInode())
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if got, want := len(fis), 3; got != want {
|
||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
}
|
||||
// if got, want := len(fis), 3; got != want {
|
||||
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
// }
|
||||
|
||||
fis, err = rd.Readdir(fis[0].Sys().(*FileInfo).Inode)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// fis, err = rd.Readdir(fis[0].Sys().(*FileInfo).Inode)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if got, want := len(fis), 1; got != want {
|
||||
t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
}
|
||||
// if got, want := len(fis), 1; got != want {
|
||||
// t.Fatalf("unexpected number of directory entries: got %d, want %d", got, want)
|
||||
// }
|
||||
|
||||
r, err := rd.FileReader(fis[0].Sys().(*FileInfo).Inode)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// r, err := rd.FileReader(fis[0].Sys().(*FileInfo).Inode)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
for i := 0; i < 2; i++ {
|
||||
if _, err := r.Seek(0, io.SeekStart); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
h := md5.New()
|
||||
if _, err := io.Copy(h, r); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sum := fmt.Sprintf("%x", h.Sum(nil))
|
||||
if got, want := sum, "c6c9b5d4d2a49f1b8b5e501f0f827a5c"; got != want {
|
||||
t.Fatalf("md5(bin/ack): got %s, want %s", got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
// for i := 0; i < 2; i++ {
|
||||
// if _, err := r.Seek(0, io.SeekStart); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// h := md5.New()
|
||||
// if _, err := io.Copy(h, r); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// sum := fmt.Sprintf("%x", h.Sum(nil))
|
||||
// if got, want := sum, "c6c9b5d4d2a49f1b8b5e501f0f827a5c"; 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) {
|
||||
t.Parallel()
|
||||
// func TestReadXattr(t *testing.T) {
|
||||
// t.Parallel()
|
||||
|
||||
// TODO: generate a smaller version of this file
|
||||
f, err := os.Open("testdata/xattr.squashfs")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
// // TODO: generate a smaller version of this file
|
||||
// f, err := os.Open("testdata/xattr.squashfs")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// defer f.Close()
|
||||
|
||||
rd, err := NewReader(f)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, tt := range []struct {
|
||||
Path string
|
||||
Want []Xattr
|
||||
}{
|
||||
{
|
||||
Path: "mtr-packet",
|
||||
Want: []Xattr{
|
||||
{
|
||||
Type: XattrTypeSecurity,
|
||||
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}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Path: "gnome-keyring-daemon",
|
||||
Want: []Xattr{
|
||||
{
|
||||
Type: XattrTypeSecurity,
|
||||
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}},
|
||||
},
|
||||
},
|
||||
} {
|
||||
inode, err := rd.LookupPath(tt.Path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
xattrs, err := rd.ReadXattrs(inode)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadXattrs(%v): %v", inode, err)
|
||||
}
|
||||
if diff := cmp.Diff(tt.Want, xattrs); diff != "" {
|
||||
t.Fatalf("unexpected ReadXattrs result: diff (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
}
|
||||
// rd, err := NewReader(f)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// for _, tt := range []struct {
|
||||
// Path string
|
||||
// Want []Xattr
|
||||
// }{
|
||||
// {
|
||||
// Path: "mtr-packet",
|
||||
// Want: []Xattr{
|
||||
// {
|
||||
// Type: XattrTypeSecurity,
|
||||
// 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}},
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// Path: "gnome-keyring-daemon",
|
||||
// Want: []Xattr{
|
||||
// {
|
||||
// Type: XattrTypeSecurity,
|
||||
// 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}},
|
||||
// },
|
||||
// },
|
||||
// } {
|
||||
// inode, err := rd.LookupPath(tt.Path)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// xattrs, err := rd.ReadXattrs(inode)
|
||||
// if err != nil {
|
||||
// t.Fatalf("ReadXattrs(%v): %v", inode, err)
|
||||
// }
|
||||
// if diff := cmp.Diff(tt.Want, xattrs); diff != "" {
|
||||
// t.Fatalf("unexpected ReadXattrs result: diff (-want +got):\n%s", diff)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
+23
-2
@@ -1,4 +1,4 @@
|
||||
package squashfs_test
|
||||
package squashfs
|
||||
|
||||
import (
|
||||
"io"
|
||||
@@ -15,8 +15,29 @@ const (
|
||||
squashfsName = "Code_OSS.Squashfs"
|
||||
)
|
||||
|
||||
func TestCreateSquashFromAppImage(t *testing.T) {
|
||||
func TestAppImageSquash(t *testing.T) {
|
||||
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()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
+55
-20
@@ -1,24 +1,59 @@
|
||||
package squashfs
|
||||
|
||||
//Superblock is a raw representation of a squashfs
|
||||
//Descriptions provided by https://dr-emann.github.io/squashfs/
|
||||
type superblock struct {
|
||||
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
|
||||
MkfsTime int32 //MkfsTime is when archive was created
|
||||
BlockSize uint32 //BlockSize is the size of data blocks in bytes
|
||||
Fragments uint32 //Fragments is the number of entries in fragment table
|
||||
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
|
||||
Flags uint16 //Flags are the superblock's flags
|
||||
IDCount uint16 //IDCount is the number of IDs in the id lookup table
|
||||
Major uint16
|
||||
Minor uint16
|
||||
RootInode Inode
|
||||
BytesUsed int64
|
||||
IDTableStart int64
|
||||
XattrIDTableStart int64
|
||||
InodeTableStart int64
|
||||
DirectoryTableStart int64
|
||||
FragmentTableStart int64
|
||||
LookupTableStart int64
|
||||
type Superblock struct {
|
||||
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
|
||||
MkfsTime uint32 //MkfsTime is when archive was created
|
||||
BlockSize uint32 //BlockSize is the size of data blocks in bytes
|
||||
Fragments uint32 //Fragments is the number of entries in fragment table
|
||||
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
|
||||
Flags uint16 //Flags are the superblock's flags
|
||||
IDCount uint16 //IDCount is the number of IDs in the id lookup table
|
||||
Major uint16 //Major version of squashfs format
|
||||
Minor uint16 //Minor version of squashfs format
|
||||
RootInode uint64 //RootInode is a reference to the root of the squashfs
|
||||
BytesUsed uint64 //BytesUsed is how many bytes the archive is. squashfs archives are often padded to 4KB.
|
||||
IDTableOffset uint64 //IDTableOff is the byte offset of the IDTable
|
||||
XattrIDTableOffset uint64 //XattrIDTableOffset is the byte offset of the xattr id table
|
||||
InodeTableOffset uint64 //InodeTableOffset is the byte offset of the inode table
|
||||
DirectoryTableOffset uint64 //DirectoryTableOffset is the byte offset of the directory table
|
||||
FragmentTableOffset uint64 //FragmentTableOffset is the byte offset of the fragment table
|
||||
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
|
||||
|
||||
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 {
|
||||
rdr *io.Reader //underlyting reader
|
||||
rdr *io.ReaderAt //underlying reader
|
||||
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
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
// import (
|
||||
// "bytes"
|
||||
// "flag"
|
||||
// "fmt"
|
||||
// "io"
|
||||
// "io/ioutil"
|
||||
// "os"
|
||||
// "os/exec"
|
||||
// "path/filepath"
|
||||
// "strings"
|
||||
// "testing"
|
||||
// "time"
|
||||
|
||||
"github.com/distr1/distri"
|
||||
// "github.com/distr1/distri/internal/distritest"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/orcaman/writerseeker"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
// "github.com/distr1/distri"
|
||||
// // "github.com/distr1/distri/internal/distritest"
|
||||
// "github.com/google/go-cmp/cmp"
|
||||
// "github.com/orcaman/writerseeker"
|
||||
// "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 {
|
||||
w, err := NewWriter(iow, time.Now())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// func writeTestImage(iow io.WriteSeeker, xattr bool) error {
|
||||
// w, err := NewWriter(iow, time.Now())
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
var xattrs []Xattr
|
||||
if xattr {
|
||||
xattrs = append(xattrs, Xattr{
|
||||
Type: 2,
|
||||
FullName: "capability",
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := ff.Write([]byte("hello world!")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ff.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
// var xattrs []Xattr
|
||||
// if xattr {
|
||||
// xattrs = append(xattrs, Xattr{
|
||||
// Type: 2,
|
||||
// FullName: "capability",
|
||||
// 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)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if _, err := ff.Write([]byte("hello world!")); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := ff.Close(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
ff, err = w.Root.File("leer", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ff.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
// ff, err = w.Root.File("leer", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH, nil)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := ff.Close(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
ff, err = w.Root.File("second file", time.Now(), unix.S_IRUSR|unix.S_IXUSR|
|
||||
unix.S_IRGRP|unix.S_IXGRP|
|
||||
unix.S_IROTH|unix.S_IXOTH,
|
||||
nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := ff.Write([]byte("NON.\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ff.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
// ff, err = w.Root.File("second file", time.Now(), unix.S_IRUSR|unix.S_IXUSR|
|
||||
// unix.S_IRGRP|unix.S_IXGRP|
|
||||
// unix.S_IROTH|unix.S_IXOTH,
|
||||
// nil)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if _, err := ff.Write([]byte("NON.\n")); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := ff.Close(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
if err := w.Root.Symlink("second file", "second link", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH); err != nil {
|
||||
return err
|
||||
}
|
||||
// if err := w.Root.Symlink("second file", "second link", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
subdir := w.Root.Directory("subdir", time.Now())
|
||||
// subdir := w.Root.Directory("subdir", 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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := ff.Write([]byte("foo\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ff.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := subsubdir.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
// subsubdir := subdir.Directory("deep", time.Now())
|
||||
// ff, err = subsubdir.File("yo", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH, nil)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if _, err := ff.Write([]byte("foo\n")); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := ff.Close(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := subsubdir.Flush(); err != nil {
|
||||
// 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)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := ff.Write([]byte("contents\n")); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ff.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
// ff, err = subdir.File("third file (in subdir)", time.Now(), unix.S_IRUSR|unix.S_IRGRP|unix.S_IROTH, nil)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if _, err := ff.Write([]byte("contents\n")); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := ff.Close(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
if err := subdir.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
ff, err = w.Root.File("testbin", time.Now(), unix.S_IRUSR|unix.S_IXUSR|
|
||||
unix.S_IRGRP|unix.S_IXGRP|
|
||||
unix.S_IROTH|unix.S_IXOTH,
|
||||
nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
zf, err := os.Open(os.Args[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer zf.Close()
|
||||
if _, err := io.Copy(ff, zf); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ff.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
// if err := subdir.Flush(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// ff, err = w.Root.File("testbin", time.Now(), unix.S_IRUSR|unix.S_IXUSR|
|
||||
// unix.S_IRGRP|unix.S_IXGRP|
|
||||
// unix.S_IROTH|unix.S_IXOTH,
|
||||
// nil)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// zf, err := os.Open(os.Args[0])
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer zf.Close()
|
||||
// if _, err := io.Copy(ff, zf); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := ff.Close(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
|
||||
if err := w.Root.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := w.Flush(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// if err := w.Root.Flush(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// if err := w.Flush(); err != nil {
|
||||
// return err
|
||||
// }
|
||||
// return nil
|
||||
// }
|
||||
|
||||
func TestUnsquashfs(t *testing.T) {
|
||||
t.Parallel()
|
||||
// func TestUnsquashfs(t *testing.T) {
|
||||
// t.Parallel()
|
||||
|
||||
ctx, canc := distri.InterruptibleContext()
|
||||
defer canc()
|
||||
// ctx, canc := distri.InterruptibleContext()
|
||||
// defer canc()
|
||||
|
||||
if _, err := exec.LookPath("unsquashfs"); err != nil {
|
||||
t.Skip("unsquashfs not found in $PATH")
|
||||
}
|
||||
// if _, err := exec.LookPath("unsquashfs"); err != nil {
|
||||
// t.Skip("unsquashfs not found in $PATH")
|
||||
// }
|
||||
|
||||
for _, xattr := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("xattr %v", xattr), func(t *testing.T) {
|
||||
var (
|
||||
f *os.File
|
||||
err error
|
||||
)
|
||||
if *fsImagePath != "" {
|
||||
f, err = os.Create(*fsImagePath + fmt.Sprintf("-xattr-%v", xattr))
|
||||
} else {
|
||||
f, err = ioutil.TempFile("", fmt.Sprintf("squashfs-xattr-%v", xattr))
|
||||
if err == nil {
|
||||
defer os.Remove(f.Name())
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// for _, xattr := range []bool{false, true} {
|
||||
// t.Run(fmt.Sprintf("xattr %v", xattr), func(t *testing.T) {
|
||||
// var (
|
||||
// f *os.File
|
||||
// err error
|
||||
// )
|
||||
// if *fsImagePath != "" {
|
||||
// f, err = os.Create(*fsImagePath + fmt.Sprintf("-xattr-%v", xattr))
|
||||
// } else {
|
||||
// f, err = ioutil.TempFile("", fmt.Sprintf("squashfs-xattr-%v", xattr))
|
||||
// if err == nil {
|
||||
// defer os.Remove(f.Name())
|
||||
// }
|
||||
// }
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if err := writeTestImage(f, xattr); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if err := writeTestImage(f, xattr); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if err := f.Close(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if err := f.Close(); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// Extract our generated file system using unsquashfs(1)
|
||||
out, err := ioutil.TempDir("", fmt.Sprintf("unsquashfs-xattr-%v", xattr))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// defer distritest.RemoveAll(t, out)
|
||||
cmd := exec.CommandContext(ctx, "unsquashfs", "-no-xattrs", "-d", filepath.Join(out, "x"), f.Name())
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// // Extract our generated file system using unsquashfs(1)
|
||||
// out, err := ioutil.TempDir("", fmt.Sprintf("unsquashfs-xattr-%v", xattr))
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// // defer distritest.RemoveAll(t, out)
|
||||
// cmd := exec.CommandContext(ctx, "unsquashfs", "-no-xattrs", "-d", filepath.Join(out, "x"), f.Name())
|
||||
// cmd.Stderr = os.Stderr
|
||||
// if err := cmd.Run(); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
fbin, err := os.Open(os.Args[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// fbin, err := os.Open(os.Args[0])
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// Verify the extracted files match our expectations.
|
||||
for _, entry := range []struct {
|
||||
path string
|
||||
contents io.Reader
|
||||
}{
|
||||
{"leer", strings.NewReader("")},
|
||||
{"hellö wörld", strings.NewReader("hello world!")},
|
||||
{"testbin", fbin},
|
||||
{"subdir/third file (in subdir)", strings.NewReader("contents\n")},
|
||||
} {
|
||||
entry := entry // copy
|
||||
t.Run(entry.path, func(t *testing.T) {
|
||||
in, err := os.Open(filepath.Join(out, "x", entry.path))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := ioutil.ReadAll(in)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want, err := ioutil.ReadAll(entry.contents)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Fatalf("path %q differs", entry.path)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// // Verify the extracted files match our expectations.
|
||||
// for _, entry := range []struct {
|
||||
// path string
|
||||
// contents io.Reader
|
||||
// }{
|
||||
// {"leer", strings.NewReader("")},
|
||||
// {"hellö wörld", strings.NewReader("hello world!")},
|
||||
// {"testbin", fbin},
|
||||
// {"subdir/third file (in subdir)", strings.NewReader("contents\n")},
|
||||
// } {
|
||||
// entry := entry // copy
|
||||
// t.Run(entry.path, func(t *testing.T) {
|
||||
// in, err := os.Open(filepath.Join(out, "x", entry.path))
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// got, err := ioutil.ReadAll(in)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// want, err := ioutil.ReadAll(entry.contents)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if !bytes.Equal(got, want) {
|
||||
// t.Fatalf("path %q differs", entry.path)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
func TestReader(t *testing.T) {
|
||||
t.Parallel()
|
||||
// func TestReader(t *testing.T) {
|
||||
// t.Parallel()
|
||||
|
||||
for _, xattr := range []bool{false, true} {
|
||||
t.Run(fmt.Sprintf("xattr %v", xattr), func(t *testing.T) {
|
||||
var err error
|
||||
buf := &writerseeker.WriterSeeker{}
|
||||
if err := writeTestImage(buf, xattr); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// for _, xattr := range []bool{false, true} {
|
||||
// t.Run(fmt.Sprintf("xattr %v", xattr), func(t *testing.T) {
|
||||
// var err error
|
||||
// buf := &writerseeker.WriterSeeker{}
|
||||
// if err := writeTestImage(buf, xattr); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if _, err := buf.Seek(0, io.SeekCurrent); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if _, err := buf.Seek(0, io.SeekCurrent); err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
rd, err := NewReader(buf.BytesReader())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// rd, err := NewReader(buf.BytesReader())
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
fbin, err := os.Open(os.Args[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// fbin, err := os.Open(os.Args[0])
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// Verify the extracted files match our expectations.
|
||||
for _, entry := range []struct {
|
||||
path string
|
||||
contents io.Reader
|
||||
}{
|
||||
{"leer", strings.NewReader("")},
|
||||
{"hellö wörld", strings.NewReader("hello world!")},
|
||||
{"testbin", fbin},
|
||||
{"subdir/third file (in subdir)", strings.NewReader("contents\n")},
|
||||
} {
|
||||
entry := entry // copy
|
||||
t.Run(entry.path, func(t *testing.T) {
|
||||
// TODO: is this t.Parallel()-safe?
|
||||
inode, err := rd.LookupPath(entry.path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
in, err := rd.FileReader(inode)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
got, err := ioutil.ReadAll(in)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
want, err := ioutil.ReadAll(entry.contents)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Fatalf("path %q differs", entry.path)
|
||||
}
|
||||
})
|
||||
}
|
||||
// // Verify the extracted files match our expectations.
|
||||
// for _, entry := range []struct {
|
||||
// path string
|
||||
// contents io.Reader
|
||||
// }{
|
||||
// {"leer", strings.NewReader("")},
|
||||
// {"hellö wörld", strings.NewReader("hello world!")},
|
||||
// {"testbin", fbin},
|
||||
// {"subdir/third file (in subdir)", strings.NewReader("contents\n")},
|
||||
// } {
|
||||
// entry := entry // copy
|
||||
// t.Run(entry.path, func(t *testing.T) {
|
||||
// // TODO: is this t.Parallel()-safe?
|
||||
// inode, err := rd.LookupPath(entry.path)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// in, err := rd.FileReader(inode)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// got, err := ioutil.ReadAll(in)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// want, err := ioutil.ReadAll(entry.contents)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
// if !bytes.Equal(got, want) {
|
||||
// t.Fatalf("path %q differs", entry.path)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
if xattr {
|
||||
t.Run("xattrs", func(t *testing.T) {
|
||||
inode, err := rd.LookupPath("hellö wörld")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if xattr {
|
||||
// t.Run("xattrs", func(t *testing.T) {
|
||||
// inode, err := rd.LookupPath("hellö wörld")
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
xattrs, err := rd.ReadXattrs(inode)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// xattrs, err := rd.ReadXattrs(inode)
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
if got, want := len(xattrs), 1; got != want {
|
||||
t.Fatalf("unexpected number of extended attributes: got %d, want %d", got, want)
|
||||
}
|
||||
wantXattr := Xattr{
|
||||
Type: 2,
|
||||
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},
|
||||
}
|
||||
if diff := cmp.Diff(wantXattr, xattrs[0]); diff != "" {
|
||||
t.Errorf("unexpected extended attribute: diff (-want +got):\n%s", diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
// if got, want := len(xattrs), 1; got != want {
|
||||
// t.Fatalf("unexpected number of extended attributes: got %d, want %d", got, want)
|
||||
// }
|
||||
// wantXattr := Xattr{
|
||||
// Type: 2,
|
||||
// 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},
|
||||
// }
|
||||
// if diff := cmp.Diff(wantXattr, xattrs[0]); diff != "" {
|
||||
// t.Errorf("unexpected extended attribute: diff (-want +got):\n%s", diff)
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user