2023-10-05 17:15:41 +02:00
|
|
|
package storev1
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2023-10-11 13:08:29 +02:00
|
|
|
"crypto/sha256"
|
2023-10-05 17:15:41 +02:00
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/nix-community/go-nix/pkg/storepath"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Validate performs some checks on the PathInfo struct, returning either the
|
|
|
|
// StorePath of the root node, or an error.
|
|
|
|
func (p *PathInfo) Validate() (*storepath.StorePath, error) {
|
|
|
|
// ensure References has the right number of bytes.
|
|
|
|
for i, reference := range p.GetReferences() {
|
|
|
|
if len(reference) != storepath.PathHashSize {
|
|
|
|
return nil, fmt.Errorf("invalid length of digest at position %d, expected %d, got %d", i, storepath.PathHashSize, len(reference))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there's a Narinfo field populated..
|
|
|
|
if narInfo := p.GetNarinfo(); narInfo != nil {
|
2023-10-11 13:08:29 +02:00
|
|
|
// ensure the NarSha256 digest has the correct length.
|
|
|
|
if len(narInfo.GetNarSha256()) != sha256.Size {
|
|
|
|
return nil, fmt.Errorf("invalid number of bytes for NarSha256: expected %d, got %d", sha256.Size, len(narInfo.GetNarSha256()))
|
|
|
|
}
|
|
|
|
|
2023-10-05 17:15:41 +02:00
|
|
|
// ensure the number of references matches len(References).
|
|
|
|
if len(narInfo.GetReferenceNames()) != len(p.GetReferences()) {
|
|
|
|
return nil, fmt.Errorf("inconsistent number of references: %d (references) vs %d (narinfo)", len(narInfo.GetReferenceNames()), len(p.GetReferences()))
|
|
|
|
}
|
|
|
|
|
|
|
|
// for each ReferenceName…
|
|
|
|
for i, referenceName := range narInfo.GetReferenceNames() {
|
|
|
|
// ensure it parses to a store path
|
|
|
|
storePath, err := storepath.FromString(referenceName)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid ReferenceName at position %d: %w", i, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ensure the digest matches the one at References[i]
|
|
|
|
if !bytes.Equal(p.GetReferences()[i], storePath.Digest) {
|
|
|
|
return nil, fmt.Errorf(
|
|
|
|
"digest in ReferenceName at position %d does not match digest in PathInfo, expected %s, got %s",
|
|
|
|
i,
|
|
|
|
base64.StdEncoding.EncodeToString(p.GetReferences()[i]),
|
|
|
|
base64.StdEncoding.EncodeToString(storePath.Digest),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ensure there is a (root) node present
|
|
|
|
rootNode := p.GetNode()
|
|
|
|
if rootNode == nil {
|
|
|
|
return nil, fmt.Errorf("root node must be set")
|
|
|
|
}
|
|
|
|
|
2023-10-10 23:03:47 +02:00
|
|
|
// for all three node types, ensure the name properly parses to a store path,
|
|
|
|
// and in case it refers to a digest, ensure it has the right length.
|
|
|
|
|
2023-10-11 00:22:40 +02:00
|
|
|
var storePath *storepath.StorePath
|
|
|
|
var err error
|
|
|
|
|
2023-10-05 17:15:41 +02:00
|
|
|
if node := rootNode.GetDirectory(); node != nil {
|
|
|
|
if len(node.Digest) != 32 {
|
|
|
|
return nil, fmt.Errorf("invalid digest size for %s, expected %d, got %d", node.Name, 32, len(node.Digest))
|
|
|
|
}
|
|
|
|
|
2023-10-11 00:22:40 +02:00
|
|
|
storePath, err = storepath.FromString(string(node.GetName()))
|
2023-10-05 17:15:41 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to parse %s as StorePath: %w", node.Name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if node := rootNode.GetFile(); node != nil {
|
|
|
|
if len(node.Digest) != 32 {
|
|
|
|
return nil, fmt.Errorf("invalid digest size for %s, expected %d, got %d", node.Name, 32, len(node.Digest))
|
|
|
|
}
|
|
|
|
|
2023-10-11 00:22:40 +02:00
|
|
|
storePath, err = storepath.FromString(string(node.GetName()))
|
2023-10-05 17:15:41 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to parse %s as StorePath: %w", node.Name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if node := rootNode.GetSymlink(); node != nil {
|
2023-10-11 00:22:40 +02:00
|
|
|
storePath, err = storepath.FromString(string(node.GetName()))
|
2023-10-05 17:15:41 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("unable to parse %s as StorePath: %w", node.Name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// this would only happen if we introduced a new type
|
|
|
|
panic("unreachable")
|
|
|
|
}
|
2023-10-11 00:22:40 +02:00
|
|
|
|
|
|
|
// If the Deriver field is populated, ensure it parses to a StorePath.
|
|
|
|
// We can't check for it to *not* end with .drv, as the .drv files produced by
|
|
|
|
// recursive Nix end with multiple .drv suffixes, and only one is popped when
|
|
|
|
// converting to this field.
|
|
|
|
if p.Deriver != nil {
|
|
|
|
storePath := storepath.StorePath{
|
|
|
|
Name: string(p.Deriver.GetName()),
|
|
|
|
Digest: p.Deriver.GetDigest(),
|
|
|
|
}
|
|
|
|
if err := storePath.Validate(); err != nil {
|
|
|
|
return nil, fmt.Errorf("invalid deriver field: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return storePath, nil
|
2023-10-05 17:15:41 +02:00
|
|
|
}
|