filestore.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. package layer // import "github.com/docker/docker/layer"
  2. import (
  3. "compress/gzip"
  4. "context"
  5. "encoding/json"
  6. "io"
  7. "os"
  8. "path/filepath"
  9. "regexp"
  10. "strconv"
  11. "strings"
  12. "github.com/containerd/log"
  13. "github.com/docker/distribution"
  14. "github.com/docker/docker/pkg/ioutils"
  15. "github.com/opencontainers/go-digest"
  16. "github.com/pkg/errors"
  17. )
  18. var (
  19. stringIDRegexp = regexp.MustCompile(`^[a-f0-9]{64}(-init)?$`)
  20. supportedAlgorithms = []digest.Algorithm{
  21. digest.SHA256,
  22. // digest.SHA384, // Currently not used
  23. // digest.SHA512, // Currently not used
  24. }
  25. )
  26. type fileMetadataStore struct {
  27. root string
  28. }
  29. type fileMetadataTransaction struct {
  30. store *fileMetadataStore
  31. ws *ioutils.AtomicWriteSet
  32. }
  33. // newFSMetadataStore returns an instance of a metadata store
  34. // which is backed by files on disk using the provided root
  35. // as the root of metadata files.
  36. func newFSMetadataStore(root string) (*fileMetadataStore, error) {
  37. if err := os.MkdirAll(root, 0o700); err != nil {
  38. return nil, err
  39. }
  40. return &fileMetadataStore{
  41. root: root,
  42. }, nil
  43. }
  44. func (fms *fileMetadataStore) getLayerDirectory(layer ChainID) string {
  45. dgst := digest.Digest(layer)
  46. return filepath.Join(fms.root, string(dgst.Algorithm()), dgst.Encoded())
  47. }
  48. func (fms *fileMetadataStore) getLayerFilename(layer ChainID, filename string) string {
  49. return filepath.Join(fms.getLayerDirectory(layer), filename)
  50. }
  51. func (fms *fileMetadataStore) getMountDirectory(mount string) string {
  52. return filepath.Join(fms.root, "mounts", mount)
  53. }
  54. func (fms *fileMetadataStore) getMountFilename(mount, filename string) string {
  55. return filepath.Join(fms.getMountDirectory(mount), filename)
  56. }
  57. func (fms *fileMetadataStore) StartTransaction() (*fileMetadataTransaction, error) {
  58. tmpDir := filepath.Join(fms.root, "tmp")
  59. if err := os.MkdirAll(tmpDir, 0o755); err != nil {
  60. return nil, err
  61. }
  62. ws, err := ioutils.NewAtomicWriteSet(tmpDir)
  63. if err != nil {
  64. return nil, err
  65. }
  66. return &fileMetadataTransaction{
  67. store: fms,
  68. ws: ws,
  69. }, nil
  70. }
  71. func (fm *fileMetadataTransaction) SetSize(size int64) error {
  72. return fm.ws.WriteFile("size", []byte(strconv.FormatInt(size, 10)), 0o644)
  73. }
  74. func (fm *fileMetadataTransaction) SetParent(parent ChainID) error {
  75. return fm.ws.WriteFile("parent", []byte(digest.Digest(parent).String()), 0o644)
  76. }
  77. func (fm *fileMetadataTransaction) SetDiffID(diff DiffID) error {
  78. return fm.ws.WriteFile("diff", []byte(digest.Digest(diff).String()), 0o644)
  79. }
  80. func (fm *fileMetadataTransaction) SetCacheID(cacheID string) error {
  81. return fm.ws.WriteFile("cache-id", []byte(cacheID), 0o644)
  82. }
  83. func (fm *fileMetadataTransaction) SetDescriptor(ref distribution.Descriptor) error {
  84. jsonRef, err := json.Marshal(ref)
  85. if err != nil {
  86. return err
  87. }
  88. return fm.ws.WriteFile("descriptor.json", jsonRef, 0o644)
  89. }
  90. func (fm *fileMetadataTransaction) TarSplitWriter(compressInput bool) (io.WriteCloser, error) {
  91. f, err := fm.ws.FileWriter("tar-split.json.gz", os.O_TRUNC|os.O_CREATE|os.O_WRONLY, 0o644)
  92. if err != nil {
  93. return nil, err
  94. }
  95. var wc io.WriteCloser
  96. if compressInput {
  97. wc = gzip.NewWriter(f)
  98. } else {
  99. wc = f
  100. }
  101. return ioutils.NewWriteCloserWrapper(wc, func() error {
  102. wc.Close()
  103. return f.Close()
  104. }), nil
  105. }
  106. func (fm *fileMetadataTransaction) Commit(layer ChainID) error {
  107. finalDir := fm.store.getLayerDirectory(layer)
  108. if err := os.MkdirAll(filepath.Dir(finalDir), 0o755); err != nil {
  109. return err
  110. }
  111. return fm.ws.Commit(finalDir)
  112. }
  113. func (fm *fileMetadataTransaction) Cancel() error {
  114. return fm.ws.Cancel()
  115. }
  116. func (fm *fileMetadataTransaction) String() string {
  117. return fm.ws.String()
  118. }
  119. func (fms *fileMetadataStore) GetSize(layer ChainID) (int64, error) {
  120. content, err := os.ReadFile(fms.getLayerFilename(layer, "size"))
  121. if err != nil {
  122. return 0, err
  123. }
  124. size, err := strconv.ParseInt(string(content), 10, 64)
  125. if err != nil {
  126. return 0, err
  127. }
  128. return size, nil
  129. }
  130. func (fms *fileMetadataStore) GetParent(layer ChainID) (ChainID, error) {
  131. content, err := os.ReadFile(fms.getLayerFilename(layer, "parent"))
  132. if err != nil {
  133. if os.IsNotExist(err) {
  134. return "", nil
  135. }
  136. return "", err
  137. }
  138. dgst, err := digest.Parse(strings.TrimSpace(string(content)))
  139. if err != nil {
  140. return "", err
  141. }
  142. return ChainID(dgst), nil
  143. }
  144. func (fms *fileMetadataStore) GetDiffID(layer ChainID) (DiffID, error) {
  145. content, err := os.ReadFile(fms.getLayerFilename(layer, "diff"))
  146. if err != nil {
  147. return "", err
  148. }
  149. dgst, err := digest.Parse(strings.TrimSpace(string(content)))
  150. if err != nil {
  151. return "", err
  152. }
  153. return DiffID(dgst), nil
  154. }
  155. func (fms *fileMetadataStore) GetCacheID(layer ChainID) (string, error) {
  156. contentBytes, err := os.ReadFile(fms.getLayerFilename(layer, "cache-id"))
  157. if err != nil {
  158. return "", err
  159. }
  160. content := strings.TrimSpace(string(contentBytes))
  161. if content == "" {
  162. return "", errors.Errorf("invalid cache id value")
  163. }
  164. return content, nil
  165. }
  166. func (fms *fileMetadataStore) GetDescriptor(layer ChainID) (distribution.Descriptor, error) {
  167. content, err := os.ReadFile(fms.getLayerFilename(layer, "descriptor.json"))
  168. if err != nil {
  169. if os.IsNotExist(err) {
  170. // only return empty descriptor to represent what is stored
  171. return distribution.Descriptor{}, nil
  172. }
  173. return distribution.Descriptor{}, err
  174. }
  175. var ref distribution.Descriptor
  176. err = json.Unmarshal(content, &ref)
  177. if err != nil {
  178. return distribution.Descriptor{}, err
  179. }
  180. return ref, err
  181. }
  182. func (fms *fileMetadataStore) TarSplitReader(layer ChainID) (io.ReadCloser, error) {
  183. fz, err := os.Open(fms.getLayerFilename(layer, "tar-split.json.gz"))
  184. if err != nil {
  185. return nil, err
  186. }
  187. f, err := gzip.NewReader(fz)
  188. if err != nil {
  189. fz.Close()
  190. return nil, err
  191. }
  192. return ioutils.NewReadCloserWrapper(f, func() error {
  193. f.Close()
  194. return fz.Close()
  195. }), nil
  196. }
  197. func (fms *fileMetadataStore) SetMountID(mount string, mountID string) error {
  198. if err := os.MkdirAll(fms.getMountDirectory(mount), 0o755); err != nil {
  199. return err
  200. }
  201. return os.WriteFile(fms.getMountFilename(mount, "mount-id"), []byte(mountID), 0o644)
  202. }
  203. func (fms *fileMetadataStore) SetInitID(mount string, init string) error {
  204. if err := os.MkdirAll(fms.getMountDirectory(mount), 0o755); err != nil {
  205. return err
  206. }
  207. return os.WriteFile(fms.getMountFilename(mount, "init-id"), []byte(init), 0o644)
  208. }
  209. func (fms *fileMetadataStore) SetMountParent(mount string, parent ChainID) error {
  210. if err := os.MkdirAll(fms.getMountDirectory(mount), 0o755); err != nil {
  211. return err
  212. }
  213. return os.WriteFile(fms.getMountFilename(mount, "parent"), []byte(digest.Digest(parent).String()), 0o644)
  214. }
  215. func (fms *fileMetadataStore) GetMountID(mount string) (string, error) {
  216. contentBytes, err := os.ReadFile(fms.getMountFilename(mount, "mount-id"))
  217. if err != nil {
  218. return "", err
  219. }
  220. content := strings.TrimSpace(string(contentBytes))
  221. if !stringIDRegexp.MatchString(content) {
  222. return "", errors.New("invalid mount id value")
  223. }
  224. return content, nil
  225. }
  226. func (fms *fileMetadataStore) GetInitID(mount string) (string, error) {
  227. contentBytes, err := os.ReadFile(fms.getMountFilename(mount, "init-id"))
  228. if err != nil {
  229. if os.IsNotExist(err) {
  230. return "", nil
  231. }
  232. return "", err
  233. }
  234. content := strings.TrimSpace(string(contentBytes))
  235. if !stringIDRegexp.MatchString(content) {
  236. return "", errors.New("invalid init id value")
  237. }
  238. return content, nil
  239. }
  240. func (fms *fileMetadataStore) GetMountParent(mount string) (ChainID, error) {
  241. content, err := os.ReadFile(fms.getMountFilename(mount, "parent"))
  242. if err != nil {
  243. if os.IsNotExist(err) {
  244. return "", nil
  245. }
  246. return "", err
  247. }
  248. dgst, err := digest.Parse(strings.TrimSpace(string(content)))
  249. if err != nil {
  250. return "", err
  251. }
  252. return ChainID(dgst), nil
  253. }
  254. func (fms *fileMetadataStore) getOrphan() ([]roLayer, error) {
  255. var orphanLayers []roLayer
  256. for _, algorithm := range supportedAlgorithms {
  257. fileInfos, err := os.ReadDir(filepath.Join(fms.root, string(algorithm)))
  258. if err != nil {
  259. if os.IsNotExist(err) {
  260. continue
  261. }
  262. return nil, err
  263. }
  264. for _, fi := range fileInfos {
  265. if !fi.IsDir() || !strings.HasSuffix(fi.Name(), "-removing") {
  266. continue
  267. }
  268. // At this stage, fi.Name value looks like <digest>-<random>-removing
  269. // Split on '-' to get the digest value.
  270. nameSplit := strings.Split(fi.Name(), "-")
  271. dgst := digest.NewDigestFromEncoded(algorithm, nameSplit[0])
  272. if err := dgst.Validate(); err != nil {
  273. log.G(context.TODO()).WithError(err).WithField("digest", string(algorithm)+":"+nameSplit[0]).Debug("ignoring invalid digest")
  274. continue
  275. }
  276. chainFile := filepath.Join(fms.root, string(algorithm), fi.Name(), "cache-id")
  277. contentBytes, err := os.ReadFile(chainFile)
  278. if err != nil {
  279. if !os.IsNotExist(err) {
  280. log.G(context.TODO()).WithError(err).WithField("digest", dgst).Error("failed to read cache ID")
  281. }
  282. continue
  283. }
  284. cacheID := strings.TrimSpace(string(contentBytes))
  285. if cacheID == "" {
  286. log.G(context.TODO()).Error("invalid cache ID")
  287. continue
  288. }
  289. l := &roLayer{
  290. chainID: ChainID(dgst),
  291. cacheID: cacheID,
  292. }
  293. orphanLayers = append(orphanLayers, *l)
  294. }
  295. }
  296. return orphanLayers, nil
  297. }
  298. func (fms *fileMetadataStore) List() ([]ChainID, []string, error) {
  299. var ids []ChainID
  300. for _, algorithm := range supportedAlgorithms {
  301. fileInfos, err := os.ReadDir(filepath.Join(fms.root, string(algorithm)))
  302. if err != nil {
  303. if os.IsNotExist(err) {
  304. continue
  305. }
  306. return nil, nil, err
  307. }
  308. for _, fi := range fileInfos {
  309. if fi.IsDir() && fi.Name() != "mounts" {
  310. dgst := digest.NewDigestFromEncoded(algorithm, fi.Name())
  311. if err := dgst.Validate(); err != nil {
  312. log.G(context.TODO()).Debugf("Ignoring invalid digest %s:%s", algorithm, fi.Name())
  313. } else {
  314. ids = append(ids, ChainID(dgst))
  315. }
  316. }
  317. }
  318. }
  319. fileInfos, err := os.ReadDir(filepath.Join(fms.root, "mounts"))
  320. if err != nil {
  321. if os.IsNotExist(err) {
  322. return ids, []string{}, nil
  323. }
  324. return nil, nil, err
  325. }
  326. var mounts []string
  327. for _, fi := range fileInfos {
  328. if fi.IsDir() {
  329. mounts = append(mounts, fi.Name())
  330. }
  331. }
  332. return ids, mounts, nil
  333. }
  334. // Remove layerdb folder if that is marked for removal
  335. func (fms *fileMetadataStore) Remove(layer ChainID, cache string) error {
  336. dgst := digest.Digest(layer)
  337. files, err := os.ReadDir(filepath.Join(fms.root, string(dgst.Algorithm())))
  338. if err != nil {
  339. return err
  340. }
  341. for _, f := range files {
  342. if !strings.HasSuffix(f.Name(), "-removing") || !strings.HasPrefix(f.Name(), dgst.Encoded()) {
  343. continue
  344. }
  345. // Make sure that we only remove layerdb folder which points to
  346. // requested cacheID
  347. dir := filepath.Join(fms.root, string(dgst.Algorithm()), f.Name())
  348. chainFile := filepath.Join(dir, "cache-id")
  349. contentBytes, err := os.ReadFile(chainFile)
  350. if err != nil {
  351. log.G(context.TODO()).WithError(err).WithField("file", chainFile).Error("cannot get cache ID")
  352. continue
  353. }
  354. cacheID := strings.TrimSpace(string(contentBytes))
  355. if cacheID != cache {
  356. continue
  357. }
  358. log.G(context.TODO()).Debugf("Removing folder: %s", dir)
  359. err = os.RemoveAll(dir)
  360. if err != nil && !os.IsNotExist(err) {
  361. log.G(context.TODO()).WithError(err).WithField("name", f.Name()).Error("cannot remove layer")
  362. continue
  363. }
  364. }
  365. return nil
  366. }
  367. func (fms *fileMetadataStore) RemoveMount(mount string) error {
  368. return os.RemoveAll(fms.getMountDirectory(mount))
  369. }