fstests: drop check.log and check.time into section specific results dir
[xfstests-dev.git] / src / fsync-err.c
1 /*
2  * fsync-err.c: test whether writeback errors are reported to all open fds
3  *              and properly cleared as expected after being seen once on each
4  *
5  * Copyright (c) 2017: Jeff Layton <jlayton@redhat.com>
6  */
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include <getopt.h>
16 #include <stdbool.h>
17
18 /*
19  * btrfs has a fixed stripewidth of 64k, so we need to write enough data to
20  * ensure that we hit both stripes by default.
21  */
22 #define DEFAULT_BUFSIZE (65 * 1024)
23
24 /* default number of fds to open */
25 #define DEFAULT_NUM_FDS 10
26
27 static void usage()
28 {
29         printf("Usage: fsync-err [ -b bufsize ] [ -n num_fds ] [ -s ] -d dmerror path <filename>\n");
30 }
31
32 int main(int argc, char **argv)
33 {
34         int *fd, ret, i, numfds = DEFAULT_NUM_FDS;
35         char *fname, *buf;
36         char *dmerror_path = NULL;
37         char *cmdbuf;
38         size_t cmdsize, bufsize = DEFAULT_BUFSIZE;
39         bool simple_mode = false;
40
41         while ((i = getopt(argc, argv, "b:d:n:s")) != -1) {
42                 switch (i) {
43                 case 'b':
44                         bufsize = strtol(optarg, &buf, 0);
45                         if (*buf != '\0') {
46                                 printf("bad string conversion: %s\n", optarg);
47                                 return 1;
48                         }
49                         break;
50                 case 'd':
51                         dmerror_path = optarg;
52                         break;
53                 case 'n':
54                         numfds = strtol(optarg, &buf, 0);
55                         if (*buf != '\0') {
56                                 printf("bad string conversion: %s\n", optarg);
57                                 return 1;
58                         }
59                         break;
60                 case 's':
61                         /*
62                          * Many filesystems will continue to throw errors after
63                          * fsync has already advanced to the current error,
64                          * due to metadata writeback failures or other
65                          * issues. Allow those fs' to opt out of more thorough
66                          * testing.
67                          */
68                         simple_mode = true;
69                 }
70         }
71
72         if (argc < 1) {
73                 usage();
74                 return 1;
75         }
76
77         if (!dmerror_path) {
78                 printf("Must specify dmerror path with -d option!\n");
79                 return 1;
80         }
81
82         /* Remaining argument is filename */
83         fname = argv[optind];
84
85         fd = calloc(numfds, sizeof(*fd));
86         if (!fd) {
87                 printf("malloc failed: %m\n");
88                 return 1;
89         }
90
91         for (i = 0; i < numfds; ++i) {
92                 fd[i] = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
93                 if (fd[i] < 0) {
94                         printf("open of fd[%d] failed: %m\n", i);
95                         return 1;
96                 }
97         }
98
99         buf = malloc(bufsize);
100         if (!buf) {
101                 printf("malloc failed: %m\n");
102                 return 1;
103         }
104
105         /* fill it with some junk */
106         memset(buf, 0x7c, bufsize);
107
108         for (i = 0; i < numfds; ++i) {
109                 ret = pwrite(fd[i], buf, bufsize, i * bufsize);
110                 if (ret < 0) {
111                         printf("First write on fd[%d] failed: %m\n", i);
112                         return 1;
113                 }
114         }
115
116         for (i = 0; i < numfds; ++i) {
117                 ret = fsync(fd[i]);
118                 if (ret < 0) {
119                         printf("First fsync on fd[%d] failed: %m\n", i);
120                         return 1;
121                 }
122         }
123
124         /* enough for path + dmerror command string  (and then some) */
125         cmdsize = strlen(dmerror_path) + 64;
126
127         cmdbuf = malloc(cmdsize);
128         if (!cmdbuf) {
129                 printf("malloc failed: %m\n");
130                 return 1;
131         }
132
133         ret = snprintf(cmdbuf, cmdsize, "%s load_error_table", dmerror_path);
134         if (ret < 0 || ret >= cmdsize) {
135                 printf("sprintf failure: %d\n", ret);
136                 return 1;
137         }
138
139         /* flip the device to non-working mode */
140         ret = system(cmdbuf);
141         if (ret) {
142                 if (WIFEXITED(ret))
143                         printf("system: program exited: %d\n",
144                                         WEXITSTATUS(ret));
145                 else
146                         printf("system: 0x%x\n", (int)ret);
147
148                 return 1;
149         }
150
151         for (i = 0; i < numfds; ++i) {
152                 ret = pwrite(fd[i], buf, bufsize, i * bufsize);
153                 if (ret < 0) {
154                         printf("Second write on fd[%d] failed: %m\n", i);
155                         return 1;
156                 }
157         }
158
159         for (i = 0; i < numfds; ++i) {
160                 ret = fsync(fd[i]);
161                 /* Now, we EXPECT the error! */
162                 if (ret >= 0) {
163                         printf("Success on second fsync on fd[%d]!\n", i);
164                         return 1;
165                 }
166         }
167
168         if (!simple_mode) {
169                 for (i = 0; i < numfds; ++i) {
170                         ret = fsync(fd[i]);
171                         if (ret < 0) {
172                                 /*
173                                  * We did a failed write and fsync on each fd
174                                  * before.  Now the error should be clear since
175                                  * we've not done any writes since then.
176                                  */
177                                 printf("Third fsync on fd[%d] failed: %m\n", i);
178                                 return 1;
179                         }
180                 }
181         }
182
183         /* flip the device to working mode */
184         ret = snprintf(cmdbuf, cmdsize, "%s load_working_table", dmerror_path);
185         if (ret < 0 || ret >= cmdsize) {
186                 printf("sprintf failure: %d\n", ret);
187                 return 1;
188         }
189
190         ret = system(cmdbuf);
191         if (ret) {
192                 if (WIFEXITED(ret))
193                         printf("system: program exited: %d\n",
194                                         WEXITSTATUS(ret));
195                 else
196                         printf("system: 0x%x\n", (int)ret);
197
198                 return 1;
199         }
200
201         if (!simple_mode) {
202                 for (i = 0; i < numfds; ++i) {
203                         ret = fsync(fd[i]);
204                         if (ret < 0) {
205                                 /* The error should still be clear */
206                                 printf("fsync after healing device on fd[%d] failed: %m\n", i);
207                                 return 1;
208                         }
209                 }
210         }
211
212         /*
213          * reopen each file one at a time to ensure the same inode stays
214          * in core. fsync each one to make sure we see no errors on a fresh
215          * open of the inode.
216          */
217         for (i = 0; i < numfds; ++i) {
218                 ret = close(fd[i]);
219                 if (ret < 0) {
220                         printf("Close of fd[%d] returned unexpected error: %m\n", i);
221                         return 1;
222                 }
223                 fd[i] = open(fname, O_WRONLY, 0644);
224                 if (fd[i] < 0) {
225                         printf("Second open of fd[%d] failed: %m\n", i);
226                         return 1;
227                 }
228                 ret = fsync(fd[i]);
229                 if (ret < 0) {
230                         /* New opens should not return an error */
231                         printf("First fsync after reopen of fd[%d] failed: %m\n", i);
232                         return 1;
233                 }
234         }
235
236         printf("Test passed!\n");
237         return 0;
238 }