Whamcloud - gitweb
LU-17402 kernel: update dotdot patch path for RHEL 8.10
[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 {
54         if (!flags)
55                 return;
56
57         printf("flags (0x%x):", flags);
58         if (flags & FIEMAP_EXTENT_LAST)
59                 printf(" LAST");
60         if (flags & FIEMAP_EXTENT_UNKNOWN)
61                 printf(" UNKNOWN");
62         if (flags & FIEMAP_EXTENT_DELALLOC)
63                 printf(" DELALLOC");
64         if (flags & FIEMAP_EXTENT_ENCODED)
65                 printf(" ENCODED");
66         if (flags & FIEMAP_EXTENT_DATA_ENCRYPTED)
67                 printf(" DATA_ENCRYPTED");
68         if (flags & FIEMAP_EXTENT_NOT_ALIGNED)
69                 printf(" NOT_ALIGNED");
70         if (flags & FIEMAP_EXTENT_DATA_INLINE)
71                 printf(" DATA_INLINE");
72         if (flags & FIEMAP_EXTENT_DATA_TAIL)
73                 printf(" DATA_TAIL");
74         if (flags & FIEMAP_EXTENT_UNWRITTEN)
75                 printf(" UNWRITTEN");
76         if (flags & FIEMAP_EXTENT_MERGED)
77                 printf(" MERGED");
78         if (flags & FIEMAP_EXTENT_SHARED)
79                 printf(" SHARED");
80         if (flags & FIEMAP_EXTENT_NET)
81                 printf(" NET");
82         printf("\n");
83 }
84
85
86 /* This test executes fiemap ioctl and check
87  * a) there are no file ranges marked with FIEMAP_EXTENT_UNWRITTEN
88  * b) data ranges sizes sum is equal to given in second param
89  */
90 static int check_fiemap(int fd, long long expected_sum,
91                         unsigned int *mapped_extents)
92 {
93         /* This buffer is enougth for 1MB length file */
94         union { struct fiemap f; char c[4096]; } fiemap_buf;
95         struct fiemap *fiemap = &fiemap_buf.f;
96         struct fiemap_extent *fm_extents = &fiemap->fm_extents[0];
97         unsigned int count = (sizeof(fiemap_buf) - sizeof(*fiemap)) /
98                         sizeof(*fm_extents);
99         unsigned int i = 0;
100         long long ext_len_sum = 0;
101
102         memset(&fiemap_buf, 0, sizeof(fiemap_buf));
103
104         fiemap->fm_start = 0;
105         fiemap->fm_flags = (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_DEVICE_ORDER);
106         fiemap->fm_extent_count = count;
107         fiemap->fm_length = FIEMAP_MAX_OFFSET;
108
109         if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0) {
110                 fprintf(stderr, "error while ioctl %i\n",  errno);
111                 return -1;
112         }
113
114         for (i = 0; i < fiemap->fm_mapped_extents; i++) {
115                 printf("extent %d in offset %lu, length %lu\n",
116                         i, (unsigned long)fm_extents[i].fe_logical,
117                         (unsigned long)fm_extents[i].fe_length);
118
119                 print_extent_flags(fm_extents[i].fe_flags);
120
121                 if (fm_extents[i].fe_flags & FIEMAP_EXTENT_UNWRITTEN) {
122                         fprintf(stderr, "Unwritten extent\n");
123                         return -2;
124                 }
125                 ext_len_sum += fm_extents[i].fe_length;
126         }
127
128         printf("No unwritten extents, extents number %u, sum of lengths %lli, expected sum %lli\n",
129                 fiemap->fm_mapped_extents,
130                 ext_len_sum, expected_sum);
131
132         *mapped_extents = fiemap->fm_mapped_extents;
133         return ext_len_sum != expected_sum ||
134                 (expected_sum && !*mapped_extents);
135 }
136
137 /* LU-17110
138  * When userspace uses fiemap with fm_extent_count=0, it means that kernelspace
139  * should return only the number of extents. So we should always check
140  * fm_extent_count before accessing to fm_extents array. Otherwise this could
141  * lead to buffer overflow and slab memory corruption.
142  */
143 struct th_args {
144         int fd;
145         int iter_nbr;
146         int expected_mapped;
147 };
148
149 static void *corruption_th(void *args)
150 {
151         int i;
152         struct th_args *ta = args;
153         struct fiemap fiemap = {
154                 .fm_start = 0,
155                 .fm_flags = (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_DEVICE_ORDER),
156                 .fm_extent_count = 0,
157                 .fm_length = FIEMAP_MAX_OFFSET };
158
159         for (i = 0; i < ta->iter_nbr; i++) {
160                 if (ioctl(ta->fd, FS_IOC_FIEMAP, &fiemap) < 0) {
161                         fprintf(stderr, "error while ioctl: %s\n",
162                                 strerror(errno));
163                         return (void *) (long long) -errno;
164                 }
165                 if (ta->expected_mapped != fiemap.fm_mapped_extents) {
166                         fprintf(stderr,
167                                 "mapped extents mismatch: expected=%d, returned=%d\n",
168                                 ta->expected_mapped, fiemap.fm_mapped_extents);
169                         return (void *) -EINVAL;
170                 }
171         }
172
173         return NULL;
174 }
175
176 int main(int argc, char **argv)
177 {
178         int c;
179         struct option long_opts[] = {
180                 { .name = "test", .has_arg = no_argument, .val = 't' },
181                 { .name = "corruption_test", .has_arg = no_argument, .val = 'c' },
182                 { .name = NULL }
183         };
184         int fd;
185         int rc;
186         unsigned int mapped_extents = 0;
187         bool corruption_test = false;
188
189         optind = 0;
190         while ((c = getopt_long(argc, argv, "tc", long_opts, NULL)) != -1) {
191                 switch (c) {
192                 case 't':
193                         return 0;
194                 case 'c':
195                         corruption_test = true;
196                         break;
197                 default:
198                         fprintf(stderr, "error: %s: option '%s' unrecognized\n",
199                                 argv[0], argv[optind - 1]);
200                 return -1;
201                 }
202         }
203
204         if (optind != argc - 2) {
205                 fprintf(stderr, "Usage: %s <filename> <filesize>\n", argv[0]);
206                 return -1;
207         }
208
209         fd = open(argv[optind], O_RDONLY);
210         if (fd < 0) {
211                 fprintf(stderr, "cannot open %s for reading, error %i",
212                         argv[optind], errno);
213                 return -1;
214         }
215
216         rc = check_fiemap(fd, atoll(argv[optind + 1]), &mapped_extents);
217         if (rc)
218                 goto close;
219
220         if (corruption_test) {
221                 pthread_t th[200];
222                 int i;
223                 void *rval = NULL;
224                 struct th_args args = {
225                         .fd = fd,
226                         .expected_mapped = mapped_extents,
227                         .iter_nbr = 500
228                 };
229
230                 for (i = 0; i < 200; i++) {
231                         rc = pthread_create(&th[i], NULL, corruption_th, &args);
232                         if (rc)
233                                 goto close;
234                 }
235                 for (i = 0; i < 200; i++) {
236                         rc = pthread_join(th[i], &rval);
237                         if (rc || rval) {
238                                 rc =  1;
239                                 goto close;
240                         }
241                 }
242         }
243 close:
244         if (close(fd) < 0)
245                 fprintf(stderr, "closing %s, error %i", argv[optind], errno);
246
247         return rc;
248 }