3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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.
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).
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
18 * Please visit http://www.xyratex.com/contact if you need additional
19 * information or have any questions.
25 * Copyright 2013 Xyratex Technology Limited
27 * Author: Artem Blagodarenko <Artem_Blagodarenko@xyratex.com>
34 #include <sys/ioctl.h>
35 #include <sys/types.h>
42 #include <linux/types.h>
44 #include <linux/lustre/lustre_user.h>
47 # define FS_IOC_FIEMAP (_IOWR('f', 11, struct fiemap))
52 static inline void print_extent_flags(unsigned int flags) {
56 printf("flags (0x%x):", flags);
57 if (flags & FIEMAP_EXTENT_LAST)
59 if (flags & FIEMAP_EXTENT_UNKNOWN)
61 if (flags & FIEMAP_EXTENT_DELALLOC)
63 if (flags & FIEMAP_EXTENT_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)
73 if (flags & FIEMAP_EXTENT_UNWRITTEN)
75 if (flags & FIEMAP_EXTENT_MERGED)
77 if (flags & FIEMAP_EXTENT_SHARED)
79 if (flags & FIEMAP_EXTENT_NET)
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)
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)) /
98 long long ext_len_sum = 0;
100 memset(&fiemap_buf, 0, sizeof(fiemap_buf));
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;
107 if (ioctl(fd, FS_IOC_FIEMAP, fiemap) < 0) {
108 fprintf(stderr, "error while ioctl %i\n", errno);
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);
117 print_extent_flags(fm_extents[i].fe_flags);
119 if (fm_extents[i].fe_flags & FIEMAP_EXTENT_UNWRITTEN) {
120 fprintf(stderr, "Unwritten extent\n");
123 ext_len_sum += fm_extents[i].fe_length;
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);
131 *mapped_extents = fiemap->fm_mapped_extents;
132 return ext_len_sum != expected_sum || (expected_sum && !*mapped_extents);
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.
148 static void *corruption_th(void *args)
151 struct th_args *ta = args;
152 struct fiemap fiemap = {
154 .fm_flags = (FIEMAP_FLAG_SYNC | FIEMAP_FLAG_DEVICE_ORDER),
155 .fm_extent_count = 0,
156 .fm_length = FIEMAP_MAX_OFFSET };
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",
162 return (void *) (long long) -errno;
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;
174 int main(int argc, char **argv)
177 struct option long_opts[] = {
178 { .name = "test", .has_arg = no_argument, .val = 't' },
179 { .name = "corruption_test", .has_arg = no_argument, .val = 'c' },
184 unsigned int mapped_extents = 0;
185 bool corruption_test = false;
188 while ((c = getopt_long(argc, argv, "tc", long_opts, NULL)) != -1) {
193 corruption_test = true;
196 fprintf(stderr, "error: %s: option '%s' unrecognized\n",
197 argv[0], argv[optind - 1]);
202 if (optind != argc - 2) {
203 fprintf(stderr, "Usage: %s <filename> <filesize>\n", argv[0]);
207 fd = open(argv[optind], O_RDONLY);
209 fprintf(stderr, "cannot open %s for reading, error %i",
210 argv[optind], errno);
214 rc = check_fiemap(fd, atoll(argv[optind + 1]), &mapped_extents);
218 if (corruption_test) {
222 struct th_args args = {
224 .expected_mapped = mapped_extents,
228 for (i = 0; i < 200; i++) {
229 rc = pthread_create(&th[i], NULL, corruption_th, &args);
233 for (i = 0; i < 200; i++) {
234 rc = pthread_join(th[i], &rval);
243 fprintf(stderr, "closing %s, error %i", argv[optind], errno);