Whamcloud - gitweb
LU-17110 llite: fix slab corruption with fm_extent_count=0
[fs/lustre-release.git] / lustre / tests / checkfiemap.c
1 /* GPL HEADER START
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License version 2 only,
7  * as published by the Free Software Foundation.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License version 2 for more details (a copy is included
13  * in the LICENSE file that accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License
16  * version 2 along with this program; If not, see http://www.gnu.org/licenses
17  *
18  * Please  visit http://www.xyratex.com/contact if you need additional
19  * information or have any questions.
20  *
21  * GPL HEADER END
22  */
23
24 /*
25  * Copyright 2013 Xyratex Technology Limited
26  *
27  * Author: Artem Blagodarenko <Artem_Blagodarenko@xyratex.com>
28  *
29  */
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <sys/ioctl.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <unistd.h>
40 #include <getopt.h>
41 #include <pthread.h>
42 #include <linux/types.h>
43 #include <linux/fs.h>
44 #include <linux/lustre/lustre_user.h>
45
46 #ifndef FS_IOC_FIEMAP
47 # define FS_IOC_FIEMAP (_IOWR('f', 11, struct fiemap))
48 #endif
49
50 #define ONEMB 1048576
51
52 static inline void print_extent_flags(unsigned int flags) {
53         if (!flags)
54                 return;
55
56         printf("flags (0x%x):", flags);
57         if (flags & FIEMAP_EXTENT_LAST)
58                 printf(" LAST");
59         if (flags & FIEMAP_EXTENT_UNKNOWN)
60                 printf(" UNKNOWN");
61         if (flags & FIEMAP_EXTENT_DELALLOC)
62                 printf(" DELALLOC");
63         if (flags & FIEMAP_EXTENT_ENCODED)
64                 printf(" ENCODED");
65         if (flags & FIEMAP_EXTENT_DATA_ENCRYPTED)
66                 printf(" DATA_ENCRYPTED");
67         if (flags & FIEMAP_EXTENT_NOT_ALIGNED)
68                 printf(" NOT_ALIGNED");
69         if (flags & FIEMAP_EXTENT_DATA_INLINE)
70                 printf(" DATA_INLINE");
71         if (flags & FIEMAP_EXTENT_DATA_TAIL)
72                 printf(" DATA_TAIL");
73         if (flags & FIEMAP_EXTENT_UNWRITTEN)
74                 printf(" UNWRITTEN");
75         if (flags & FIEMAP_EXTENT_MERGED)
76                 printf(" MERGED");
77         if (flags & FIEMAP_EXTENT_SHARED)
78                 printf(" SHARED");
79         if (flags & FIEMAP_EXTENT_NET)
80                 printf(" NET");
81         printf("\n");
82 }
83
84
85 /* This test executes fiemap ioctl and check
86  * a) there are no file ranges marked with FIEMAP_EXTENT_UNWRITTEN
87  * b) data ranges sizes sum is equal to given in second param */
88 static int check_fiemap(int fd, long long expected_sum,
89                         unsigned int *mapped_extents)
90 {
91         /* This buffer is enougth for 1MB length file */
92         union { struct fiemap f; char c[4096]; } fiemap_buf;
93         struct fiemap *fiemap = &fiemap_buf.f;
94         struct fiemap_extent *fm_extents = &fiemap->fm_extents[0];
95         unsigned int count = (sizeof(fiemap_buf) - sizeof(*fiemap)) /
96                         sizeof(*fm_extents);
97         unsigned int i = 0;
98         long long ext_len_sum = 0;
99
100         memset(&fiemap_buf, 0, sizeof(fiemap_buf));
101
102         fiemap->fm_start = 0;
103         fiemap->fm_flags = (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_DEVICE_ORDER);
104         fiemap->fm_extent_count = count;
105         fiemap->fm_length = FIEMAP_MAX_OFFSET;
106
107         if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0) {
108                 fprintf(stderr, "error while ioctl %i\n",  errno);
109                 return -1;
110         }
111
112         for (i = 0; i < fiemap->fm_mapped_extents; i++) {
113                 printf("extent %d in offset %lu, length %lu\n",
114                         i, (unsigned long)fm_extents[i].fe_logical,
115                         (unsigned long)fm_extents[i].fe_length);
116
117                 print_extent_flags(fm_extents[i].fe_flags);
118
119                 if (fm_extents[i].fe_flags & FIEMAP_EXTENT_UNWRITTEN) {
120                         fprintf(stderr, "Unwritten extent\n");
121                         return -2;
122                 } else {
123                         ext_len_sum += fm_extents[i].fe_length;
124                 }
125         }
126
127         printf("No unwritten extents, extents number %u, sum of lengths %lli, expected sum %lli\n",
128                 fiemap->fm_mapped_extents,
129                 ext_len_sum, expected_sum);
130
131         *mapped_extents = fiemap->fm_mapped_extents;
132         return ext_len_sum != expected_sum || (expected_sum && !*mapped_extents);
133 }
134
135 /**
136  * LU-17110
137  * When userspace uses fiemap with fm_extent_count=0, it means that kernelspace
138  * should return only the number of extents. So we should always check
139  * fm_extent_count before accessing to fm_extents array. Otherwise this could
140  * lead to buffer overflow and slab memory corruption.
141  */
142 struct th_args {
143         int fd;
144         int iter_nbr;
145         int expected_mapped;
146 };
147
148 static void *corruption_th(void *args)
149 {
150         int i;
151         struct th_args *ta = args;
152         struct fiemap fiemap = {
153                 .fm_start = 0,
154                 .fm_flags = (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_DEVICE_ORDER),
155                 .fm_extent_count = 0,
156                 .fm_length = FIEMAP_MAX_OFFSET };
157
158         for (i = 0; i < ta->iter_nbr; i++) {
159                 if (ioctl(ta->fd, FS_IOC_FIEMAP, &fiemap) < 0) {
160                         fprintf(stderr, "error while ioctl: %s\n",
161                                 strerror(errno));
162                         return (void *) (long long) -errno;
163                 }
164                 if (ta->expected_mapped != fiemap.fm_mapped_extents) {
165                         fprintf(stderr, "mapped extents mismatch: expected=%d, returned=%d\n",
166                                 ta->expected_mapped, fiemap.fm_mapped_extents);
167                         return (void *) -EINVAL;
168                 }
169         }
170
171         return NULL;
172 }
173
174 int main(int argc, char **argv)
175 {
176         int c;
177         struct option long_opts[] = {
178                 { .name = "test", .has_arg = no_argument, .val = 't' },
179                 { .name = "corruption_test", .has_arg = no_argument, .val = 'c' },
180                 { .name = NULL }
181         };
182         int fd;
183         int rc;
184         unsigned int mapped_extents = 0;
185         bool corruption_test = false;
186
187         optind = 0;
188         while ((c = getopt_long(argc, argv, "tc", long_opts, NULL)) != -1) {
189                 switch (c) {
190                 case 't':
191                         return 0;
192                 case 'c':
193                         corruption_test = true;
194                         break;
195                 default:
196                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
197                                 argv[0], argv[optind - 1]);
198                 return -1;
199                 }
200         }
201
202         if (optind != argc - 2) {
203                 fprintf(stderr, "Usage: %s <filename> <filesize>\n", argv[0]);
204                 return -1;
205         }
206
207         fd = open(argv[optind], O_RDONLY);
208         if (fd < 0) {
209                 fprintf(stderr, "cannot open %s for reading, error %i",
210                         argv[optind], errno);
211                 return -1;
212         }
213
214         rc = check_fiemap(fd, atoll(argv[optind + 1]), &mapped_extents);
215         if (rc)
216                 goto close;
217
218         if (corruption_test) {
219                 pthread_t th[200];
220                 int i;
221                 void *rval = NULL;
222                 struct th_args args = {
223                         .fd = fd,
224                         .expected_mapped = mapped_extents,
225                         .iter_nbr = 500
226                 };
227
228                 for (i = 0; i < 200; i++) {
229                         rc = pthread_create(&th[i], NULL, corruption_th, &args);
230                         if (rc)
231                                 goto close;
232                 }
233                 for (i = 0; i < 200; i++) {
234                         rc = pthread_join(th[i], &rval);
235                         if (rc || rval) {
236                                 rc =  1;
237                                 goto close;
238                         }
239                 }
240         }
241 close:
242         if (close(fd) < 0)
243                 fprintf(stderr, "closing %s, error %i", argv[optind], errno);
244
245         return rc;
246 }