xfs/{019, 031}: make sure we don't set rtinherit=1 on mkfs
[xfstests-dev.git] / src / t_open_tmpfiles.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2019 Oracle.  All Rights Reserved.
4  * Author: Darrick J. Wong <darrick.wong@oracle.com>
5  *
6  * Test program to open unlinked files and leak them.
7  */
8 #ifndef _GNU_SOURCE
9 # define _GNU_SOURCE
10 #endif
11 #include <time.h>
12 #include <unistd.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <string.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/ioctl.h>
21 #include "global.h"
22
23 static int min_fd = -1;
24 static int max_fd = -1;
25 static unsigned int nr_opened = 0;
26 static float start_time;
27 static int shutdown_fd = -1;
28
29 void clock_time(float *time)
30 {
31         static clockid_t clkid = CLOCK_MONOTONIC;
32         struct timespec ts;
33         int ret;
34
35 retry:
36         ret = clock_gettime(clkid, &ts);
37         if (ret) {
38                 if (clkid == CLOCK_MONOTONIC) {
39                         clkid = CLOCK_REALTIME;
40                         goto retry;
41                 }
42                 perror("clock_gettime");
43                 exit(2);
44         }
45         *time = ts.tv_sec + ((float)ts.tv_nsec / 1000000000);
46 }
47
48 /*
49  * Exit the program due to an error.
50  *
51  * If we've exhausted all the file descriptors, make sure we close all the
52  * open fds in the order we received them in order to exploit a quirk of ext4
53  * and xfs where the oldest unlinked inodes are at the /end/ of the unlinked
54  * lists, which will make removing the unlinked files maximally painful.
55  *
56  * If it's some other error, just die and let the kernel sort it out.
57  */
58 void die(void)
59 {
60         float end_time;
61         int fd;
62
63         switch (errno) {
64         case EMFILE:
65         case ENFILE:
66         case ENOSPC:
67                 clock_time(&end_time);
68                 printf("Opened %u files in %.2fs.\n", nr_opened,
69                                 end_time - start_time);
70                 fflush(stdout);
71
72                 if (shutdown_fd >= 0) {
73                         /*
74                          * Flush the log so that we have to process the
75                          * unlinked inodes the next time we mount.
76                          */
77                         int flag = XFS_FSOP_GOING_FLAGS_LOGFLUSH;
78                         int ret;
79
80                         ret = ioctl(shutdown_fd, XFS_IOC_GOINGDOWN, &flag);
81                         if (ret) {
82                                 perror("shutdown");
83                                 exit(2);
84                         }
85                         exit(0);
86                 }
87
88                 clock_time(&start_time);
89                 for (fd = min_fd; fd <= max_fd; fd++)
90                         close(fd);
91                 clock_time(&end_time);
92                 printf("Closed %u files in %.2fs.\n", nr_opened,
93                                 end_time - start_time);
94                 exit(0);
95                 break;
96         default:
97                 perror("open?");
98                 exit(2);
99                 break;
100         }
101 }
102
103 /* Remember how many file we open and all that. */
104 void remember_fd(int fd)
105 {
106         if (min_fd == -1 || min_fd > fd)
107                 min_fd = fd;
108         if (max_fd == -1 || max_fd < fd)
109                 max_fd = fd;
110         nr_opened++;
111 }
112
113 /* Put an opened file on the unlinked list and leak the fd. */
114 void leak_tmpfile(void)
115 {
116         int fd = -1;
117         int ret;
118 #ifdef O_TMPFILE
119         static int try_o_tmpfile = 1;
120 #endif
121
122         /* Try to create an O_TMPFILE and leak the fd. */
123 #ifdef O_TMPFILE
124         if (try_o_tmpfile) {
125                 fd = open(".", O_TMPFILE | O_RDWR, 0644);
126                 if (fd >= 0) {
127                         remember_fd(fd);
128                         return;
129                 }
130                 if (fd < 0) {
131                         if (errno == EOPNOTSUPP)
132                                 try_o_tmpfile = 0;
133                         else
134                                 die();
135                 }
136         }
137 #endif
138
139         /* Oh well, create a new file, unlink it, and leak the fd. */
140         fd = open("./moo", O_CREAT | O_RDWR, 0644);
141         if (fd < 0)
142                 die();
143         ret = unlink("./moo");
144         if (ret)
145                 die();
146         remember_fd(fd);
147 }
148
149 /*
150  * Try to put as many files on the unlinked list and then kill them.
151  * The first argument is a directory to chdir into; the second argumennt (if
152  * provided) is a file path that will be opened and then used to shut down the
153  * fs before the program exits.
154  */
155 int main(int argc, char *argv[])
156 {
157         int ret;
158
159         if (argc > 1) {
160                 ret = chdir(argv[1]);
161                 if (ret)
162                         perror(argv[1]);
163         }
164         if (argc > 2) {
165                 shutdown_fd = open(argv[2], O_RDONLY);
166                 if (shutdown_fd < 0) {
167                         perror(argv[2]);
168                         return 1;
169                 }
170         }
171
172         clock_time(&start_time);
173         while (1)
174                 leak_tmpfile();
175         return 0;
176 }