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)
}
|