filestore.go 8.7 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/docker/pkg/ioutils"
  17. "github.com/opencontainers/go-digest"
  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. ws *ioutils.AtomicWriteSet
  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. ws, err := ioutils.NewAtomicWriteSet(tmpDir)
  64. if err != nil {
  65. return nil, err
  66. }
  67. return &fileMetadataTransaction{
  68. store: fms,
  69. ws: ws,
  70. }, nil
  71. }
  72. func (fm *fileMetadataTransaction) SetSize(size int64) error {
  73. content := fmt.Sprintf("%d", size)
  74. return fm.ws.WriteFile("size", []byte(content), 0644)
  75. }
  76. func (fm *fileMetadataTransaction) SetParent(parent ChainID) error {
  77. return fm.ws.WriteFile("parent", []byte(digest.Digest(parent).String()), 0644)
  78. }
  79. func (fm *fileMetadataTransaction) SetDiffID(diff DiffID) error {
  80. return fm.ws.WriteFile("diff", []byte(digest.Digest(diff).String()), 0644)
  81. }
  82. func (fm *fileMetadataTransaction) SetCacheID(cacheID string) error {
  83. return fm.ws.WriteFile("cache-id", []byte(cacheID), 0644)
  84. }
  85. func (fm *fileMetadataTransaction) SetDescriptor(ref distribution.Descriptor) error {
  86. jsonRef, err := json.Marshal(ref)
  87. if err != nil {
  88. return err
  89. }
  90. return fm.ws.WriteFile("descriptor.json", jsonRef, 0644)
  91. }
  92. func (fm *fileMetadataTransaction) TarSplitWriter(compressInput bool) (io.WriteCloser, error) {
  93. f, err := fm.ws.FileWriter("tar-split.json.gz", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0644)
  94. if err != nil {
  95. return nil, err
  96. }
  97. var wc io.WriteCloser
  98. if compressInput {
  99. wc = gzip.NewWriter(f)
  100. } else {
  101. wc = f
  102. }
  103. return ioutils.NewWriteCloserWrapper(wc, func() error {
  104. wc.Close()
  105. return f.Close()
  106. }), nil
  107. }
  108. func (fm *fileMetadataTransaction) Commit(layer ChainID) error {
  109. finalDir := fm.store.getLayerDirectory(layer)
  110. if err := os.MkdirAll(filepath.Dir(finalDir), 0755); err != nil {
  111. return err
  112. }
  113. return fm.ws.Commit(finalDir)
  114. }
  115. func (fm *fileMetadataTransaction) Cancel() error {
  116. return fm.ws.Cancel()
  117. }
  118. func (fm *fileMetadataTransaction) String() string {
  119. return fm.ws.String()
  120. }
  121. func (fms *fileMetadataStore) GetSize(layer ChainID) (int64, error) {
  122. content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "size"))
  123. if err != nil {
  124. return 0, err
  125. }
  126. size, err := strconv.ParseInt(string(content), 10, 64)
  127. if err != nil {
  128. return 0, err
  129. }
  130. return size, nil
  131. }
  132. func (fms *fileMetadataStore) GetParent(layer ChainID) (ChainID, error) {
  133. content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "parent"))
  134. if err != nil {
  135. if os.IsNotExist(err) {
  136. return "", nil
  137. }
  138. return "", err
  139. }
  140. dgst, err := digest.Parse(strings.TrimSpace(string(content)))
  141. if err != nil {
  142. return "", err
  143. }
  144. return ChainID(dgst), nil
  145. }
  146. func (fms *fileMetadataStore) GetDiffID(layer ChainID) (DiffID, error) {
  147. content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "diff"))
  148. if err != nil {
  149. return "", err
  150. }
  151. dgst, err := digest.Parse(strings.TrimSpace(string(content)))
  152. if err != nil {
  153. return "", err
  154. }
  155. return DiffID(dgst), nil
  156. }
  157. func (fms *fileMetadataStore) GetCacheID(layer ChainID) (string, error) {
  158. contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "cache-id"))
  159. if err != nil {
  160. return "", err
  161. }
  162. content := strings.TrimSpace(string(contentBytes))
  163. if !stringIDRegexp.MatchString(content) {
  164. return "", errors.New("invalid cache id value")
  165. }
  166. return content, nil
  167. }
  168. func (fms *fileMetadataStore) GetDescriptor(layer ChainID) (distribution.Descriptor, error) {
  169. content, err := ioutil.ReadFile(fms.getLayerFilename(layer, "descriptor.json"))
  170. if err != nil {
  171. if os.IsNotExist(err) {
  172. // only return empty descriptor to represent what is stored
  173. return distribution.Descriptor{}, nil
  174. }
  175. return distribution.Descriptor{}, err
  176. }
  177. var ref distribution.Descriptor
  178. err = json.Unmarshal(content, &ref)
  179. if err != nil {
  180. return distribution.Descriptor{}, err
  181. }
  182. return ref, err
  183. }
  184. func (fms *fileMetadataStore) TarSplitReader(layer ChainID) (io.ReadCloser, error) {
  185. fz, err := os.Open(fms.getLayerFilename(layer, "tar-split.json.gz"))
  186. if err != nil {
  187. return nil, err
  188. }
  189. f, err := gzip.NewReader(fz)
  190. if err != nil {
  191. return nil, err
  192. }
  193. return ioutils.NewReadCloserWrapper(f, func() error {
  194. f.Close()
  195. return fz.Close()
  196. }), nil
  197. }
  198. func (fms *fileMetadataStore) SetMountID(mount string, mountID string) error {
  199. if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
  200. return err
  201. }
  202. return ioutil.WriteFile(fms.getMountFilename(mount, "mount-id"), []byte(mountID), 0644)
  203. }
  204. func (fms *fileMetadataStore) SetInitID(mount string, init string) error {
  205. if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
  206. return err
  207. }
  208. return ioutil.WriteFile(fms.getMountFilename(mount, "init-id"), []byte(init), 0644)
  209. }
  210. func (fms *fileMetadataStore) SetMountParent(mount string, parent ChainID) error {
  211. if err := os.MkdirAll(fms.getMountDirectory(mount), 0755); err != nil {
  212. return err
  213. }
  214. return ioutil.WriteFile(fms.getMountFilename(mount, "parent"), []byte(digest.Digest(parent).String()), 0644)
  215. }
  216. func (fms *fileMetadataStore) GetMountID(mount string) (string, error) {
  217. contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "mount-id"))
  218. if err != nil {
  219. return "", err
  220. }
  221. content := strings.TrimSpace(string(contentBytes))
  222. if !stringIDRegexp.MatchString(content) {
  223. return "", errors.New("invalid mount id value")
  224. }
  225. return content, nil
  226. }
  227. func (fms *fileMetadataStore) GetInitID(mount string) (string, error) {
  228. contentBytes, err := ioutil.ReadFile(fms.getMountFilename(mount, "init-id"))
  229. if err != nil {
  230. if os.IsNotExist(err) {
  231. return "", nil
  232. }
  233. return "", err
  234. }
  235. content := strings.TrimSpace(string(contentBytes))
  236. if !stringIDRegexp.MatchString(content) {
  237. return "", errors.New("invalid init id value")
  238. }
  239. return content, nil
  240. }
  241. func (fms *fileMetadataStore) GetMountParent(mount string) (ChainID, error) {
  242. content, err := ioutil.ReadFile(fms.getMountFilename(mount, "parent"))
  243. if err != nil {
  244. if os.IsNotExist(err) {
  245. return "", nil
  246. }
  247. return "", err
  248. }
  249. dgst, err := digest.Parse(strings.TrimSpace(string(content)))
  250. if err != nil {
  251. return "", err
  252. }
  253. return ChainID(dgst), nil
  254. }
  255. func (fms *fileMetadataStore) List() ([]ChainID, []string, error) {
  256. var ids []ChainID
  257. for _, algorithm := range supportedAlgorithms {
  258. fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, string(algorithm)))
  259. if err != nil {
  260. if os.IsNotExist(err) {
  261. continue
  262. }
  263. return nil, nil, err
  264. }
  265. for _, fi := range fileInfos {
  266. if fi.IsDir() && fi.Name() != "mounts" {
  267. dgst := digest.NewDigestFromHex(string(algorithm), fi.Name())
  268. if err := dgst.Validate(); err != nil {
  269. logrus.Debugf("Ignoring invalid digest %s:%s", algorithm, fi.Name())
  270. } else {
  271. ids = append(ids, ChainID(dgst))
  272. }
  273. }
  274. }
  275. }
  276. fileInfos, err := ioutil.ReadDir(filepath.Join(fms.root, "mounts"))
  277. if err != nil {
  278. if os.IsNotExist(err) {
  279. return ids, []string{}, nil
  280. }
  281. return nil, nil, err
  282. }
  283. var mounts []string
  284. for _, fi := range fileInfos {
  285. if fi.IsDir() {
  286. mounts = append(mounts, fi.Name())
  287. }
  288. }
  289. return ids, mounts, nil
  290. }
  291. func (fms *fileMetadataStore) Remove(layer ChainID) error {
  292. return os.RemoveAll(fms.getLayerDirectory(layer))
  293. }
  294. func (fms *fileMetadataStore) RemoveMount(mount string) error {
  295. return os.RemoveAll(fms.getMountDirectory(mount))
  296. }