File: overlay_linux.go

package info (click to toggle)
singularity-container 4.1.5%2Bds4-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 43,876 kB
  • sloc: asm: 14,840; sh: 3,190; ansic: 1,751; awk: 414; makefile: 413; python: 99
file content (137 lines) | stat: -rw-r--r-- 4,039 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright (c) 2019-2023, Sylabs Inc. All rights reserved.
// This software is licensed under a 3-clause BSD license. Please consult the
// LICENSE.md file distributed with the sources of this project regarding your
// rights to use or distribute this software.

package tools

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"syscall"

	"github.com/sylabs/singularity/v4/internal/pkg/util/fs/overlay"
	"github.com/sylabs/singularity/v4/pkg/image"
	"github.com/sylabs/singularity/v4/pkg/sylog"
)

// CreateOverlay creates a writable overlay using a directory inside the OCI
// bundle.
func CreateOverlay(ctx context.Context, bundlePath string) error {
	oldumask := syscall.Umask(0)
	defer syscall.Umask(oldumask)

	olDir := filepath.Join(bundlePath, "overlay")
	var err error
	if err = overlay.EnsureOverlayDir(olDir, true, 0o700); err != nil {
		return fmt.Errorf("failed to create %s: %s", olDir, err)
	}
	// delete overlay directory in case of error
	defer func() {
		if err != nil {
			sylog.Debugf("Encountered error in CreateOverlay; attempting to remove overlay dir %q", olDir)
			os.RemoveAll(olDir)
		}
	}()

	olSet := overlay.Set{WritableOverlay: &overlay.Item{
		SourcePath: olDir,
		Type:       image.SANDBOX,
		Readonly:   false,
	}}

	return olSet.Mount(ctx, RootFs(bundlePath).Path())
}

// DeleteOverlay deletes an overlay previously created using a directory inside
// the OCI bundle.
func DeleteOverlay(ctx context.Context, bundlePath string) error {
	olDir := filepath.Join(bundlePath, "overlay")
	rootFsDir := RootFs(bundlePath).Path()

	if err := overlay.DetachMount(ctx, rootFsDir); err != nil {
		return err
	}

	return overlay.DetachAndDelete(olDir)
}

// PrepareOverlayTmpfs creates a tmpfs, mounted to the overlay/ directory in the
// bundle, but does not perform an overlay mount using it.
func PrepareOverlayTmpfs(bundlePath string, sizeMiB int, allowSetuid bool) (string, error) {
	var err error

	oldumask := syscall.Umask(0)
	defer syscall.Umask(oldumask)

	olDir := filepath.Join(bundlePath, "overlay")
	err = overlay.EnsureOverlayDir(olDir, true, 0o700)
	if err != nil {
		return "", fmt.Errorf("failed to create %s: %s", olDir, err)
	}
	// delete overlay directory in case of error
	defer func() {
		if err != nil {
			sylog.Debugf("Encountered error in CreateOverlay; attempting to remove overlay dir %q", olDir)
			os.RemoveAll(olDir)
		}
	}()

	options := fmt.Sprintf("mode=1777,size=%dm", sizeMiB)

	var flags uintptr = syscall.MS_NODEV
	if !allowSetuid {
		flags |= syscall.MS_NOSUID
	}

	err = syscall.Mount("tmpfs", olDir, "tmpfs", flags, options)
	if err != nil {
		return "", fmt.Errorf("failed to bind %s: %s", olDir, err)
	}
	return olDir, nil
}

// CreateOverlay creates a writable overlay using tmpfs, mounting it over the bundle rootfs.
func CreateOverlayTmpfs(ctx context.Context, bundlePath string, sizeMiB int, allowSetuid bool) (string, error) {
	olDir, err := PrepareOverlayTmpfs(bundlePath, sizeMiB, allowSetuid)
	if err != nil {
		return "", err
	}

	oi := overlay.Item{
		SourcePath: olDir,
		Type:       image.SANDBOX,
		Readonly:   false,
	}
	oi.SetAllowSetuid(allowSetuid)
	olSet := overlay.Set{WritableOverlay: &oi}

	err = olSet.Mount(ctx, RootFs(bundlePath).Path())
	if err != nil {
		// best effort to cleanup tmpfs mount
		sylog.Debugf("Encountered error in CreateOverlayTmpfs; attempting to detach overlay dir %q", olDir)
		syscall.Unmount(olDir, syscall.MNT_DETACH)
		return "", err
	}

	return olDir, nil
}

// DeleteOverlayTmpfs unmounts and deletes a tmpfs backed overlay created with CreateOverlayTmpfs.
func DeleteOverlayTmpfs(ctx context.Context, bundlePath, olDir string) error {
	rootFsDir := RootFs(bundlePath).Path()

	if err := overlay.DetachMount(ctx, rootFsDir); err != nil {
		return err
	}

	// Because CreateOverlayTmpfs() mounts the tmpfs on olDir, and then
	// calls ApplyOverlay(), there needs to be an extra unmount in the this case
	if err := overlay.DetachMount(ctx, olDir); err != nil {
		return err
	}

	return overlay.DetachAndDelete(olDir)
}