github.com/lusis/distribution@v2.0.1+incompatible/registry/storage/blobstore.go (about) 1 package storage 2 3 import ( 4 "fmt" 5 6 ctxu "github.com/docker/distribution/context" 7 "github.com/docker/distribution/digest" 8 storagedriver "github.com/docker/distribution/registry/storage/driver" 9 "golang.org/x/net/context" 10 ) 11 12 // TODO(stevvooe): Currently, the blobStore implementation used by the 13 // manifest store. The layer store should be refactored to better leverage the 14 // blobStore, reducing duplicated code. 15 16 // blobStore implements a generalized blob store over a driver, supporting the 17 // read side and link management. This object is intentionally a leaky 18 // abstraction, providing utility methods that support creating and traversing 19 // backend links. 20 type blobStore struct { 21 driver storagedriver.StorageDriver 22 pm *pathMapper 23 ctx context.Context 24 } 25 26 // exists reports whether or not the path exists. If the driver returns error 27 // other than storagedriver.PathNotFound, an error may be returned. 28 func (bs *blobStore) exists(dgst digest.Digest) (bool, error) { 29 path, err := bs.path(dgst) 30 31 if err != nil { 32 return false, err 33 } 34 35 ok, err := exists(bs.driver, path) 36 if err != nil { 37 return false, err 38 } 39 40 return ok, nil 41 } 42 43 // get retrieves the blob by digest, returning it a byte slice. This should 44 // only be used for small objects. 45 func (bs *blobStore) get(dgst digest.Digest) ([]byte, error) { 46 bp, err := bs.path(dgst) 47 if err != nil { 48 return nil, err 49 } 50 51 return bs.driver.GetContent(bp) 52 } 53 54 // link links the path to the provided digest by writing the digest into the 55 // target file. 56 func (bs *blobStore) link(path string, dgst digest.Digest) error { 57 if exists, err := bs.exists(dgst); err != nil { 58 return err 59 } else if !exists { 60 return fmt.Errorf("cannot link non-existent blob") 61 } 62 63 // The contents of the "link" file are the exact string contents of the 64 // digest, which is specified in that package. 65 return bs.driver.PutContent(path, []byte(dgst)) 66 } 67 68 // linked reads the link at path and returns the content. 69 func (bs *blobStore) linked(path string) ([]byte, error) { 70 linked, err := bs.readlink(path) 71 if err != nil { 72 return nil, err 73 } 74 75 return bs.get(linked) 76 } 77 78 // readlink returns the linked digest at path. 79 func (bs *blobStore) readlink(path string) (digest.Digest, error) { 80 content, err := bs.driver.GetContent(path) 81 if err != nil { 82 return "", err 83 } 84 85 linked, err := digest.ParseDigest(string(content)) 86 if err != nil { 87 return "", err 88 } 89 90 if exists, err := bs.exists(linked); err != nil { 91 return "", err 92 } else if !exists { 93 return "", fmt.Errorf("link %q invalid: blob %s does not exist", path, linked) 94 } 95 96 return linked, nil 97 } 98 99 // resolve reads the digest link at path and returns the blob store link. 100 func (bs *blobStore) resolve(path string) (string, error) { 101 dgst, err := bs.readlink(path) 102 if err != nil { 103 return "", err 104 } 105 106 return bs.path(dgst) 107 } 108 109 // put stores the content p in the blob store, calculating the digest. If the 110 // content is already present, only the digest will be returned. This should 111 // only be used for small objects, such as manifests. 112 func (bs *blobStore) put(p []byte) (digest.Digest, error) { 113 dgst, err := digest.FromBytes(p) 114 if err != nil { 115 ctxu.GetLogger(bs.ctx).Errorf("error digesting content: %v, %s", err, string(p)) 116 return "", err 117 } 118 119 bp, err := bs.path(dgst) 120 if err != nil { 121 return "", err 122 } 123 124 // If the content already exists, just return the digest. 125 if exists, err := bs.exists(dgst); err != nil { 126 return "", err 127 } else if exists { 128 return dgst, nil 129 } 130 131 return dgst, bs.driver.PutContent(bp, p) 132 } 133 134 // path returns the canonical path for the blob identified by digest. The blob 135 // may or may not exist. 136 func (bs *blobStore) path(dgst digest.Digest) (string, error) { 137 bp, err := bs.pm.path(blobDataPathSpec{ 138 digest: dgst, 139 }) 140 141 if err != nil { 142 return "", err 143 } 144 145 return bp, nil 146 } 147 148 // exists provides a utility method to test whether or not 149 func exists(driver storagedriver.StorageDriver, path string) (bool, error) { 150 if _, err := driver.Stat(path); err != nil { 151 switch err := err.(type) { 152 case storagedriver.PathNotFoundError: 153 return false, nil 154 default: 155 return false, err 156 } 157 } 158 159 return true, nil 160 }