summaryrefslogtreecommitdiff
path: root/cmd/ioworkload/scenario_mountfs.go
blob: 4c48ded3815904c043c66447259479bf4619d8e2 (plain)
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
package main

import (
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"syscall"
	"unsafe"

	"golang.org/x/sys/unix"
)

type mountIDReq struct {
	Size  uint32
	Pad   uint32
	MntID uint64
	Param uint64
}

func mountfsManagement() error {
	dir, cleanup, err := makeTempDir("mountfs-management")
	if err != nil {
		return err
	}
	defer cleanup()

	mountPoint := filepath.Join(dir, "mnt")
	if err := os.Mkdir(mountPoint, 0o755); err != nil {
		return fmt.Errorf("mkdir mountpoint: %w", err)
	}

	swapFile := filepath.Join(dir, "swapfile")
	if err := os.WriteFile(swapFile, []byte("swap"), 0o600); err != nil {
		return fmt.Errorf("write swap file: %w", err)
	}

	mountPath := mustCStringPtr(mountPoint)
	swapPath := mustCStringPtr(swapFile)
	newRoot := mustCStringPtr(mountPoint)
	putOld := mustCStringPtr(dir)
	tmpfs := mustCStringPtr("tmpfs")
	none := mustCStringPtr("none")
	atFDCWDInt := int64(unix.AT_FDCWD)
	atFDCWD := uintptr(atFDCWDInt)

	// Best-effort coverage: these calls are expected to fail on most hosts
	// without CAP_SYS_ADMIN, but still exercise syscall tracing paths.
	_, _, _ = syscall.RawSyscall6(unix.SYS_MOUNT, uintptr(unsafe.Pointer(none)), uintptr(unsafe.Pointer(mountPath)), uintptr(unsafe.Pointer(tmpfs)), 0, 0, 0)
	_, _, _ = syscall.RawSyscall(unix.SYS_UMOUNT2, uintptr(unsafe.Pointer(mountPath)), 0, 0)
	_, _, _ = syscall.RawSyscall(unix.SYS_UMOUNT2, uintptr(unsafe.Pointer(mountPath)), uintptr(unix.MNT_DETACH), 0)
	_, _, _ = syscall.RawSyscall6(unix.SYS_MOVE_MOUNT, atFDCWD, uintptr(unsafe.Pointer(mountPath)), atFDCWD, uintptr(unsafe.Pointer(mountPath)), 0, 0)
	_, _, _ = syscall.RawSyscall(unix.SYS_FSMOUNT, ^uintptr(0), 0, 0)
	_, _, _ = syscall.RawSyscall(unix.SYS_PIVOT_ROOT, uintptr(unsafe.Pointer(newRoot)), uintptr(unsafe.Pointer(putOld)), 0)
	_, _, _ = syscall.RawSyscall6(unix.SYS_QUOTACTL, 0, uintptr(unsafe.Pointer(mountPath)), 0, 0, 0, 0)
	_, _, _ = syscall.RawSyscall(unix.SYS_SWAPON, uintptr(unsafe.Pointer(swapPath)), 0, 0)
	_, _, _ = syscall.RawSyscall(unix.SYS_SWAPOFF, uintptr(unsafe.Pointer(swapPath)), 0, 0)

	req := mountIDReq{Size: uint32(unsafe.Sizeof(mountIDReq{}))}
	var statBuf [256]byte
	_, _, _ = syscall.RawSyscall6(unix.SYS_STATMOUNT, uintptr(unsafe.Pointer(&req)), uintptr(unsafe.Pointer(&statBuf[0])), uintptr(len(statBuf)), 0, 0, 0)

	var mountIDs [8]uint64
	_, _, _ = syscall.RawSyscall6(unix.SYS_LISTMOUNT, uintptr(unsafe.Pointer(&req)), uintptr(unsafe.Pointer(&mountIDs[0])), uintptr(len(mountIDs)), 0, 0, 0)

	if nr, err := listnsSyscallNr(); err == nil {
		var nsIDs [8]uint64
		_, _, _ = syscall.RawSyscall6(nr, uintptr(unsafe.Pointer(&req)), uintptr(unsafe.Pointer(&nsIDs[0])), uintptr(len(nsIDs)), 0, 0, 0)
	}

	return nil
}

func listnsSyscallNr() (uintptr, error) {
	return listnsSyscallNrForArch(runtime.GOARCH)
}

func listnsSyscallNrForArch(arch string) (uintptr, error) {
	// __NR_listns was introduced from asm-generic numbering where amd64/arm64 use 470.
	switch arch {
	case "amd64", "arm64":
		return 470, nil
	default:
		return 0, fmt.Errorf("listns syscall number not defined for GOARCH=%s", arch)
	}
}

func mustCStringPtr(s string) *byte {
	p, _ := unix.BytePtrFromString(s)
	return p
}