diff --git a/Makefile b/Makefile index d85b142..7fa6eaa 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,26 @@ SECTORS = 32768 SECTOR_SIZE = 512 TARGET_MBR = mbr.bin +TARGET_IMG = linux.img +TARGET_FS = fs.bin mbr.bin: dd if=/dev/zero of=$(TARGET_MBR) seek=$(SECTORS) bs=$(SECTOR_SIZE) count=1 - fdisk -i -y $(TARGET_MBR) \ No newline at end of file + fdisk -i -y $(TARGET_MBR) + +# Linux only and require admin authority +#!!! Extract xfs from gpt using cmd/main.go. +linux.img: main + $(eval DEVICE := $(shell losetup -f)) + dd of=$(TARGET_IMG) count=0 seek=1 bs=41943040 + losetup $(DEVICE) $(TARGET_IMG) + parted $(DEVICE) -s mklabel gpt -s mkpart primary xfs 0 100% + mkfs.xfs $(DEVICE)p1 + losetup -d $(DEVICE) + +main: cmd/main.go + go build -o main cmd/main.go + +fs.bin: linux.img main + ./main linux.img + mv primary0 $(TARGET_FS) \ No newline at end of file diff --git a/cmd/main.go b/cmd/main.go index 856493c..6d72a23 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -2,10 +2,12 @@ package main import ( "fmt" - "github.com/masahiro331/go-disk" "io" "log" "os" + + "github.com/masahiro331/go-disk" + "github.com/masahiro331/go-xfs-filesystem/xfs" ) func main() { @@ -21,7 +23,7 @@ func main() { log.Fatal(err) } r := io.NewSectionReader(f, 0, fi.Size()) - driver, err := disk.NewDriver(r) + driver, err := disk.NewDriver(r, xfs.Check) if err != nil { log.Fatal(err) } diff --git a/disk.go b/disk.go index 38616ef..9056531 100644 --- a/disk.go +++ b/disk.go @@ -3,19 +3,25 @@ package disk import ( "io" + "github.com/masahiro331/go-disk/fs" "github.com/masahiro331/go-disk/gpt" "github.com/masahiro331/go-disk/mbr" "github.com/masahiro331/go-disk/types" "golang.org/x/xerrors" ) -type Driver interface { - Next() (types.Partition, error) -} - -func NewDriver(sr *io.SectionReader) (Driver, error) { +func NewDriver(sr *io.SectionReader, checkFsFuncs ...fs.CheckFsFunc) (types.Driver, error) { m, err := mbr.NewMasterBootRecord(sr) if err != nil { + if xerrors.Is(mbr.InvalidSignature, err) { + ok, err := fs.CheckFileSystems(sr, checkFsFuncs) + if err != nil { + return nil, xerrors.Errorf("failed to check filesystem: %w", err) + } + if ok { + return fs.NewDirectFileSystem(sr) + } + } return nil, xerrors.Errorf("failed to new MBR: %w", err) } @@ -28,4 +34,4 @@ func NewDriver(sr *io.SectionReader) (Driver, error) { } return g, nil -} \ No newline at end of file +} diff --git a/disk_test.go b/disk_test.go new file mode 100644 index 0000000..00afed7 --- /dev/null +++ b/disk_test.go @@ -0,0 +1,57 @@ +package disk_test + +import ( + "io" + "os" + "strings" + "testing" + + "github.com/masahiro331/go-disk" + "github.com/masahiro331/go-disk/fs" + "github.com/masahiro331/go-xfs-filesystem/xfs" +) + +func TestNewDriver(t *testing.T) { + tests := []struct { + name string + inputFile string + wantErr string + checkFsFuncs []fs.CheckFsFunc + }{ + { + name: "Happy path, Direct filesystem", + inputFile: "testdata/fs.bin", + wantErr: "", + checkFsFuncs: []fs.CheckFsFunc{xfs.Check}, + }, + { + name: "Invalid path, Direct filesystem no check fs functions", + inputFile: "testdata/fs.bin", + wantErr: "Invalid master boot record signature", + checkFsFuncs: []fs.CheckFsFunc{}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f, err := os.Open(tt.inputFile) + if err != nil { + t.Fatal(err) + } + defer f.Close() + + info, err := f.Stat() + if err != nil { + t.Fatal(err) + } + sr := io.NewSectionReader(f, 0, info.Size()) + _, err = disk.NewDriver(sr, tt.checkFsFuncs...) + + if tt.wantErr == "" && err != nil { + t.Errorf("input: %s, required no error = %v", tt.inputFile, err) + } else if tt.wantErr != "" && !strings.HasSuffix(err.Error(), tt.wantErr) { + t.Errorf("input: %s, expected: %q, actual: %q", tt.inputFile, tt.wantErr, err.Error()) + } + }) + } +} diff --git a/fs/fs.go b/fs/fs.go new file mode 100644 index 0000000..3f22765 --- /dev/null +++ b/fs/fs.go @@ -0,0 +1,86 @@ +package fs + +import ( + "io" + + "github.com/masahiro331/go-disk/types" + "golang.org/x/xerrors" +) + +var ( + _ types.Driver = &DirectFileSystem{} + _ types.Partition = &DirectFileSystemPartition{} +) + +type DirectFileSystem struct { + Partition *DirectFileSystemPartition +} + +func (d *DirectFileSystem) Next() (types.Partition, error) { + if d.Partition == nil { + return nil, io.EOF + } + partition := d.Partition + d.Partition = nil + + return partition, nil +} + +type DirectFileSystemPartition struct { + sectionReader *io.SectionReader +} + +func (p DirectFileSystemPartition) Name() string { + return "0" +} + +func (p DirectFileSystemPartition) GetType() []byte { + return []byte{} +} + +func (p DirectFileSystemPartition) GetStartSector() uint64 { + return uint64(0) +} + +func (p DirectFileSystemPartition) Bootable() bool { + return false +} + +func (p DirectFileSystemPartition) GetSize() uint64 { + return uint64(p.sectionReader.Size()) +} + +func (p DirectFileSystemPartition) GetSectionReader() io.SectionReader { + return *p.sectionReader +} + +func (p DirectFileSystemPartition) IsSupported() bool { + return true +} + +func NewDirectFileSystem(sr *io.SectionReader) (*DirectFileSystem, error) { + _, err := sr.Seek(0, io.SeekStart) + if err != nil { + return nil, xerrors.Errorf("failed to DirectFileSystem seek offset error: %w", err) + } + return &DirectFileSystem{ + Partition: &DirectFileSystemPartition{ + sectionReader: sr, + }, + }, nil +} + +type CheckFsFunc func(r io.Reader) bool + +func CheckFileSystems(r *io.SectionReader, checkFsFuncs []CheckFsFunc) (bool, error) { + for _, checkFsFunc := range checkFsFuncs { + _, err := r.Seek(0, io.SeekStart) + if err != nil { + return false, xerrors.Errorf("failed to seek offset error: %w", err) + } + if checkFsFunc(r) { + return true, nil + } + } + return false, nil +} diff --git a/go.mod b/go.mod index 25dd278..d347962 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,13 @@ module github.com/masahiro331/go-disk go 1.19 -require golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 +require ( + github.com/masahiro331/go-xfs-filesystem v0.0.0-20231205045356-1b22259a6c44 + golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 +) + +require ( + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.23.0 // indirect +) diff --git a/go.sum b/go.sum index 1ff365f..057d9a9 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,22 @@ -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/masahiro331/go-xfs-filesystem v0.0.0-20231205045356-1b22259a6c44 h1:VmSjn0UCyfXUNdePDr7uM/uZTnGSp+mKD5+cYkEoLx4= +github.com/masahiro331/go-xfs-filesystem v0.0.0-20231205045356-1b22259a6c44/go.mod h1:QKBZqdn6teT0LK3QhAf3K6xakItd1LonOShOEC44idQ= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/testdata/fs.bin b/testdata/fs.bin new file mode 100644 index 0000000..ecf4288 Binary files /dev/null and b/testdata/fs.bin differ diff --git a/types/types.go b/types/types.go index 9883ffb..1e822ef 100644 --- a/types/types.go +++ b/types/types.go @@ -2,6 +2,10 @@ package types import "io" +type Driver interface { + Next() (Partition, error) +} + type Partition interface { Bootable() bool GetStartSector() uint64