Browse code

volumes/subpath: Plumb context

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>

Paweł Gronowski authored on 2023/07/27 21:56:28
Showing 19 changed files
... ...
@@ -514,14 +514,14 @@ func (container *Container) AddMountPointWithVolume(destination string, vol volu
514 514
 }
515 515
 
516 516
 // UnmountVolumes unmounts all volumes
517
-func (container *Container) UnmountVolumes(volumeEventLog func(name string, action events.Action, attributes map[string]string)) error {
517
+func (container *Container) UnmountVolumes(ctx context.Context, volumeEventLog func(name string, action events.Action, attributes map[string]string)) error {
518 518
 	var errs []string
519 519
 	for _, volumeMount := range container.MountPoints {
520 520
 		if volumeMount.Volume == nil {
521 521
 			continue
522 522
 		}
523 523
 
524
-		if err := volumeMount.Cleanup(); err != nil {
524
+		if err := volumeMount.Cleanup(ctx); err != nil {
525 525
 			errs = append(errs, err.Error())
526 526
 			continue
527 527
 		}
... ...
@@ -371,7 +371,7 @@ func (container *Container) DetachAndUnmount(volumeEventLog func(name string, ac
371 371
 				Warn("Unable to unmount")
372 372
 		}
373 373
 	}
374
-	return container.UnmountVolumes(volumeEventLog)
374
+	return container.UnmountVolumes(ctx, volumeEventLog)
375 375
 }
376 376
 
377 377
 // ignoreUnsupportedXAttrs ignores errors when extended attributes
... ...
@@ -1,6 +1,7 @@
1 1
 package container // import "github.com/docker/docker/container"
2 2
 
3 3
 import (
4
+	"context"
4 5
 	"fmt"
5 6
 	"os"
6 7
 	"path/filepath"
... ...
@@ -128,7 +129,7 @@ func (container *Container) ConfigMounts() []Mount {
128 128
 // On Windows it only delegates to `UnmountVolumes` since there is nothing to
129 129
 // force unmount.
130 130
 func (container *Container) DetachAndUnmount(volumeEventLog func(name string, action events.Action, attributes map[string]string)) error {
131
-	return container.UnmountVolumes(volumeEventLog)
131
+	return container.UnmountVolumes(context.TODO(), volumeEventLog)
132 132
 }
133 133
 
134 134
 // TmpfsMounts returns the list of tmpfs mounts
... ...
@@ -17,6 +17,7 @@ import (
17 17
 
18 18
 	"github.com/docker/docker/api/types"
19 19
 	"github.com/docker/docker/container"
20
+	"github.com/docker/docker/internal/compatcontext"
20 21
 	"github.com/docker/docker/internal/mounttree"
21 22
 	"github.com/docker/docker/internal/unshare"
22 23
 	"github.com/docker/docker/pkg/fileutils"
... ...
@@ -54,6 +55,8 @@ type containerFSView struct {
54 54
 
55 55
 // openContainerFS opens a new view of the container's filesystem.
56 56
 func (daemon *Daemon) openContainerFS(container *container.Container) (_ *containerFSView, err error) {
57
+	ctx := context.TODO()
58
+
57 59
 	if err := daemon.Mount(container); err != nil {
58 60
 		return nil, err
59 61
 	}
... ...
@@ -63,14 +66,15 @@ func (daemon *Daemon) openContainerFS(container *container.Container) (_ *contai
63 63
 		}
64 64
 	}()
65 65
 
66
-	mounts, cleanup, err := daemon.setupMounts(container)
66
+	mounts, cleanup, err := daemon.setupMounts(ctx, container)
67 67
 	if err != nil {
68 68
 		return nil, err
69 69
 	}
70 70
 	defer func() {
71
-		cleanup()
71
+		ctx := compatcontext.WithoutCancel(ctx)
72
+		cleanup(ctx)
72 73
 		if err != nil {
73
-			_ = container.UnmountVolumes(daemon.LogVolumeEvent)
74
+			_ = container.UnmountVolumes(ctx, daemon.LogVolumeEvent)
74 75
 		}
75 76
 	}()
76 77
 
... ...
@@ -208,7 +212,7 @@ func (vw *containerFSView) Close() error {
208 208
 	runtime.SetFinalizer(vw, nil)
209 209
 	close(vw.todo)
210 210
 	err := multierror.Append(nil, <-vw.done)
211
-	err = multierror.Append(err, vw.ctr.UnmountVolumes(vw.d.LogVolumeEvent))
211
+	err = multierror.Append(err, vw.ctr.UnmountVolumes(context.TODO(), vw.d.LogVolumeEvent))
212 212
 	err = multierror.Append(err, vw.d.Unmount(vw.ctr))
213 213
 	return err.ErrorOrNil()
214 214
 }
... ...
@@ -222,7 +222,7 @@ func (daemon *Daemon) create(ctx context.Context, daemonCfg *config.Config, opts
222 222
 		return nil, err
223 223
 	}
224 224
 
225
-	if err := daemon.createContainerOSSpecificSettings(ctr, opts.params.Config, opts.params.HostConfig); err != nil {
225
+	if err := daemon.createContainerOSSpecificSettings(ctx, ctr, opts.params.Config, opts.params.HostConfig); err != nil {
226 226
 		return nil, err
227 227
 	}
228 228
 
... ...
@@ -13,6 +13,7 @@ import (
13 13
 	mounttypes "github.com/docker/docker/api/types/mount"
14 14
 	"github.com/docker/docker/container"
15 15
 	"github.com/docker/docker/errdefs"
16
+	"github.com/docker/docker/internal/compatcontext"
16 17
 	"github.com/docker/docker/oci"
17 18
 	volumemounts "github.com/docker/docker/volume/mounts"
18 19
 	volumeopts "github.com/docker/docker/volume/service/opts"
... ...
@@ -21,7 +22,7 @@ import (
21 21
 )
22 22
 
23 23
 // createContainerOSSpecificSettings performs host-OS specific container create functionality
24
-func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error {
24
+func (daemon *Daemon) createContainerOSSpecificSettings(ctx context.Context, container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error {
25 25
 	if err := daemon.Mount(container); err != nil {
26 26
 		return err
27 27
 	}
... ...
@@ -48,7 +49,7 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con
48 48
 		// Skip volumes for which we already have something mounted on that
49 49
 		// destination because of a --volume-from.
50 50
 		if container.HasMountFor(destination) {
51
-			log.G(context.TODO()).WithField("container", container.ID).WithField("destination", spec).Debug("mountpoint already exists, skipping anonymous volume")
51
+			log.G(ctx).WithField("container", container.ID).WithField("destination", spec).Debug("mountpoint already exists, skipping anonymous volume")
52 52
 			// Not an error, this could easily have come from the image config.
53 53
 			continue
54 54
 		}
... ...
@@ -73,12 +74,12 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con
73 73
 
74 74
 		container.AddMountPointWithVolume(destination, &volumeWrapper{v: v, s: daemon.volumes}, true)
75 75
 	}
76
-	return daemon.populateVolumes(container)
76
+	return daemon.populateVolumes(ctx, container)
77 77
 }
78 78
 
79 79
 // populateVolumes copies data from the container's rootfs into the volume for non-binds.
80 80
 // this is only called when the container is created.
81
-func (daemon *Daemon) populateVolumes(c *container.Container) error {
81
+func (daemon *Daemon) populateVolumes(ctx context.Context, c *container.Container) error {
82 82
 	for _, mnt := range c.MountPoints {
83 83
 		if mnt.Volume == nil {
84 84
 			continue
... ...
@@ -88,14 +89,14 @@ func (daemon *Daemon) populateVolumes(c *container.Container) error {
88 88
 			continue
89 89
 		}
90 90
 
91
-		if err := daemon.populateVolume(c, mnt); err != nil {
91
+		if err := daemon.populateVolume(ctx, c, mnt); err != nil {
92 92
 			return err
93 93
 		}
94 94
 	}
95 95
 	return nil
96 96
 }
97 97
 
98
-func (daemon *Daemon) populateVolume(c *container.Container, mnt *volumemounts.MountPoint) error {
98
+func (daemon *Daemon) populateVolume(ctx context.Context, c *container.Container, mnt *volumemounts.MountPoint) error {
99 99
 	ctrDestPath, err := c.GetResourcePath(mnt.Destination)
100 100
 	if err != nil {
101 101
 		return err
... ...
@@ -108,18 +109,18 @@ func (daemon *Daemon) populateVolume(c *container.Container, mnt *volumemounts.M
108 108
 		return err
109 109
 	}
110 110
 
111
-	volumePath, cleanup, err := mnt.Setup(c.MountLabel, daemon.idMapping.RootPair(), nil)
111
+	volumePath, cleanup, err := mnt.Setup(ctx, c.MountLabel, daemon.idMapping.RootPair(), nil)
112 112
 	if err != nil {
113 113
 		if errdefs.IsNotFound(err) {
114 114
 			return nil
115 115
 		}
116
-		log.G(context.TODO()).WithError(err).Debugf("can't copy data from %s:%s, to %s", c.ID, mnt.Destination, volumePath)
116
+		log.G(ctx).WithError(err).Debugf("can't copy data from %s:%s, to %s", c.ID, mnt.Destination, volumePath)
117 117
 		return errors.Wrapf(err, "failed to populate volume")
118 118
 	}
119
-	defer mnt.Cleanup()
120
-	defer cleanup()
119
+	defer mnt.Cleanup(compatcontext.WithoutCancel(ctx))
120
+	defer cleanup(compatcontext.WithoutCancel(ctx))
121 121
 
122
-	log.G(context.TODO()).Debugf("copying image data from %s:%s, to %s", c.ID, mnt.Destination, volumePath)
122
+	log.G(ctx).Debugf("copying image data from %s:%s, to %s", c.ID, mnt.Destination, volumePath)
123 123
 	if err := c.CopyImagePathContent(volumePath, ctrDestPath); err != nil {
124 124
 		return err
125 125
 	}
... ...
@@ -11,7 +11,7 @@ import (
11 11
 )
12 12
 
13 13
 // createContainerOSSpecificSettings performs host-OS specific container create functionality
14
-func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error {
14
+func (daemon *Daemon) createContainerOSSpecificSettings(ctx context.Context, container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error {
15 15
 	if containertypes.Isolation.IsDefault(hostConfig.Isolation) {
16 16
 		// Make sure the host config has the default daemon isolation if not specified by caller.
17 17
 		hostConfig.Isolation = daemon.defaultIsolation
... ...
@@ -34,7 +34,7 @@ func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Con
34 34
 
35 35
 		// Create the volume in the volume driver. If it doesn't exist,
36 36
 		// a new one will be created.
37
-		v, err := daemon.volumes.Create(context.TODO(), "", volumeDriver, volumeopts.WithCreateReference(container.ID))
37
+		v, err := daemon.volumes.Create(ctx, "", volumeDriver, volumeopts.WithCreateReference(container.ID))
38 38
 		if err != nil {
39 39
 			return err
40 40
 		}
... ...
@@ -466,7 +466,7 @@ func (daemon *Daemon) restore(cfg *configStore) error {
466 466
 						ces.ExitCode = 255
467 467
 					}
468 468
 					c.SetStopped(&ces)
469
-					daemon.Cleanup(c)
469
+					daemon.Cleanup(context.TODO(), c)
470 470
 					if err := c.CheckpointTo(daemon.containersReplica); err != nil {
471 471
 						baseLogger.WithError(err).Error("failed to update stopped container state")
472 472
 					}
... ...
@@ -89,7 +89,7 @@ func (daemon *Daemon) handleContainerExit(c *container.Container, e *libcontaine
89 89
 		"exitCode":     strconv.Itoa(exitStatus.ExitCode),
90 90
 		"execDuration": strconv.Itoa(int(execDuration.Seconds())),
91 91
 	}
92
-	daemon.Cleanup(c)
92
+	daemon.Cleanup(context.TODO(), c)
93 93
 
94 94
 	if restart {
95 95
 		c.RestartCount++
... ...
@@ -139,7 +139,7 @@ func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *configStore
139 139
 			}
140 140
 			container.Reset(false)
141 141
 
142
-			daemon.Cleanup(container)
142
+			daemon.Cleanup(compatcontext.WithoutCancel(ctx), container)
143 143
 			// if containers AutoRemove flag is set, remove it after clean up
144 144
 			if container.HostConfig.AutoRemove {
145 145
 				container.Unlock()
... ...
@@ -164,12 +164,12 @@ func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *configStore
164 164
 		return err
165 165
 	}
166 166
 
167
-	m, cleanup, err := daemon.setupMounts(container)
167
+	m, cleanup, err := daemon.setupMounts(ctx, container)
168 168
 	if err != nil {
169 169
 		return err
170 170
 	}
171 171
 	mnts = append(mnts, m...)
172
-	defer cleanup()
172
+	defer cleanup(compatcontext.WithoutCancel(ctx))
173 173
 
174 174
 	spec, err := daemon.createSpec(ctx, daemonCfg, container, mnts)
175 175
 	if err != nil {
... ...
@@ -260,19 +260,19 @@ func (daemon *Daemon) containerStart(ctx context.Context, daemonCfg *configStore
260 260
 
261 261
 // Cleanup releases any network resources allocated to the container along with any rules
262 262
 // around how containers are linked together.  It also unmounts the container's root filesystem.
263
-func (daemon *Daemon) Cleanup(container *container.Container) {
263
+func (daemon *Daemon) Cleanup(ctx context.Context, container *container.Container) {
264 264
 	// Microsoft HCS containers get in a bad state if host resources are
265 265
 	// released while the container still exists.
266 266
 	if ctr, ok := container.C8dContainer(); ok {
267 267
 		if err := ctr.Delete(context.Background()); err != nil {
268
-			log.G(context.TODO()).Errorf("%s cleanup: failed to delete container from containerd: %v", container.ID, err)
268
+			log.G(ctx).Errorf("%s cleanup: failed to delete container from containerd: %v", container.ID, err)
269 269
 		}
270 270
 	}
271 271
 
272 272
 	daemon.releaseNetwork(container)
273 273
 
274 274
 	if err := container.UnmountIpcMount(); err != nil {
275
-		log.G(context.TODO()).Warnf("%s cleanup: failed to unmount IPC: %s", container.ID, err)
275
+		log.G(ctx).Warnf("%s cleanup: failed to unmount IPC: %s", container.ID, err)
276 276
 	}
277 277
 
278 278
 	if err := daemon.conditionalUnmountOnCleanup(container); err != nil {
... ...
@@ -284,11 +284,11 @@ func (daemon *Daemon) Cleanup(container *container.Container) {
284 284
 	}
285 285
 
286 286
 	if err := container.UnmountSecrets(); err != nil {
287
-		log.G(context.TODO()).Warnf("%s cleanup: failed to unmount secrets: %s", container.ID, err)
287
+		log.G(ctx).Warnf("%s cleanup: failed to unmount secrets: %s", container.ID, err)
288 288
 	}
289 289
 
290 290
 	if err := recursiveUnmount(container.Root); err != nil {
291
-		log.G(context.TODO()).WithError(err).WithField("container", container.ID).Warn("Error while cleaning up container resource mounts.")
291
+		log.G(ctx).WithError(err).WithField("container", container.ID).Warn("Error while cleaning up container resource mounts.")
292 292
 	}
293 293
 
294 294
 	for _, eConfig := range container.ExecCommands.Commands() {
... ...
@@ -296,8 +296,8 @@ func (daemon *Daemon) Cleanup(container *container.Container) {
296 296
 	}
297 297
 
298 298
 	if container.BaseFS != "" {
299
-		if err := container.UnmountVolumes(daemon.LogVolumeEvent); err != nil {
300
-			log.G(context.TODO()).Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err)
299
+		if err := container.UnmountVolumes(ctx, daemon.LogVolumeEvent); err != nil {
300
+			log.G(ctx).Warnf("%s cleanup: Failed to umount volumes: %v", container.ID, err)
301 301
 		}
302 302
 	}
303 303
 
... ...
@@ -15,6 +15,7 @@ import (
15 15
 	mounttypes "github.com/docker/docker/api/types/mount"
16 16
 	"github.com/docker/docker/container"
17 17
 	"github.com/docker/docker/internal/cleanups"
18
+	"github.com/docker/docker/internal/compatcontext"
18 19
 	volumemounts "github.com/docker/docker/volume/mounts"
19 20
 	"github.com/pkg/errors"
20 21
 )
... ...
@@ -25,7 +26,7 @@ import (
25 25
 //
26 26
 // The cleanup function should be called as soon as the container has been
27 27
 // started.
28
-func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, func() error, error) {
28
+func (daemon *Daemon) setupMounts(ctx context.Context, c *container.Container) ([]container.Mount, func(context.Context) error, error) {
29 29
 	var mounts []container.Mount
30 30
 	// TODO: tmpfs mounts should be part of Mountpoints
31 31
 	tmpfsMounts := make(map[string]bool)
... ...
@@ -39,8 +40,8 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, fu
39 39
 
40 40
 	cleanups := cleanups.Composite{}
41 41
 	defer func() {
42
-		if err := cleanups.Call(); err != nil {
43
-			log.G(context.TODO()).WithError(err).Warn("failed to cleanup temporary mounts created by MountPoint.Setup")
42
+		if err := cleanups.Call(compatcontext.WithoutCancel(ctx)); err != nil {
43
+			log.G(ctx).WithError(err).Warn("failed to cleanup temporary mounts created by MountPoint.Setup")
44 44
 		}
45 45
 	}()
46 46
 
... ...
@@ -62,7 +63,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, fu
62 62
 			return nil
63 63
 		}
64 64
 
65
-		path, clean, err := m.Setup(c.MountLabel, daemon.idMapping.RootPair(), checkfunc)
65
+		path, clean, err := m.Setup(ctx, c.MountLabel, daemon.idMapping.RootPair(), checkfunc)
66 66
 		if err != nil {
67 67
 			return nil, nil, err
68 68
 		}
... ...
@@ -8,6 +8,7 @@ import (
8 8
 	"github.com/docker/docker/api/types/mount"
9 9
 	"github.com/docker/docker/container"
10 10
 	"github.com/docker/docker/internal/cleanups"
11
+	"github.com/docker/docker/internal/compatcontext"
11 12
 	"github.com/docker/docker/pkg/idtools"
12 13
 	volumemounts "github.com/docker/docker/volume/mounts"
13 14
 )
... ...
@@ -23,11 +24,11 @@ import (
23 23
 // BUGBUG TODO Windows containerd. This would be much better if it returned
24 24
 // an array of runtime spec mounts, not container mounts. Then no need to
25 25
 // do multiple transitions.
26
-func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, func() error, error) {
26
+func (daemon *Daemon) setupMounts(ctx context.Context, c *container.Container) ([]container.Mount, func(context.Context) error, error) {
27 27
 	cleanups := cleanups.Composite{}
28 28
 	defer func() {
29
-		if err := cleanups.Call(); err != nil {
30
-			log.G(context.TODO()).WithError(err).Warn("failed to cleanup temporary mounts created by MountPoint.Setup")
29
+		if err := cleanups.Call(compatcontext.WithoutCancel(ctx)); err != nil {
30
+			log.G(ctx).WithError(err).Warn("failed to cleanup temporary mounts created by MountPoint.Setup")
31 31
 		}
32 32
 	}()
33 33
 
... ...
@@ -36,7 +37,7 @@ func (daemon *Daemon) setupMounts(c *container.Container) ([]container.Mount, fu
36 36
 		if err := daemon.lazyInitializeVolume(c.ID, mount); err != nil {
37 37
 			return nil, nil, err
38 38
 		}
39
-		s, c, err := mount.Setup(c.MountLabel, idtools.Identity{}, nil)
39
+		s, c, err := mount.Setup(ctx, c.MountLabel, idtools.Identity{}, nil)
40 40
 		if err != nil {
41 41
 			return nil, nil, err
42 42
 		}
... ...
@@ -1,22 +1,24 @@
1 1
 package cleanups
2 2
 
3 3
 import (
4
+	"context"
5
+
4 6
 	"github.com/docker/docker/internal/multierror"
5 7
 )
6 8
 
7 9
 type Composite struct {
8
-	cleanups []func() error
10
+	cleanups []func(context.Context) error
9 11
 }
10 12
 
11 13
 // Add adds a cleanup to be called.
12
-func (c *Composite) Add(f func() error) {
14
+func (c *Composite) Add(f func(context.Context) error) {
13 15
 	c.cleanups = append(c.cleanups, f)
14 16
 }
15 17
 
16 18
 // Call calls all cleanups in reverse order and returns an error combining all
17 19
 // non-nil errors.
18
-func (c *Composite) Call() error {
19
-	err := call(c.cleanups)
20
+func (c *Composite) Call(ctx context.Context) error {
21
+	err := call(ctx, c.cleanups)
20 22
 	c.cleanups = nil
21 23
 	return err
22 24
 }
... ...
@@ -24,19 +26,19 @@ func (c *Composite) Call() error {
24 24
 // Release removes all cleanups, turning Call into a no-op.
25 25
 // Caller still can call the cleanups by calling the returned function
26 26
 // which is equivalent to calling the Call before Release was called.
27
-func (c *Composite) Release() func() error {
27
+func (c *Composite) Release() func(context.Context) error {
28 28
 	cleanups := c.cleanups
29 29
 	c.cleanups = nil
30
-	return func() error {
31
-		return call(cleanups)
30
+	return func(ctx context.Context) error {
31
+		return call(ctx, cleanups)
32 32
 	}
33 33
 }
34 34
 
35
-func call(cleanups []func() error) error {
35
+func call(ctx context.Context, cleanups []func(context.Context) error) error {
36 36
 	var errs []error
37 37
 	for idx := len(cleanups) - 1; idx >= 0; idx-- {
38 38
 		c := cleanups[idx]
39
-		errs = append(errs, c())
39
+		errs = append(errs, c(ctx))
40 40
 	}
41 41
 	return multierror.Join(errs...)
42 42
 }
... ...
@@ -1,6 +1,7 @@
1 1
 package cleanups
2 2
 
3 3
 import (
4
+	"context"
4 5
 	"errors"
5 6
 	"fmt"
6 7
 	"testing"
... ...
@@ -18,20 +19,20 @@ func TestCall(t *testing.T) {
18 18
 	var errZ = errors.New("errorZ")
19 19
 	var errYZ = errors.Join(errY, errZ)
20 20
 
21
-	c.Add(func() error {
21
+	c.Add(func(ctx context.Context) error {
22 22
 		return err1
23 23
 	})
24
-	c.Add(func() error {
24
+	c.Add(func(ctx context.Context) error {
25 25
 		return nil
26 26
 	})
27
-	c.Add(func() error {
27
+	c.Add(func(ctx context.Context) error {
28 28
 		return fmt.Errorf("something happened: %w", err2)
29 29
 	})
30
-	c.Add(func() error {
30
+	c.Add(func(ctx context.Context) error {
31 31
 		return errors.Join(errX, fmt.Errorf("joined: %w", errYZ))
32 32
 	})
33 33
 
34
-	err := c.Call()
34
+	err := c.Call(context.Background())
35 35
 
36 36
 	errs := err.(interface{ Unwrap() []error }).Unwrap()
37 37
 
... ...
@@ -20,7 +20,7 @@ import (
20 20
 // After use, it is the caller's responsibility to call Close on the returned
21 21
 // SafePath object, which will unmount the temporary file/directory
22 22
 // and remove it.
23
-func Join(path, subpath string) (*SafePath, error) {
23
+func Join(_ context.Context, path, subpath string) (*SafePath, error) {
24 24
 	base, subpart, err := evaluatePath(path, subpath)
25 25
 	if err != nil {
26 26
 		return nil, err
... ...
@@ -126,20 +126,20 @@ func tempMountPoint(sourceFd int) (string, error) {
126 126
 
127 127
 // cleanupSafePaths returns a function that unmounts the path and removes the
128 128
 // mountpoint.
129
-func cleanupSafePath(path string) func() error {
130
-	return func() error {
131
-		log.G(context.TODO()).WithField("path", path).Debug("removing safe temp mount")
129
+func cleanupSafePath(path string) func(context.Context) error {
130
+	return func(ctx context.Context) error {
131
+		log.G(ctx).WithField("path", path).Debug("removing safe temp mount")
132 132
 
133 133
 		if err := unix_noeintr.Unmount(path, unix.MNT_DETACH); err != nil {
134 134
 			if errors.Is(err, unix.EINVAL) {
135
-				log.G(context.TODO()).WithField("path", path).Warn("safe temp mount no longer exists?")
135
+				log.G(ctx).WithField("path", path).Warn("safe temp mount no longer exists?")
136 136
 				return nil
137 137
 			}
138 138
 			return errors.Wrapf(err, "error unmounting safe mount %s", path)
139 139
 		}
140 140
 		if err := os.Remove(path); err != nil {
141 141
 			if errors.Is(err, os.ErrNotExist) {
142
-				log.G(context.TODO()).WithField("path", path).Warn("safe temp mount no longer exists?")
142
+				log.G(ctx).WithField("path", path).Warn("safe temp mount no longer exists?")
143 143
 				return nil
144 144
 			}
145 145
 			return errors.Wrapf(err, "failed to delete temporary safe mount")
... ...
@@ -1,6 +1,7 @@
1 1
 package safepath
2 2
 
3 3
 import (
4
+	"context"
4 5
 	"os"
5 6
 	"path/filepath"
6 7
 	"runtime"
... ...
@@ -40,9 +41,9 @@ func TestJoinEscapingSymlink(t *testing.T) {
40 40
 			err = os.Symlink(tc.target, filepath.Join(dir, "link"))
41 41
 			assert.NilError(t, err, "failed to create symlink to %s", tc.target)
42 42
 
43
-			safe, err := Join(dir, "link")
43
+			safe, err := Join(context.Background(), dir, "link")
44 44
 			if err == nil {
45
-				safe.Close()
45
+				safe.Close(context.Background())
46 46
 			}
47 47
 			assert.ErrorType(t, err, &ErrEscapesBase{})
48 48
 		})
... ...
@@ -70,10 +71,10 @@ func TestJoinGoodSymlink(t *testing.T) {
70 70
 		"subdir_link_relative", "foo_link_relative",
71 71
 	} {
72 72
 		t.Run(target, func(t *testing.T) {
73
-			safe, err := Join(dir, target)
73
+			safe, err := Join(context.Background(), dir, target)
74 74
 			assert.NilError(t, err)
75 75
 
76
-			defer safe.Close()
76
+			defer safe.Close(context.Background())
77 77
 			if strings.HasPrefix(target, "subdir") {
78 78
 				data, err := os.ReadFile(filepath.Join(safe.Path(), "hello.txt"))
79 79
 				assert.NilError(t, err)
... ...
@@ -97,10 +98,10 @@ func TestJoinWithSymlinkReplace(t *testing.T) {
97 97
 	err = os.Symlink(target, link)
98 98
 	assert.Check(t, err, "failed to create symlink to foo")
99 99
 
100
-	safe, err := Join(dir, "link")
100
+	safe, err := Join(context.Background(), dir, "link")
101 101
 	assert.NilError(t, err)
102 102
 
103
-	defer safe.Close()
103
+	defer safe.Close(context.Background())
104 104
 
105 105
 	// Delete the link target.
106 106
 	err = os.Remove(target)
... ...
@@ -133,12 +134,12 @@ func TestJoinCloseInvalidates(t *testing.T) {
133 133
 	err = os.WriteFile(foo, []byte("bar"), 0o744)
134 134
 	assert.NilError(t, err, "failed to create test file")
135 135
 
136
-	safe, err := Join(dir, "foo")
136
+	safe, err := Join(context.Background(), dir, "foo")
137 137
 	assert.NilError(t, err)
138 138
 
139 139
 	assert.Check(t, safe.IsValid())
140 140
 
141
-	assert.NilError(t, safe.Close())
141
+	assert.NilError(t, safe.Close(context.Background()))
142 142
 
143 143
 	assert.Check(t, !safe.IsValid())
144 144
 }
... ...
@@ -8,6 +8,7 @@ import (
8 8
 
9 9
 	"github.com/containerd/log"
10 10
 	"github.com/docker/docker/internal/cleanups"
11
+	"github.com/docker/docker/internal/compatcontext"
11 12
 	"github.com/pkg/errors"
12 13
 	"golang.org/x/sys/windows"
13 14
 )
... ...
@@ -19,7 +20,7 @@ import (
19 19
 // The path is safe (the path target won't change) until the returned SafePath
20 20
 // is Closed.
21 21
 // Caller is responsible for calling the Close function which unlocks the path.
22
-func Join(path, subpath string) (*SafePath, error) {
22
+func Join(ctx context.Context, path, subpath string) (*SafePath, error) {
23 23
 	base, subpart, err := evaluatePath(path, subpath)
24 24
 	if err != nil {
25 25
 		return nil, err
... ...
@@ -28,8 +29,8 @@ func Join(path, subpath string) (*SafePath, error) {
28 28
 
29 29
 	cleanups := cleanups.Composite{}
30 30
 	defer func() {
31
-		if cErr := cleanups.Call(); cErr != nil {
32
-			log.G(context.TODO()).WithError(cErr).Warn("failed to close handles after error")
31
+		if cErr := cleanups.Call(compatcontext.WithoutCancel(ctx)); cErr != nil {
32
+			log.G(ctx).WithError(cErr).Warn("failed to close handles after error")
33 33
 		}
34 34
 	}()
35 35
 
... ...
@@ -44,7 +45,7 @@ func Join(path, subpath string) (*SafePath, error) {
44 44
 			}
45 45
 			return nil, errors.Wrapf(err, "failed to lock file %s", fullPath)
46 46
 		}
47
-		cleanups.Add(func() error {
47
+		cleanups.Add(func(context.Context) error {
48 48
 			if err := windows.CloseHandle(handle); err != nil {
49 49
 				return &os.PathError{Op: "CloseHandle", Path: fullPath, Err: err}
50 50
 			}
... ...
@@ -10,7 +10,7 @@ import (
10 10
 
11 11
 type SafePath struct {
12 12
 	path    string
13
-	cleanup func() error
13
+	cleanup func(ctx context.Context) error
14 14
 	mutex   sync.Mutex
15 15
 
16 16
 	// Immutable fields
... ...
@@ -18,13 +18,13 @@ type SafePath struct {
18 18
 }
19 19
 
20 20
 // Close releases the resources used by the path.
21
-func (s *SafePath) Close() error {
21
+func (s *SafePath) Close(ctx context.Context) error {
22 22
 	s.mutex.Lock()
23 23
 	defer s.mutex.Unlock()
24 24
 
25 25
 	if s.path == "" {
26 26
 		base, sub := s.SourcePath()
27
-		log.G(context.TODO()).WithFields(log.Fields{
27
+		log.G(ctx).WithFields(log.Fields{
28 28
 			"path":          s.Path(),
29 29
 			"sourceBase":    base,
30 30
 			"sourceSubpath": sub,
... ...
@@ -34,7 +34,7 @@ func (s *SafePath) Close() error {
34 34
 
35 35
 	s.path = ""
36 36
 	if s.cleanup != nil {
37
-		return s.cleanup()
37
+		return s.cleanup(ctx)
38 38
 	}
39 39
 	return nil
40 40
 }
... ...
@@ -83,7 +83,7 @@ type MountPoint struct {
83 83
 
84 84
 // Cleanup frees resources used by the mountpoint and cleans up all the paths
85 85
 // returned by Setup that hasn't been cleaned up by the caller.
86
-func (m *MountPoint) Cleanup() error {
86
+func (m *MountPoint) Cleanup(ctx context.Context) error {
87 87
 	if m.Volume == nil || m.ID == "" {
88 88
 		return nil
89 89
 	}
... ...
@@ -93,9 +93,9 @@ func (m *MountPoint) Cleanup() error {
93 93
 			continue
94 94
 		}
95 95
 
96
-		err := p.Close()
96
+		err := p.Close(ctx)
97 97
 		base, sub := p.SourcePath()
98
-		log.G(context.TODO()).WithFields(log.Fields{
98
+		log.G(ctx).WithFields(log.Fields{
99 99
 			"error":         err,
100 100
 			"path":          p.Path(),
101 101
 			"sourceBase":    base,
... ...
@@ -126,7 +126,7 @@ func (m *MountPoint) Cleanup() error {
126 126
 // still points to the same target (to avoid TOCTOU attack).
127 127
 //
128 128
 // Cleanup function doesn't need to be called when error is returned.
129
-func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.Identity, checkFun func(m *MountPoint) error) (path string, cleanup func() error, retErr error) {
129
+func (m *MountPoint) Setup(ctx context.Context, mountLabel string, rootIDs idtools.Identity, checkFun func(m *MountPoint) error) (path string, cleanup func(context.Context) error, retErr error) {
130 130
 	if m.SkipMountpointCreation {
131 131
 		return m.Source, noCleanup, nil
132 132
 	}
... ...
@@ -140,8 +140,8 @@ func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.Identity, checkFun
140 140
 		if err != nil {
141 141
 			path = ""
142 142
 			retErr = errors.Wrapf(err, "error evaluating symlinks from mount source %q", m.Source)
143
-			if cleanupErr := cleanup(); cleanupErr != nil {
144
-				log.G(context.TODO()).WithError(cleanupErr).Warn("failed to cleanup after error")
143
+			if cleanupErr := cleanup(ctx); cleanupErr != nil {
144
+				log.G(ctx).WithError(cleanupErr).Warn("failed to cleanup after error")
145 145
 			}
146 146
 			cleanup = noCleanup
147 147
 			return
... ...
@@ -150,8 +150,8 @@ func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.Identity, checkFun
150 150
 		if err != nil && !errors.Is(err, syscall.ENOTSUP) {
151 151
 			path = ""
152 152
 			retErr = errors.Wrapf(err, "error setting label on mount source '%s'", sourcePath)
153
-			if cleanupErr := cleanup(); cleanupErr != nil {
154
-				log.G(context.TODO()).WithError(cleanupErr).Warn("failed to cleanup after error")
153
+			if cleanupErr := cleanup(ctx); cleanupErr != nil {
154
+				log.G(ctx).WithError(cleanupErr).Warn("failed to cleanup after error")
155 155
 			}
156 156
 			cleanup = noCleanup
157 157
 		}
... ...
@@ -172,15 +172,15 @@ func (m *MountPoint) Setup(mountLabel string, rootIDs idtools.Identity, checkFun
172 172
 		if m.Spec.VolumeOptions != nil && m.Spec.VolumeOptions.Subpath != "" {
173 173
 			subpath := m.Spec.VolumeOptions.Subpath
174 174
 
175
-			safePath, err := safepath.Join(volumePath, subpath)
175
+			safePath, err := safepath.Join(ctx, volumePath, subpath)
176 176
 			if err != nil {
177 177
 				if err := m.Volume.Unmount(id); err != nil {
178
-					log.G(context.TODO()).WithError(err).Error("failed to unmount after safepath.Join failed")
178
+					log.G(ctx).WithError(err).Error("failed to unmount after safepath.Join failed")
179 179
 				}
180 180
 				return "", noCleanup, err
181 181
 			}
182 182
 			m.safePaths = append(m.safePaths, safePath)
183
-			log.G(context.TODO()).Debugf("mounting (%s|%s) via %s", volumePath, subpath, safePath.Path())
183
+			log.G(ctx).Debugf("mounting (%s|%s) via %s", volumePath, subpath, safePath.Path())
184 184
 
185 185
 			clean = safePath.Close
186 186
 			volumePath = safePath.Path()
... ...
@@ -260,6 +260,6 @@ func errInvalidSpec(spec string) error {
260 260
 }
261 261
 
262 262
 // noCleanup is a no-op cleanup function.
263
-func noCleanup() error {
263
+func noCleanup(_ context.Context) error {
264 264
 	return nil
265 265
 }