Browse code

Add refcounts to graphdrivers that use fsdiff

This makes sure fsdiff doesn't try to unmount things that shouldn't be.

**Note**: This is intended as a temporary solution to have as minor a
change as possible for 1.11.1. A bigger change will be required in order
to support container re-attach.

Signed-off-by: Brian Goff <cpuguy83@gmail.com>
(cherry picked from commit 7342060b070df67481f8da4f394a57cac1671d56)

Brian Goff authored on 2016/04/19 08:41:29
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,32 @@
0
+package graphdriver
1
+
2
+import "sync"
3
+
4
+// RefCounter is a generic counter for use by graphdriver Get/Put calls
5
+type RefCounter struct {
6
+	counts map[string]int
7
+	mu     sync.Mutex
8
+}
9
+
10
+// NewRefCounter returns a new RefCounter
11
+func NewRefCounter() *RefCounter {
12
+	return &RefCounter{counts: make(map[string]int)}
13
+}
14
+
15
+// Increment increaes the ref count for the given id and returns the current count
16
+func (c *RefCounter) Increment(id string) int {
17
+	c.mu.Lock()
18
+	c.counts[id]++
19
+	count := c.counts[id]
20
+	c.mu.Unlock()
21
+	return count
22
+}
23
+
24
+// Decrement decreases the ref count for the given id and returns the current count
25
+func (c *RefCounter) Decrement(id string) int {
26
+	c.mu.Lock()
27
+	c.counts[id]--
28
+	count := c.counts[id]
29
+	c.mu.Unlock()
30
+	return count
31
+}
... ...
@@ -28,6 +28,7 @@ type Driver struct {
28 28
 	home    string
29 29
 	uidMaps []idtools.IDMap
30 30
 	gidMaps []idtools.IDMap
31
+	ctr     *graphdriver.RefCounter
31 32
 }
32 33
 
33 34
 // Init creates a driver with the given home and the set of options.
... ...
@@ -46,6 +47,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
46 46
 		home:      home,
47 47
 		uidMaps:   uidMaps,
48 48
 		gidMaps:   gidMaps,
49
+		ctr:       graphdriver.NewRefCounter(),
49 50
 	}
50 51
 
51 52
 	return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
