0393c6bd837d35778434add6d60fa5331962b335
[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
28 void clock_time(float *time)
29 {
30         static clockid_t clkid = CLOCK_MONOTONIC;
31         struct timespec ts;
32         int ret;
33
34 retry:
35         ret = clock_gettime(clkid, &ts);
36         if (ret) {
37                 if (clkid == CLOCK_MONOTONIC) {
38                         clkid = CLOCK_REALTIME;
39                         goto retry;
40                 }
41                 perror("clock_gettime");
42                 exit(2);
43         }
44         *time = ts.tv_sec + ((float)ts.tv_nsec / 1000000000);
45 }
46
47 /*
48  * Exit the program due to an error.
49  *
50  * If we've exhausted all the file descriptors, make sure we close all the
51  * open fds in the order we received them in order to exploit a quirk of ext4
52  * and xfs where the oldest unlinked inodes are at the /end/ of the unlinked
53  * lists, which will make removing the unlinked files maximally painful.
54  *
55  * If it's some other error, just die and let the kernel sort it out.
56  */
57 void die(void)
58 {
59         float end_time;
60         int fd;
61
62         switch (errno) {
63         case EMFILE:
64         case ENFILE:
65         case ENOSPC:
66                 clock_time(&end_time);
67                 printf("Opened %u files in %.2fs.\n", nr_opened,
68                                 end_time - start_time);
69                 fflush(stdout);
70
71                 clock_time(&start_time);
72                 for (fd = min_fd; fd <= max_fd; fd++)
73                         close(fd);
74                 clock_time(&end_time);
75                 printf("Closed %u files in %.2fs.\n", nr_opened,
76                                 end_time - start_time);
77                 exit(0);
78                 break;
79         default:
80                 perror("open?");
81                 exit(2);
82                 break;
83         }
84 }
85
86 /* Remember how many file we open and all that. */
87 void remember_fd(int fd)
88 {
89         if (min_fd == -1 || min_fd > fd)
90                 min_fd = fd;
91         if (max_fd == -1 || max_fd < fd)
92                 max_fd = fd;
93         nr_opened++;
94 }
95
96 /* Put an opened file on the unlinked list and leak the fd. */
97 void leak_tmpfile(void)
98 {
99         int fd = -1;
100         int ret;
101 #ifdef O_TMPFILE
102         static int try_o_tmpfile = 1;
103 #endif
104
105         /* Try to create an O_TMPFILE and leak the fd. */
106 #ifdef O_TMPFILE
107         if (try_o_tmpfile) {
108                 fd = open(".", O_TMPFILE | O_RDWR, 0644);
109                 if (fd >= 0) {
110                         remember_fd(fd);
111                         return;
112                 }
113                 if (fd < 0) {
114                         if (errno == EOPNOTSUPP)
115                                 try_o_tmpfile = 0;
116                         else
117                                 die();
118                 }
119         }
120 #endif
121
122         /* Oh well, create a new file, unlink it, and leak the fd. */
123         fd = open("./moo", O_CREAT | O_RDWR, 0644);
124         if (fd < 0)
125                 die();
126         ret = unlink("./moo");
127         if (ret)
128                 die();
129         remember_fd(fd);
130 }
131
132 /*
133  * Try to put as many files on the unlinked list and then kill them.
134  * The first argument is a directory to chdir into; passing any second arg
135  * will shut down the fs instead of closing files.
136  */
137 int main(int argc, char *argv[])
138 {
139         int ret;
140
141         if (argc > 1) {
142                 ret = chdir(argv[1]);
143                 if (ret)
144                         perror(argv[1]);
145         }
146
147         clock_time(&start_time);
148         while (1)
149                 leak_tmpfile();
150         return 0;
151 }