1package layer // import "github.com/docker/docker/layer"
2
3import (
4	"compress/gzip"
5	"errors"
6	"fmt"
7	"io"
8	"os"
9
10	"github.com/opencontainers/go-digest"
11	"github.com/sirupsen/logrus"
12	"github.com/vbatts/tar-split/tar/asm"
13	"github.com/vbatts/tar-split/tar/storage"
14)
15
16// CreateRWLayerByGraphID creates a RWLayer in the layer store using
17// the provided name with the given graphID. To get the RWLayer
18// after migration the layer may be retrieved by the given name.
19func (ls *layerStore) CreateRWLayerByGraphID(name, graphID string, parent ChainID) (err error) {
20	ls.mountL.Lock()
21	defer ls.mountL.Unlock()
22	m, ok := ls.mounts[name]
23	if ok {
24		if m.parent.chainID != parent {
25			return errors.New("name conflict, mismatched parent")
26		}
27		if m.mountID != graphID {
28			return errors.New("mount already exists")
29		}
30
31		return nil
32	}
33
34	if !ls.driver.Exists(graphID) {
35		return fmt.Errorf("graph ID does not exist: %q", graphID)
36	}
37
38	var p *roLayer
39	if string(parent) != "" {
40		p = ls.get(parent)
41		if p == nil {
42			return ErrLayerDoesNotExist
43		}
44
45		// Release parent chain if error
46		defer func() {
47			if err != nil {
48				ls.layerL.Lock()
49				ls.releaseLayer(p)
50				ls.layerL.Unlock()
51			}
52		}()
53	}
54
55	// TODO: Ensure graphID has correct parent
56
57	m = &mountedLayer{
58		name:       name,
59		parent:     p,
60		mountID:    graphID,
61		layerStore: ls,
62		references: map[RWLayer]*referencedRWLayer{},
63	}
64
65	// Check for existing init layer
66	initID := fmt.Sprintf("%s-init", graphID)
67	if ls.driver.Exists(initID) {
68		m.initID = initID
69	}
70
71	return ls.saveMount(m)
72}
73
74func (ls *layerStore) ChecksumForGraphID(id, parent, oldTarDataPath, newTarDataPath string) (diffID DiffID, size int64, err error) {
75	defer func() {
76		if err != nil {
77			logrus.Debugf("could not get checksum for %q with tar-split: %q", id, err)
78			diffID, size, err = ls.checksumForGraphIDNoTarsplit(id, parent, newTarDataPath)
79		}
80	}()
81
82	if oldTarDataPath == "" {
83		err = errors.New("no tar-split file")
84		return
85	}
86
87	tarDataFile, err := os.Open(oldTarDataPath)
88	if err != nil {
89		return
90	}
91	defer tarDataFile.Close()
92	uncompressed, err := gzip.NewReader(tarDataFile)
93	if err != nil {
94		return
95	}
96
97	dgst := digest.Canonical.Digester()
98	err = ls.assembleTarTo(id, uncompressed, &size, dgst.Hash())
99	if err != nil {
100		return
101	}
102
103	diffID = DiffID(dgst.Digest())
104	err = os.RemoveAll(newTarDataPath)
105	if err != nil {
106		return
107	}
108	err = os.Link(oldTarDataPath, newTarDataPath)
109
110	return
111}
112
113func (ls *layerStore) checksumForGraphIDNoTarsplit(id, parent, newTarDataPath string) (diffID DiffID, size int64, err error) {
114	rawarchive, err := ls.driver.Diff(id, parent)
115	if err != nil {
116		return
117	}
118	defer rawarchive.Close()
119
120	f, err := os.Create(newTarDataPath)
121	if err != nil {
122		return
123	}
124	defer f.Close()
125	mfz := gzip.NewWriter(f)
126	defer mfz.Close()
127	metaPacker := storage.NewJSONPacker(mfz)
128
129	packerCounter := &packSizeCounter{metaPacker, &size}
130
131	archive, err := asm.NewInputTarStream(rawarchive, packerCounter, nil)
132	if err != nil {
133		return
134	}
135	dgst, err := digest.FromReader(archive)
136	if err != nil {
137		return
138	}
139	diffID = DiffID(dgst)
140	return
141}
142
143func (ls *layerStore) RegisterByGraphID(graphID string, parent ChainID, diffID DiffID, tarDataFile string, size int64) (Layer, error) {
144	// err is used to hold the error which will always trigger
145	// cleanup of creates sources but may not be an error returned
146	// to the caller (already exists).
147	var err error
148	var p *roLayer
149	if string(parent) != "" {
150		p = ls.get(parent)
151		if p == nil {
152			return nil, ErrLayerDoesNotExist
153		}
154
155		// Release parent chain if error
156		defer func() {
157			if err != nil {
158				ls.layerL.Lock()
159				ls.releaseLayer(p)
160				ls.layerL.Unlock()
161			}
162		}()
163	}
164
165	// Create new roLayer
166	layer := &roLayer{
167		parent:         p,
168		cacheID:        graphID,
169		referenceCount: 1,
170		layerStore:     ls,
171		references:     map[Layer]struct{}{},
172		diffID:         diffID,
173		size:           size,
174		chainID:        createChainIDFromParent(parent, diffID),
175	}
176
177	ls.layerL.Lock()
178	defer ls.layerL.Unlock()
179
180	if existingLayer := ls.getWithoutLock(layer.chainID); existingLayer != nil {
181		// Set error for cleanup, but do not return
182		err = errors.New("layer already exists")
183		return existingLayer.getReference(), nil
184	}
185
186	tx, err := ls.store.StartTransaction()
187	if err != nil {
188		return nil, err
189	}
190
191	defer func() {
192		if err != nil {
193			logrus.Debugf("Cleaning up transaction after failed migration for %s: %v", graphID, err)
194			if err := tx.Cancel(); err != nil {
195				logrus.Errorf("Error canceling metadata transaction %q: %s", tx.String(), err)
196			}
197		}
198	}()
199
200	tsw, err := tx.TarSplitWriter(false)
201	if err != nil {
202		return nil, err
203	}
204	defer tsw.Close()
205	tdf, err := os.Open(tarDataFile)
206	if err != nil {
207		return nil, err
208	}
209	defer tdf.Close()
210	_, err = io.Copy(tsw, tdf)
211	if err != nil {
212		return nil, err
213	}
214
215	if err = storeLayer(tx, layer); err != nil {
216		return nil, err
217	}
218
219	if err = tx.Commit(layer.chainID); err != nil {
220		return nil, err
221	}
222
223	ls.layerMap[layer.chainID] = layer
224
225	return layer.getReference(), nil
226}
227
228type unpackSizeCounter struct {
229	unpacker storage.Unpacker
230	size     *int64
231}
232
233func (u *unpackSizeCounter) Next() (*storage.Entry, error) {
234	e, err := u.unpacker.Next()
235	if err == nil && u.size != nil {
236		*u.size += e.Size
237	}
238	return e, err
239}
240
241type packSizeCounter struct {
242	packer storage.Packer
243	size   *int64
244}
245
246func (p *packSizeCounter) AddEntry(e storage.Entry) (int, error) {
247	n, err := p.packer.AddEntry(e)
248	if err == nil && p.size != nil {
249		*p.size += e.Size
250	}
251	return n, err
252}
253