... ...
@@ -151,26 +153,35 @@ func (d *Driver) Remove(id string) error {
151 151
 // Get mounts a device with given id into the root filesystem
152 152
 func (d *Driver) Get(id, mountLabel string) (string, error) {
153 153
 	mp := path.Join(d.home, "mnt", id)
154
+	if count := d.ctr.Increment(id); count > 1 {
155
+		return mp, nil
156
+	}
154 157
 
155 158
 	uid, gid, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
156 159
 	if err != nil {
160
+		d.ctr.Decrement(id)
157 161
 		return "", err
158 162
 	}
163
+
159 164
 	// Create the target directories if they don't exist
160 165
 	if err := idtools.MkdirAllAs(path.Join(d.home, "mnt"), 0755, uid, gid); err != nil && !os.IsExist(err) {
166
+		d.ctr.Decrement(id)
161 167
 		return "", err
162 168
 	}
163 169
 	if err := idtools.MkdirAs(mp, 0755, uid, gid); err != nil && !os.IsExist(err) {
170
+		d.ctr.Decrement(id)
164 171
 		return "", err
165 172
 	}
166 173
 
167 174
 	// Mount the device
168 175
 	if err := d.DeviceSet.MountDevice(id, mp, mountLabel); err != nil {
176
+		d.ctr.Decrement(id)
169 177
 		return "", err
170 178
 	}
171 179
 
172 180
 	rootFs := path.Join(mp, "rootfs")
173 181
 	if err := idtools.MkdirAllAs(rootFs, 0755, uid, gid); err != nil && !os.IsExist(err) {
182
+		d.ctr.Decrement(id)
174 183
 		d.DeviceSet.UnmountDevice(id, mp)
175 184
 		return "", err
176 185
 	}
... ...
@@ -180,6 +191,7 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
180 180
 		// Create an "id" file with the container/image id in it to help reconstruct this in case
181 181
 		// of later problems
182 182
 		if err := ioutil.WriteFile(idFile, []byte(id), 0600); err != nil {
183
+			d.ctr.Decrement(id)
183 184
 			d.DeviceSet.UnmountDevice(id, mp)
184 185
 			return "", err
185 186
 		}
... ...
@@ -190,6 +202,9 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
190 190
 
191 191
 // Put unmounts a device and removes it.
192 192
 func (d *Driver) Put(id string) error {
193
+	if count := d.ctr.Decrement(id); count > 0 {
194
+		return nil
195
+	}
193 196
 	mp := path.Join(d.home, "mnt", id)
194 197
 	err := d.DeviceSet.UnmountDevice(id, mp)
195 198
 	if err != nil {
... ...
@@ -95,6 +95,7 @@ type Driver struct {
95 95
 	pathCache     map[string]string
96 96
 	uidMaps       []idtools.IDMap
97 97
 	gidMaps       []idtools.IDMap
98
+	ctr           *graphdriver.RefCounter
98 99
 }
99 100
 
100 101
 var backingFs = "<unknown>"
... ...
@@ -147,6 +148,7 @@ func Init(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (grap
147 147
 		pathCache: make(map[string]string),
148 148
 		uidMaps:   uidMaps,
149 149
 		gidMaps:   gidMaps,
150
+		ctr:       graphdriver.NewRefCounter(),
150 151
 	}
151 152
 
152 153
 	return NaiveDiffDriverWithApply(d, uidMaps, gidMaps), nil
... ...
@@ -348,28 +350,39 @@ func (d *Driver) Get(id string, mountLabel string) (string, error) {
348 348
 	workDir := path.Join(dir, "work")
349 349
 	mergedDir := path.Join(dir, "merged")
350 350
 
351
+	if count := d.ctr.Increment(id); count > 1 {
352
+		return mergedDir, nil
353
+	}
354
+
351 355
 	opts := fmt.Sprintf("lowerdir=%s,upperdir=%s,workdir=%s", lowerDir, upperDir, workDir)
352 356
 
353 357
 	// if it's mounted already, just return
354 358
 	mounted, err := d.mounted(mergedDir)
355 359
 	if err != nil {
360
+		d.ctr.Decrement(id)
356 361
 		return "", err
357 362
 	}
358 363
 	if mounted {
364
+		d.ctr.Decrement(id)
359 365
 		return mergedDir, nil
360 366
 	}
361 367
 
362 368
 	if err := syscall.Mount("overlay", mergedDir, "overlay", 0, label.FormatMountLabel(opts, mountLabel)); err != nil {
369
+		d.ctr.Decrement(id)
363 370
 		return "", fmt.Errorf("error creating overlay mount to %s: %v", mergedDir, err)
364 371
 	}
365 372
 	// chown "workdir/work" to the remapped root UID/GID. Overlay fs inside a
366 373
 	// user namespace requires this to move a directory from lower to upper.
367 374
 	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
368 375
 	if err != nil {
376
+		d.ctr.Decrement(id)
377
+		syscall.Unmount(mergedDir, 0)
369 378
 		return "", err
370 379
 	}
371 380
 
372 381
 	if err := os.Chown(path.Join(workDir, "work"), rootUID, rootGID); err != nil {
382
+		d.ctr.Decrement(id)
383
+		syscall.Unmount(mergedDir, 0)
373 384
 		return "", err
374 385
 	}
375 386
 
... ...
@@ -386,6 +399,9 @@ func (d *Driver) mounted(dir string) (bool, error) {
386 386
 
387 387
 // Put unmounts the mount path created for the give id.
388 388
 func (d *Driver) Put(id string) error {
389
+	if count := d.ctr.Decrement(id); count > 0 {
390
+		return nil
391
+	}
389 392
 	d.pathCacheLock.Lock()
390 393
 	mountpoint, exists := d.pathCache[id]
391 394
 	d.pathCacheLock.Unlock()
... ...
@@ -105,6 +105,7 @@ func Init(base string, opt []string, uidMaps, gidMaps []idtools.IDMap) (graphdri
105 105
 		filesystemsCache: filesystemsCache,
106 106
 		uidMaps:          uidMaps,
107 107
 		gidMaps:          gidMaps,
108
+		ctr:              graphdriver.NewRefCounter(),
108 109
 	}
109 110
 	return graphdriver.NewNaiveDiffDriver(d, uidMaps, gidMaps), nil
110 111
 }
... ...
@@ -161,6 +162,7 @@ type Driver struct {
161 161
 	filesystemsCache map[string]bool
162 162
 	uidMaps          []idtools.IDMap
163 163
 	gidMaps          []idtools.IDMap
164
+	ctr              *graphdriver.RefCounter
164 165
 }
165 166
 
166 167
 func (d *Driver) String() string {
... ...
@@ -295,25 +297,35 @@ func (d *Driver) Remove(id string) error {
295 295
 // Get returns the mountpoint for the given id after creating the target directories if necessary.
296 296
 func (d *Driver) Get(id, mountLabel string) (string, error) {
297 297
 	mountpoint := d.mountPath(id)
298
+	if count := d.ctr.Increment(id); count > 1 {
299
+		return mountpoint, nil
300
+	}
301
+
298 302
 	filesystem := d.zfsPath(id)
299 303
 	options := label.FormatMountLabel("", mountLabel)
300 304
 	logrus.Debugf(`[zfs] mount("%s", "%s", "%s")`, filesystem, mountpoint, options)
301 305
 
302 306
 	rootUID, rootGID, err := idtools.GetRootUIDGID(d.uidMaps, d.gidMaps)
303 307
 	if err != nil {
308
+		d.ctr.Decrement(id)
304 309
 		return "", err
305 310
 	}
306 311
 	// Create the target directories if they don't exist
307 312
 	if err := idtools.MkdirAllAs(mountpoint, 0755, rootUID, rootGID); err != nil {
313
+		d.ctr.Decrement(id)
308 314
 		return "", err
309 315
 	}
310 316
 
311 317
 	if err := mount.Mount(filesystem, mountpoint, "zfs", options); err != nil {
318
+		d.ctr.Decrement(id)
312 319
 		return "", fmt.Errorf("error creating zfs mount of %s to %s: %v", filesystem, mountpoint, err)
313 320
 	}
321
+
314 322
 	// this could be our first mount after creation of the filesystem, and the root dir may still have root
315 323
 	// permissions instead of the remapped root uid:gid (if user namespaces are enabled):
316 324
 	if err := os.Chown(mountpoint, rootUID, rootGID); err != nil {
325
+		mount.Unmount(mountpoint)
326
+		d.ctr.Decrement(id)
317 327
 		return "", fmt.Errorf("error modifying zfs mountpoint (%s) directory ownership: %v", mountpoint, err)
318 328
 	}
319 329
 
... ...
@@ -322,6 +334,9 @@ func (d *Driver) Get(id, mountLabel string) (string, error) {
322 322
 
323 323
 // Put removes the existing mountpoint for the given id if it exists.
324 324
 func (d *Driver) Put(id string) error {
325
+	if count := d.ctr.Decrement(id); count > 0 {
326
+		return nil
327
+	}
325 328
 	mountpoint := d.mountPath(id)
326 329
 	mounted, err := graphdriver.Mounted(graphdriver.FsMagicZfs, mountpoint)
327 330
 	if err != nil || !mounted {