filestore.go 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. package layer
  2. import (
  3. "compress/gzip"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "io"
  8. "io/ioutil"
  9. "os"
  10. "path/filepath"
  11. "regexp"
  12. "strconv"
  13. "strings"
  14. "github.com/Sirupsen/logrus"
  15. "github.com/docker/distribution"
  16. "github.com/docker/distribution/digest"
  17. "github.com/docker/docker/pkg/ioutils"
  18. )
  19. var (
  20. stringIDRegexp = regexp.MustCompile(`^[a-f0-9]{64}(-init)?$`)
  21. supportedAlgorithms = []digest.Algorithm{
  22. digest.SHA256,
  23. // digest.SHA384, // Currently not used
  24. // digest.SHA512, // Currently not used
  25. }
  26. )
  27. type fileMetadataStore struct {
  28. root string
  29. }
  30. type fileMetadataTransaction struct {
  31. store *fileMetadataStore
  32. root string
  33. }
  34. // NewFSMetadataStore returns an instance of a metadata store
  35. // which is backed by files on disk using the provided root
  36. // as the root of metadata files.
  37. func NewFSMetadataStore(root string) (MetadataStore, error) {
  38. if err := os.MkdirAll(root, 0700); err != nil {
  39. return nil, err
  40. }
  41. return &fileMetadataStore{
  42. root: root,
  43. }, nil
  44. }
  45. func (fms *fileMetadataStore) getLayerDirectory(layer ChainID) string {
  46. dgst := digest.Digest(layer)
  47. return filepath.Join(fms.root, string(dgst.Algorithm()), dgst.Hex())
  48. }
  49. func (fms *fileMetadataStore) getLayerFilename(layer ChainID, filename string) string {
  50. return filepath.Join(fms.getLayerDirectory(layer), filename)
  51. }
  52. func (fms *fileMetadataStore) getMountDirectory(mount string) string {
  53. return filepath.Join(fms.root, "mounts", mount)
  54. }
  55. func (fms *fileMetadataStore) getMountFilename(mount, filename string) string {
  56. return filepath.Join(fms.getMountDirectory(mount), filename)
  57. }
  58. func (fms *fileMetadataStore) StartTransaction() (MetadataTransaction, error) {
  59. tmpDir := filepath.Join(fms.root, "tmp")
  60. if err := os.MkdirAll(tmpDir, 0755); err != nil {
  61. return nil, err
  62. }
  63. td, err := ioutil.TempDir(tmpDir, "layer-")
  64. if err != nil {
  65. return nil, err
  66. }
  67. // Create a new tempdir
  68. return &fileMetadataTransaction{
  69. store: fms,
  70. root: td,
  71. }, nil
  72. }
  73. func (fm *fileMetadataTransaction) SetSize(size int64) error {
  74. content := fmt.Sprintf("%d", size)
  75. return ioutil.WriteFile(filepath.Join(fm.root, "size"), []byte(content), 0644)
  76. }
  77. func (fm *fileMetadataTransaction) SetParent(parent ChainID) error {
  78. return ioutil.WriteFile(filepath.Join(fm.root, "parent"), []byte(digest.Digest(parent).String()), 0644)
  79. }
  80. func (fm *fileMetadataTransaction) SetDiffID(diff DiffID) error {
  81. return ioutil.WriteFile(filepath.Join(fm.root, "diff"), []byte(digest.Digest(diff).String()), 0644)
  82. }
  83. func (fm *fileMetadataTransaction) SetCacheID(cacheID string) error {
  84. return ioutil.WriteFile(filepath.Join(fm.root, "cache-id"), []byte(cacheID), 0644)
  85. }
  86. func (fm *fileMetadataTransaction) SetDescriptor(ref distribution.Descriptor) error {
  87. jsonRef, err := json.Marshal(ref)
  88. if err != nil {
  89. return err
  90. }
  91. return ioutil.WriteFile(filepath.Join(fm.root, "descriptor.json"), jsonRef, 0644)
  92. }
  93. func (fm *fileMetadataTransaction) TarSplitWriter(compressInput bool) (io.WriteCloser, error) {
  94. f, err := os.OpenFile(filepath.Join(fm.root, "tar-split.json.gz"), os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
  95. if err != nil {
  96. return nil, err
  97. }
  98. var wc io.WriteCloser
  99. if compressInput {
  100. wc = gzip.NewWriter(f)
  101. } else {
  102. wc = f
  103. }
  104. return ioutils.NewWriteCloserWrapper(wc, func() error {
  105. wc.Close()
  106. return f.Close()
  107. }), nil
  108. }
  109. func (fm *fileMetadataTransaction) Commit(layer ChainID) error {
  110. finalDir := fm.store.getLayerDirectory(layer)
  111. if err := os.MkdirAll(filepath.Dir(finalDir), 0755); err != nil {
  112. return err
  113. }
  114. return os.Rename(fm.root, finalDir)
  115. }
  116. func (fm *fileMetadataTransaction) Cancel() error {
  117. return os.RemoveAll(fm.root)
  118. }
  119. func (fm *fileMetadataTransaction) String() string {
  120. return fm.root
  121. }
  122. func (fms *fileMetadataStore) GetSize(layer ChainID) (int64, error) {
  123. content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "size"))
  124. if err != nil {
  125. return 0, err
  126. }
  127. size, err := strconv.ParseInt(string(content), 10, 64)
  128. if err != nil {
  129. return 0, err
  130. }
  131. return size, nil
  132. }
  133. func (fms *fileMetadataStore) GetParent(layer ChainID) (ChainID, error) {
  134. content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "parent"))
  135. if err != nil {
  136. if os.IsNotExist(err) {
  137. return "", nil
  138. }
  139. return "", err
  140. }
  141. dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
  142. if err != nil {
  143. return "", err
  144. }
  145. return ChainID(dgst), nil
  146. }
  147. func (fms *fileMetadataStore) GetDiffID(layer ChainID) (DiffID, error) {
  148. content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "diff"))
  149. if err != nil {
  150. return "", err
  151. }
  152. dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
  153. if err != nil {
  154. return "", err
  155. }
  156. return DiffID(dgst), nil
  157. }
  158. func (fms *fileMetadataStore) GetCacheID(layer ChainID) (string, error) {
  159. contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "cache-id"))
  160. if err != nil {
  161. return "", err
  162. }
  163. content := strings.TrimSpace(string(contentBytes))
  164. if !stringIDRegexp.MatchString(content) {
  165. return "", errors.New("invalid cache id value")
  166. }
  167. return content, nil
  168. }
  169. func (fms *fileMetadataStore) GetDescriptor(layer ChainID) (distribution.Descriptor, error) {
  170. content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "descriptor.json"))
  171. if err != nil {
  172. if os.IsNotExist(err) {
  173. // only return empty descriptor to represent what is stored
  174. return distribution.Descriptor{}, nil
  175. }
  176. return distribution.Descriptor{}, err
  177. }
  178. var ref distribution.Descriptor
  179. err = json.Unmarshal(content, &ref)
  180. if err != nil {
  181. return distribution.Descriptor{}, err
  182. }
  183. return ref, err
  184. }
  185. func (fms *fileMetadataStore) TarSplitReader(layer ChainID) (io.ReadCloser, error) {
  186. fz, err := os.Open(fms.getLayerFilename(layer, "tar-split.json.gz"))
  187. if err != nil {
  188. return nil, err
  189. }
  190. f, err := gzip.NewReader(fz)
  191. if err != nil {
  192. return nil, err
  193. }
  194. return ioutils.NewReadCloserWrapper(f, func() error {
  195. f.Close()
  196. return fz.Close()
  197. }), nil
  198. }
  199. func (fms *fileMetadataStore) SetMountID(mount string, mountID string) error {
  200. if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
  201. return err
  202. }
  203. return ioutil.WriteFile(fms.getMountFilename(mount, "mount-id"), []byte(mountID), 0644)
  204. }
  205. func (fms *fileMetadataStore) SetInitID(mount string, init string) error {
  206. if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
  207. return err
  208. }
  209. return ioutil.WriteFile(fms.getMountFilename(mount, "init-id"), []byte(init), 0644)
  210. }
  211. func (fms *fileMetadataStore) SetMountParent(mount string, parent ChainID) error {
  212. if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
  213. return err
  214. }
  215. return ioutil.WriteFile(fms.getMountFilename(mount, "parent"), []byte(digest.Digest(parent).String()), 0644)
  216. }
  217. func (fms *fileMetadataStore) GetMountID(mount string) (string, error) {
  218. contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "mount-id"))
  219. if err != nil {
  220. return "", err
  221. }
  222. content := strings.TrimSpace(string(contentBytes))
  223. if !stringIDRegexp.MatchString(content) {
  224. return "", errors.New("invalid mount id value")
  225. }
  226. return content, nil
  227. }
  228. func (fms *fileMetadataStore) GetInitID(mount string) (string, error) {
  229. contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "init-id"))
  230. if err != nil {
  231. if os.IsNotExist(err) {
  232. return "", nil
  233. }
  234. return "", err
  235. }
  236. content := strings.TrimSpace(string(contentBytes))
  237. if !stringIDRegexp.MatchString(content) {
  238. return "", errors.New("invalid init id value")
  239. }
  240. return content, nil
  241. }
  242. func (fms *fileMetadataStore) GetMountParent(mount string) (ChainID, error) {
  243. content, err := ioutil.ReadFile(fms.getMountFilename(mount, "parent"))
  244. if err != nil {
  245. if os.IsNotExist(err) {
  246. return "", nil
  247. }
  248. return "", err
  249. }
  250. dgst, err := digest.ParseDigest(strings.TrimSpace(string(content)))
  251. if err != nil {
  252. return "", err
  253. }
  254. return ChainID(dgst), nil
  255. }
  256. func (fms *fileMetadataStore) List() ([]ChainID, []string, error) {
  257. var ids []ChainID
  258. for _, algorithm := range supportedAlgorithms {
  259. fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, string(algorithm)))
  260. if err != nil {
  261. if os.IsNotExist(err) {
  262. continue
  263. }
  264. return nil, nil, err
  265. }
  266. for _, fi := range fileInfos {
  267. if fi.IsDir() && fi.Name() != "mounts" {
  268. dgst := digest.NewDigestFromHex(string(algorithm), fi.Name())
  269. if err := dgst.Validate(); err != nil {
  270. logrus.Debugf("Ignoring invalid digest %s:%s", algorithm, fi.Name())
  271. } else {
  272. ids = append(ids, ChainID(dgst))
  273. }
  274. }
  275. }
  276. }
  277. fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, "mounts"))
  278. if err != nil {
  279. if os.IsNotExist(err) {
  280. return ids, []string{}, nil
  281. }
  282. return nil, nil, err
  283. }
  284. var mounts []string
  285. for _, fi := range fileInfos {
  286. if fi.IsDir() {
  287. mounts = append(mounts, fi.Name())
  288. }
  289. }
  290. return ids, mounts, nil
  291. }
  292. func (fms *fileMetadataStore) Remove(layer ChainID) error {
  293. return os.RemoveAll(fms.getLayerDirectory(layer))
  294. }
  295. func (fms *fileMetadataStore) RemoveMount(mount string) error {
  296. return os.RemoveAll(fms.getMountDirectory(mount))
  297. }