5b3bdd3ada071ed312b355ddc6c570dc62b4fb36
[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
17 /*
18  * btrfs has a fixed stripewidth of 64k, so we need to write enough data to
19  * ensure that we hit both stripes by default.
20  */
21 #define DEFAULT_BUFSIZE (65 * 1024)
22
23 /* default number of fds to open */
24 #define DEFAULT_NUM_FDS 10
25
26 static void usage()
27 {
28         printf("Usage: fsync-err [ -b bufsize ] [ -n num_fds ] -d dmerror path <filename>\n");
29 }
30
31 int main(int argc, char **argv)
32 {
33         int *fd, ret, i, numfds = DEFAULT_NUM_FDS;
34         char *fname, *buf;
35         char *dmerror_path = NULL;
36         char *cmdbuf;
37         size_t cmdsize, bufsize = DEFAULT_BUFSIZE;
38
39         while ((i = getopt(argc, argv, "b:d:n:")) != -1) {
40                 switch (i) {
41                 case 'b':
42                         bufsize = strtol(optarg, &buf, 0);
43                         if (*buf != '\0') {
44                                 printf("bad string conversion: %s\n", optarg);
45                                 return 1;
46                         }
47                         break;
48                 case 'd':
49                         dmerror_path = optarg;
50                         break;
51                 case 'n':
52                         numfds = strtol(optarg, &buf, 0);
53                         if (*buf != '\0') {
54                                 printf("bad string conversion: %s\n", optarg);
55                                 return 1;
56                         }
57                         break;
58                 }
59         }
60
61         if (argc < 1) {
62                 usage();
63                 return 1;
64         }
65
66         if (!dmerror_path) {
67                 printf("Must specify dmerror path with -d option!\n");
68                 return 1;
69         }
70
71         /* Remaining argument is filename */
72         fname = argv[optind];
73
74         fd = calloc(numfds, sizeof(*fd));
75         if (!fd) {
76                 printf("malloc failed: %m\n");
77                 return 1;
78         }
79
80         for (i = 0; i < numfds; ++i) {
81                 fd[i] = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
82                 if (fd[i] < 0) {
83                         printf("open of fd[%d] failed: %m\n", i);
84                         return 1;
85                 }
86         }
87
88         buf = malloc(bufsize);
89         if (!buf) {
90                 printf("malloc failed: %m\n");
91                 return 1;
92         }
93
94         /* fill it with some junk */
95         memset(buf, 0x7c, bufsize);
96
97         for (i = 0; i < numfds; ++i) {
98                 ret = write(fd[i], buf, bufsize);
99                 if (ret < 0) {
100                         printf("First write on fd[%d] failed: %m\n", i);
101                         return 1;
102                 }
103         }
104
105         for (i = 0; i < numfds; ++i) {
106                 ret = fsync(fd[i]);
107                 if (ret < 0) {
108                         printf("First fsync on fd[%d] failed: %m\n", i);
109                         return 1;
110                 }
111         }
112
113         /* enough for path + dmerror command string  (and then some) */
114         cmdsize = strlen(dmerror_path) + 64;
115
116         cmdbuf = malloc(cmdsize);
117         if (!cmdbuf) {
118                 printf("malloc failed: %m\n");
119                 return 1;
120         }
121
122         ret = snprintf(cmdbuf, cmdsize, "%s load_error_table", dmerror_path);
123         if (ret < 0 || ret >= cmdsize) {
124                 printf("sprintf failure: %d\n", ret);
125                 return 1;
126         }
127
128         /* flip the device to non-working mode */
129         ret = system(cmdbuf);
130         if (ret) {
131                 if (WIFEXITED(ret))
132                         printf("system: program exited: %d\n",
133                                         WEXITSTATUS(ret));
134                 else
135                         printf("system: 0x%x\n", (int)ret);
136
137                 return 1;
138         }
139
140         for (i = 0; i < numfds; ++i) {
141                 ret = write(fd[i], buf, bufsize);
142                 if (ret < 0) {
143                         printf("Second write on fd[%d] failed: %m\n", i);
144                         return 1;
145                 }
146         }
147
148         for (i = 0; i < numfds; ++i) {
149                 ret = fsync(fd[i]);
150                 /* Now, we EXPECT the error! */
151                 if (ret >= 0) {
152                         printf("Success on second fsync on fd[%d]!\n", i);
153                         return 1;
154                 }
155         }
156
157         for (i = 0; i < numfds; ++i) {
158                 ret = fsync(fd[i]);
159                 if (ret < 0) {
160                         /*
161                          * We did a failed write and fsync on each fd before.
162                          * Now the error should be clear since we've not done
163                          * any writes since then.
164                          */
165                         printf("Third fsync on fd[%d] failed: %m\n", i);
166                         return 1;
167                 }
168         }
169
170         /* flip the device to working mode */
171         ret = snprintf(cmdbuf, cmdsize, "%s load_working_table", dmerror_path);
172         if (ret < 0 || ret >= cmdsize) {
173                 printf("sprintf failure: %d\n", ret);
174                 return 1;
175         }
176
177         ret = system(cmdbuf);
178         if (ret) {
179                 if (WIFEXITED(ret))
180                         printf("system: program exited: %d\n",
181                                         WEXITSTATUS(ret));
182                 else
183                         printf("system: 0x%x\n", (int)ret);
184
185                 return 1;
186         }
187
188         for (i = 0; i < numfds; ++i) {
189                 ret = fsync(fd[i]);
190                 if (ret < 0) {
191                         /* The error should still be clear */
192                         printf("fsync after healing device on fd[%d] failed: %m\n", i);
193                         return 1;
194                 }
195         }
196
197         /*
198          * reopen each file one at a time to ensure the same inode stays
199          * in core. fsync each one to make sure we see no errors on a fresh
200          * open of the inode.
201          */
202         for (i = 0; i < numfds; ++i) {
203                 ret = close(fd[i]);
204                 if (ret < 0) {
205                         printf("Close of fd[%d] returned unexpected error: %m\n", i);
206                         return 1;
207                 }
208                 fd[i] = open(fname, O_WRONLY | O_CREAT | O_TRUNC, 0644);
209                 if (fd[i] < 0) {
210                         printf("Second open of fd[%d] failed: %m\n", i);
211                         return 1;
212                 }
213                 ret = fsync(fd[i]);
214                 if (ret < 0) {
215                         /* New opens should not return an error */
216                         printf("First fsync after reopen of fd[%d] failed: %m\n", i);
217                         return 1;
218                 }
219         }
220
221         printf("Test passed!\n");
222         return 0;
223 }