summaryrefslogtreecommitdiff
path: root/cmd/ioworkload/scenario_fcntl.go
blob: 0c97002e9b4d4889699de85af34cb491642d077f (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
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
package main

import (
	"fmt"
	"path/filepath"
	"syscall"
)

// fcntlDupfd uses fcntl F_DUPFD to duplicate a file descriptor.
func fcntlDupfd() error {
	dir, cleanup, err := makeTempDir("fcntl-dupfd")
	if err != nil {
		return err
	}
	defer cleanup()

	path := filepath.Join(dir, "fcntlfile.txt")
	fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644)
	if err != nil {
		return fmt.Errorf("open: %w", err)
	}
	defer syscall.Close(fd)

	newFd, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD, 0)
	if errno != 0 {
		return fmt.Errorf("fcntl F_DUPFD: %w", errno)
	}
	defer syscall.Close(int(newFd))

	if _, err := syscall.Write(int(newFd), []byte("via fcntl")); err != nil {
		return fmt.Errorf("write via fcntl dup: %w", err)
	}
	return nil
}

// fcntlSetfl uses fcntl F_GETFL/F_SETFL to read and modify file status flags.
func fcntlSetfl() error {
	dir, cleanup, err := makeTempDir("fcntl-setfl")
	if err != nil {
		return err
	}
	defer cleanup()

	path := filepath.Join(dir, "fcntlsetflfile.txt")
	fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644)
	if err != nil {
		return fmt.Errorf("open: %w", err)
	}
	defer syscall.Close(fd)

	flags, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_GETFL, 0)
	if errno != 0 {
		return fmt.Errorf("fcntl F_GETFL: %w", errno)
	}

	_, _, errno = syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_SETFL, flags|syscall.O_APPEND)
	if errno != 0 {
		return fmt.Errorf("fcntl F_SETFL: %w", errno)
	}

	if _, err := syscall.Write(fd, []byte("appended via fcntl setfl")); err != nil {
		return fmt.Errorf("write: %w", err)
	}
	return nil
}

// fcntlDupfdCloexec uses fcntl F_DUPFD_CLOEXEC to duplicate a file descriptor
// with the close-on-exec flag set.
func fcntlDupfdCloexec() error {
	dir, cleanup, err := makeTempDir("fcntl-dupfd-cloexec")
	if err != nil {
		return err
	}
	defer cleanup()

	path := filepath.Join(dir, "fcntlcloexecfile.txt")
	fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644)
	if err != nil {
		return fmt.Errorf("open: %w", err)
	}
	defer syscall.Close(fd)

	newFd, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD_CLOEXEC, 0)
	if errno != 0 {
		return fmt.Errorf("fcntl F_DUPFD_CLOEXEC: %w", errno)
	}
	defer syscall.Close(int(newFd))

	if _, err := syscall.Write(int(newFd), []byte("via fcntl dupfd cloexec")); err != nil {
		return fmt.Errorf("write via fcntl dup cloexec: %w", err)
	}
	return nil
}

// fcntlInvalidFd calls fcntl F_GETFL on an invalid fd (99999).
// The syscall fails with EBADF, but ior should capture the enter_fcntl
// tracepoint because it is recorded on syscall entry.
func fcntlInvalidFd() error {
	for i := 0; i < 5; i++ {
		_, _, errno := syscall.Syscall(syscall.SYS_FCNTL, 99999, syscall.F_GETFL, 0)
		if errno == 0 {
			return fmt.Errorf("expected fcntl on invalid fd to fail")
		}
	}
	return nil
}

// fcntlDupfdMax opens a file and calls fcntl F_DUPFD with a minfd value
// that exceeds the process RLIMIT_NOFILE. The kernel rejects this with
// EINVAL, but ior should capture the enter_fcntl tracepoint.
func fcntlDupfdMax() error {
	dir, cleanup, err := makeTempDir("fcntl-dupfd-max")
	if err != nil {
		return err
	}
	defer cleanup()

	path := filepath.Join(dir, "fcntldupfdmaxfile.txt")
	fd, err := syscall.Open(path, syscall.O_RDWR|syscall.O_CREAT, 0o644)
	if err != nil {
		return fmt.Errorf("open: %w", err)
	}
	defer syscall.Close(fd)

	// Retry the failing fcntl a few times to avoid a single one-shot call
	// racing early trace capture under parallel integration load.
	for i := 0; i < 5; i++ {
		_, _, errno := syscall.Syscall(syscall.SYS_FCNTL, uintptr(fd), syscall.F_DUPFD, 1<<30)
		if errno == 0 {
			return fmt.Errorf("expected fcntl F_DUPFD with extreme minfd to fail")
		}
	}
	return nil
}