mirror of
https://github.com/ollama/ollama.git
synced 2025-03-29 03:01:45 +01:00
Commit 1829fb61 ("manifest: Fix crash on startup when trying to clean up unused files (#5840)") changed the config layer stored in manifests from a pointer to a value. This was done in order to avoid potential nil pointer dereferences after it is deserialized from JSON in the event that the field is missing. This changes the Layers slice to also be stored by value. This enables consistency in handling across the two objects.
127 lines
2.3 KiB
Go
127 lines
2.3 KiB
Go
package server
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
)
|
|
|
|
type Layer struct {
|
|
MediaType string `json:"mediaType"`
|
|
Digest string `json:"digest"`
|
|
Size int64 `json:"size"`
|
|
From string `json:"from,omitempty"`
|
|
status string
|
|
}
|
|
|
|
func NewLayer(r io.Reader, mediatype string) (Layer, error) {
|
|
blobs, err := GetBlobsPath("")
|
|
if err != nil {
|
|
return Layer{}, err
|
|
}
|
|
|
|
temp, err := os.CreateTemp(blobs, "sha256-")
|
|
if err != nil {
|
|
return Layer{}, err
|
|
}
|
|
defer temp.Close()
|
|
defer os.Remove(temp.Name())
|
|
|
|
sha256sum := sha256.New()
|
|
n, err := io.Copy(io.MultiWriter(temp, sha256sum), r)
|
|
if err != nil {
|
|
return Layer{}, err
|
|
}
|
|
|
|
if err := temp.Close(); err != nil {
|
|
return Layer{}, err
|
|
}
|
|
|
|
digest := fmt.Sprintf("sha256:%x", sha256sum.Sum(nil))
|
|
blob, err := GetBlobsPath(digest)
|
|
if err != nil {
|
|
return Layer{}, err
|
|
}
|
|
|
|
status := "using existing layer"
|
|
if _, err := os.Stat(blob); err != nil {
|
|
status = "creating new layer"
|
|
if err := os.Rename(temp.Name(), blob); err != nil {
|
|
return Layer{}, err
|
|
}
|
|
}
|
|
|
|
return Layer{
|
|
MediaType: mediatype,
|
|
Digest: digest,
|
|
Size: n,
|
|
status: fmt.Sprintf("%s %s", status, digest),
|
|
}, nil
|
|
}
|
|
|
|
func NewLayerFromLayer(digest, mediatype, from string) (Layer, error) {
|
|
if digest == "" {
|
|
return Layer{}, errors.New("creating new layer from layer with empty digest")
|
|
}
|
|
|
|
blob, err := GetBlobsPath(digest)
|
|
if err != nil {
|
|
return Layer{}, err
|
|
}
|
|
|
|
fi, err := os.Stat(blob)
|
|
if err != nil {
|
|
return Layer{}, err
|
|
}
|
|
|
|
return Layer{
|
|
MediaType: mediatype,
|
|
Digest: digest,
|
|
Size: fi.Size(),
|
|
From: from,
|
|
status: fmt.Sprintf("using existing layer %s", digest),
|
|
}, nil
|
|
}
|
|
|
|
func (l *Layer) Open() (io.ReadSeekCloser, error) {
|
|
if l.Digest == "" {
|
|
return nil, errors.New("opening layer with empty digest")
|
|
}
|
|
|
|
blob, err := GetBlobsPath(l.Digest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return os.Open(blob)
|
|
}
|
|
|
|
func (l *Layer) Remove() error {
|
|
if l.Digest == "" {
|
|
return nil
|
|
}
|
|
|
|
ms, err := Manifests()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, m := range ms {
|
|
for _, layer := range append(m.Layers, m.Config) {
|
|
if layer.Digest == l.Digest {
|
|
// something is using this layer
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
blob, err := GetBlobsPath(l.Digest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return os.Remove(blob)
|
|
}
|