1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
|
package main
import (
"crypto/sha256"
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"os"
"github.com/spf13/cobra"
cli "github.com/lxc/incus/v6/internal/cmd"
"github.com/lxc/incus/v6/shared/simplestreams"
)
type cmdVerify struct {
global *cmdGlobal
}
// Command generates the command definition.
func (c *cmdVerify) Command() *cobra.Command {
cmd := &cobra.Command{}
cmd.Use = "verify"
cmd.Short = "Verify the integrity of the server"
cmd.Long = cli.FormatSection("Description",
`Verify the integrity of the server
This command will analyze the image index and for every image and file
in the index, will validate that the files on disk exist and are of the
correct size and content.
`)
cmd.RunE = c.Run
return cmd
}
// Run runs the actual command logic.
func (c *cmdVerify) Run(cmd *cobra.Command, args []string) error {
// Quick checks.
exit, err := c.global.CheckArgs(cmd, args, 0, 0)
if exit {
return err
}
// Load the images file.
products := simplestreams.Products{}
body, err := os.ReadFile("streams/v1/images.json")
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return nil
}
return err
}
// Parse the existing images file.
err = json.Unmarshal(body, &products)
if err != nil {
return err
}
// Go over all the files.
for _, product := range products.Products {
for _, version := range product.Versions {
for _, item := range version.Items {
// Open the data.
dataFile, err := os.Open(item.Path)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
return fmt.Errorf("Missing image file %q", item.Path)
}
return err
}
// Get the size.
dataStat, err := dataFile.Stat()
if err != nil {
return err
}
if item.Size != dataStat.Size() {
return fmt.Errorf("File %q has a different size than listed in the index", item.Path)
}
// Get the sha256.
_, err = dataFile.Seek(0, 0)
if err != nil {
return err
}
hash256 := sha256.New()
_, err = io.Copy(hash256, dataFile)
if err != nil {
return err
}
dataSha256 := fmt.Sprintf("%x", hash256.Sum(nil))
if item.HashSha256 != dataSha256 {
return fmt.Errorf("File %q has a different SHA256 hash than listed in the index", item.Path)
}
// Done with this file.
dataFile.Close()
}
}
}
return nil
}
|