aboutsummaryrefslogtreecommitdiff
path: root/verify/cmd
diff options
context:
space:
mode:
authoraxtloss <axtlos@getcryst.al>2024-02-28 23:42:26 +0100
committeraxtloss <axtlos@getcryst.al>2024-02-28 23:42:26 +0100
commit3341bd0e945341528033ec6ebaef4f611f654ebe (patch)
treef82d4557ac097d08583e56152352cbd5abd335ea /verify/cmd
parent91d58f9ae9e9d9adc2e19a0b56d2b9757f6696d6 (diff)
downloadfsverify-3341bd0e945341528033ec6ebaef4f611f654ebe.tar.gz
fsverify-3341bd0e945341528033ec6ebaef4f611f654ebe.tar.bz2
restructure repository layout
Diffstat (limited to 'verify/cmd')
-rw-r--r--verify/cmd/root.go22
-rw-r--r--verify/cmd/verify.go174
2 files changed, 196 insertions, 0 deletions
diff --git a/verify/cmd/root.go b/verify/cmd/root.go
new file mode 100644
index 0000000..d212e4e
--- /dev/null
+++ b/verify/cmd/root.go
@@ -0,0 +1,22 @@
+package cmd
+
+import (
+ "github.com/spf13/cobra"
+ "os"
+)
+
+var rootCmd = &cobra.Command{
+ Use: "fsverify",
+}
+
+func init() {
+ rootCmd.AddCommand(NewVerifyCommand())
+}
+
+func Execute() {
+ // cobra does not exit with a non-zero return code when failing
+ // solution from https://github.com/spf13/cobra/issues/221
+ if err := rootCmd.Execute(); err != nil {
+ os.Exit(1)
+ }
+}
diff --git a/verify/cmd/verify.go b/verify/cmd/verify.go
new file mode 100644
index 0000000..d0360f6
--- /dev/null
+++ b/verify/cmd/verify.go
@@ -0,0 +1,174 @@
+package cmd
+
+import (
+ "bytes"
+ "fmt"
+ "math"
+ "os"
+ "sync"
+
+ "github.com/axtloss/fsverify/config"
+ "github.com/axtloss/fsverify/core"
+ "github.com/spf13/cobra"
+)
+
+var validateFailed bool
+
+func NewVerifyCommand() *cobra.Command {
+ cmd := &cobra.Command{
+ Use: "verify",
+ Short: "Verify the root filesystem based on the given verification",
+ RunE: ValidateCommand,
+ SilenceUsage: true,
+ }
+
+ return cmd
+}
+
+// validateThread validates a chain of nodes against a given byte slice
+func validateThread(blockStart int, blockEnd int, bundleSize int, diskBytes []byte, n int, dbfile string, waitGroup *sync.WaitGroup, errChan chan error) {
+ defer waitGroup.Done()
+ defer close(errChan)
+ var reader *bytes.Reader
+ blockCount := math.Floor(float64(bundleSize / 2000))
+ totalReadBlocks := 0
+
+ db, err := core.OpenDB(dbfile, true)
+ if err != nil {
+ errChan <- err
+ }
+
+ reader = bytes.NewReader(diskBytes)
+
+ node, err := core.GetNode(fmt.Sprintf("Entrypoint%d", n), db)
+ if err != nil {
+ errChan <- err
+ }
+ block, i, err := core.ReadBlock(node, reader, totalReadBlocks)
+ totalReadBlocks = i
+
+ err = core.VerifyBlock(block, node)
+ if err != nil {
+ errChan <- err
+ }
+
+ for int64(totalReadBlocks) < int64(blockCount) {
+ if validateFailed {
+ return
+ }
+ nodeSum, err := node.GetHash()
+ if err != nil {
+ fmt.Println("Using node ", nodeSum)
+ errChan <- err
+ }
+ node, err = core.GetNode(nodeSum, db)
+ if err != nil {
+ fmt.Println("Failed to get next node")
+ errChan <- err
+ }
+ part, i, err := core.ReadBlock(node, reader, totalReadBlocks)
+ totalReadBlocks = i
+ if err != nil {
+ errChan <- err
+ validateFailed = true
+ return
+ }
+ err = core.VerifyBlock(part, node)
+ if err != nil {
+ errChan <- err
+ validateFailed = true
+ return
+ }
+
+ }
+
+}
+
+func ValidateCommand(_ *cobra.Command, args []string) error {
+ if len(args) != 1 {
+ return fmt.Errorf("Usage: fsverify verify [disk]")
+ }
+ header, err := core.ReadHeader(config.FsVerifyPart)
+ if err != nil {
+ return err
+ }
+
+ // Check if the partition is even correct
+ // this does not check if the partition has been tampered with
+ // it only checks if the specified partition is even an fsverify partition
+ if header.MagicNumber != 0xACAB {
+ return fmt.Errorf("sanity bit does not match. Expected %d, got %d", 0xACAB, header.MagicNumber)
+ }
+
+ fmt.Println("Reading DB")
+ dbfile, err := core.ReadDB(config.FsVerifyPart)
+ if err != nil {
+ return err
+ }
+ key, err := core.ReadKey()
+ if err != nil {
+ return err
+ }
+ fmt.Println("Key: " + key)
+ verified, err := core.VerifySignature(key, header.Signature, dbfile)
+ if err != nil {
+ return err
+ } else if !verified {
+ return fmt.Errorf("Signature verification failed\n")
+ } else {
+ fmt.Println("Signature verification success!")
+ }
+
+ fmt.Println("----")
+ disk, err := os.Open(args[0])
+ if err != nil {
+ return err
+ }
+ defer disk.Close()
+ diskInfo, err := disk.Stat()
+ if err != nil {
+ return err
+ }
+ diskSize := diskInfo.Size()
+
+ // If the filesystem size has increased ever since the fsverify partition was created
+ // it would mean that fsverify is not able to verify the entire partition, making it useless
+ if header.FilesystemSize*header.FilesystemUnit != int(diskSize) {
+ return fmt.Errorf("disk size does not match disk size specified in header. Expected %d, got %d", header.FilesystemSize*header.FilesystemUnit, diskSize)
+ }
+
+ bundleSize := math.Floor(float64(diskSize / int64(config.ProcCount)))
+ diskBytes := make([]byte, diskSize)
+ _, err = disk.Read(diskBytes)
+ if err != nil {
+ return err
+ }
+ reader := bytes.NewReader(diskBytes)
+ var waitGroup sync.WaitGroup
+ errChan := make(chan error)
+ validateFailed = false
+ for i := 0; i < config.ProcCount; i++ {
+ // To ensure that each thread only uses the byte area it is meant to use, a copy of the
+ // area is made
+ diskBytes, err := core.CopyByteArea(i*(int(bundleSize)), (i+1)*(int(bundleSize)), reader)
+ if err != nil {
+ fmt.Println("Failed to copy byte area ", i*int(bundleSize), " ", (i+1)+int(bundleSize))
+ return err
+ }
+ waitGroup.Add(1)
+ go validateThread(i*int(bundleSize), (i+1)*int(bundleSize), int(bundleSize), diskBytes, i, dbfile, &waitGroup, errChan)
+ }
+
+ go func() {
+ waitGroup.Wait()
+ close(errChan)
+ }()
+
+ for err := range errChan {
+ if err != nil {
+ return err
+ }
+ }
+
+ return nil
+}