diff options
Diffstat (limited to 'tvix/store/protos/pathinfo.go')
-rw-r--r-- | tvix/store/protos/pathinfo.go | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/tvix/store/protos/pathinfo.go b/tvix/store/protos/pathinfo.go new file mode 100644 index 000000000000..6607b5c879fb --- /dev/null +++ b/tvix/store/protos/pathinfo.go @@ -0,0 +1,93 @@ +package storev1 + +import ( + "bytes" + "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 { + // 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") + } + + // ensure it properly parses to a store path, and in case it refers to a digest, ensure it has the right length. + 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)) + } + + storePath, err := storepath.FromString(string(node.GetName())) + + if err != nil { + return nil, fmt.Errorf("unable to parse %s as StorePath: %w", node.Name, err) + } + + return storePath, nil + + } 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)) + } + + storePath, err := storepath.FromString(string(node.GetName())) + if err != nil { + return nil, fmt.Errorf("unable to parse %s as StorePath: %w", node.Name, err) + } + + return storePath, nil + + } else if node := rootNode.GetSymlink(); node != nil { + storePath, err := storepath.FromString(string(node.GetName())) + + if err != nil { + return nil, fmt.Errorf("unable to parse %s as StorePath: %w", node.Name, err) + } + + return storePath, nil + + } else { + // this would only happen if we introduced a new type + panic("unreachable") + } +} |