Whamcloud - gitweb
LU-13308 mdc: support additional flags for OBD_IOC_CHLG_POLL ioctl
[fs/lustre-release.git] / lustre / utils / check_iam.c
1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.gnu.org/licenses/gpl-2.0.html
19  *
20  * GPL HEADER END
21  */
22 /*
23  *
24  * User-level tool to check iam files sanity.
25  *
26  * Author: Artem Blagodarenko <artem.blagodarenko@hpe.com>
27  */
28
29 #include <unistd.h>
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <fcntl.h>
33 #include <string.h>
34 #include <endian.h>
35 #include <errno.h>
36
37 #include <sys/types.h>
38 #include <linux/lustre/lustre_user.h>
39 #include <asm/byteorder.h>
40 #include <lustre/libiam.h>
41
42 static int verbose;
43
44 enum {
45         ROOT_NODE,
46         INDEX_NODE,
47         LEAF_NODE,
48         IDLE_NODE
49 };
50
51 struct node_info {
52         int referenced;
53         int recycled;
54         int node_type;
55 };
56
57 void usage(char *str)
58 {
59         printf("usage: %s [-h] [-v] iam_file\n", str);
60 }
61
62 struct iam_params {
63         char *filename;
64         int blocksize;
65         int fmt;
66         int keysize;
67         int recsize;
68         int ptrsize;
69         int indirect_levels;
70         int root_gap;
71         int node_gap;
72         unsigned long idle_blocks;
73         unsigned long current_block;
74         unsigned long long file_size;
75         unsigned long blocks_count;
76         struct node_info *node_info;
77         int rc;
78 };
79
80 int check_idle_blocks(char *buf, struct iam_params *params)
81 {
82         struct iam_idle_head *idle;
83         int i;
84
85         idle = (struct iam_idle_head *)buf;
86
87         if (idle->iih_magic != __cpu_to_le16(IAM_IDLE_HEADER_MAGIC)) {
88                 printf("Wrong magic 0x%x\n", idle->iih_magic);
89                 return -1;
90         }
91
92         if (verbose) {
93                 printf(", %i blocks, next table in block %i, idle blocks: ",
94                        __le16_to_cpu(idle->iih_count),
95                        __le32_to_cpu(idle->iih_next));
96         }
97
98         for (i = 0; i < __le32_to_cpu(idle->iih_count); i++) {
99                 unsigned int blk = __le32_to_cpu(idle->iih_blks[i]);
100
101                 if (verbose)
102                         printf("%i, ", blk);
103                 if (blk >= params->blocks_count) {
104                         printf("Pointer to the idle block (%i) outside the file\n",
105                                blk);
106                         params->rc = -1;
107                 } else {
108                         if (params->node_info[blk].referenced && verbose)
109                                 printf("Reference to recycled node (%i)\n",
110                                        blk);
111                         params->node_info[blk].recycled = 1;
112                 }
113         }
114
115         if (verbose)
116                 printf("\n");
117
118         return 0;
119 }
120
121 static int check_entries(unsigned char *entries, size_t size, int count,
122                          struct iam_params *params)
123 {
124         unsigned int ptr;
125         int i, j, rc;
126
127         for (i = 0; i < count; i++) {
128                 rc = 0;
129                 size -= (params->keysize + params->ptrsize);
130
131                 if (size < 0) {
132                         if (verbose)
133                                 printf("index outside of buffer\n");
134
135                         return -1;
136                 }
137
138                 if (verbose)
139                         printf("key:");
140
141                 for (j = 0; j < params->keysize; j++, entries++)
142                         if (verbose)
143                                 printf("%02x", *entries);
144
145                 ptr = __le32_to_cpu(*((__le32 *)entries));
146
147                 if (ptr >= params->blocks_count) {
148                         params->rc = -1;
149                         rc = -1;
150                 }
151                 if (verbose)
152                         printf(", ptr: %u%s\n", ptr, rc ? " wrong" : "");
153
154                 entries += params->ptrsize;
155
156                 if (rc)
157                         continue;
158
159                 if (params->node_info[ptr].recycled && verbose) {
160                         printf("Reference to recycled node (%u) from node %lu\n",
161                                 ptr, params->current_block);
162                 }
163                 params->node_info[ptr].referenced = 1;
164         }
165
166         return 0;
167 }
168
169 static int check_index(char *buf, struct iam_params *params)
170 {
171         struct iam_index_head *index;
172         int counted_limit;
173         struct dx_countlimit *limit;
174
175         index = (struct iam_index_head *)buf;
176         limit = &index->limit;
177
178         params->node_info[params->current_block].node_type = INDEX_NODE;
179
180         if (verbose)
181                 printf("Index block, count %i, limit %i\n",
182                        __le16_to_cpu(limit->count),
183                        __le16_to_cpu(limit->limit));
184
185         counted_limit = node_limit(params->node_gap, params->blocksize,
186                                    params->keysize + params->ptrsize);
187
188         if (__le16_to_cpu(limit->limit) != counted_limit) {
189                 fprintf(stderr, "Wrong limit %i, counted limit %i\n",
190                         __le16_to_cpu(limit->limit), counted_limit);
191                 return -1;
192         }
193
194
195         if (__le16_to_cpu(limit->count) > __le16_to_cpu(limit->limit)) {
196                 printf("More elements (%i) then limit (%i)\n",
197                         __le16_to_cpu(limit->count),
198                         __le16_to_cpu(limit->limit));
199                 return -1;
200         }
201
202          /* count - 1, because limit is entry itself */
203         if (check_entries(index->entries,
204                           params->blocksize - offsetof(struct iam_index_head,
205                                                        entries),
206                           __le16_to_cpu(limit->count) - 1, params)) {
207                 printf("Broken entries\n");
208                 return -1;
209         }
210
211         return 0;
212 }
213
214 static int check_root(char *buf, size_t size, struct iam_params *params)
215 {
216         struct iam_lfix_root *root;
217         unsigned int counted_limit;
218         int min;
219         struct dx_countlimit *limit;
220
221         if (verbose)
222                 printf("Root format ");
223
224         root = (struct iam_lfix_root *)buf;
225         if (root->ilr_magic == __cpu_to_le64(IAM_LFIX_ROOT_MAGIC)) {
226                 params->fmt = FMT_LFIX;
227                 if (verbose)
228                         printf("LFIX,");
229         } else if (root->ilr_magic == __cpu_to_le64(IAM_LVAR_ROOT_MAGIC)) {
230                 params->fmt = FMT_LVAR;
231                 if (verbose)
232                         printf("LVAR,");
233         } else {
234                 printf("Bad magic %llu\n", __le64_to_cpu(root->ilr_magic));
235                 return -1;
236         }
237
238         limit = &root->limit;
239
240         params->keysize = __le16_to_cpu(root->ilr_keysize);
241         params->recsize = __le16_to_cpu(root->ilr_recsize);
242         params->ptrsize = __le16_to_cpu(root->ilr_ptrsize);
243         params->indirect_levels = root->ilr_indirect_levels;
244
245         params->node_info[0].referenced = 1; //self referance
246         params->node_info[0].node_type = ROOT_NODE;
247
248         params->idle_blocks = __le32_to_cpu(root->idle_blocks);
249         if (params->idle_blocks >= params->blocks_count) {
250                 printf("Idle blocks number (%lu) is out of blocks range (%lu)\n",
251                         params->idle_blocks, params->blocks_count);
252                 params->rc = -1;
253         } else {
254                 params->node_info[params->idle_blocks].referenced = 1;
255                 params->node_info[params->idle_blocks].node_type = IDLE_NODE;
256         }
257
258         if (verbose) {
259                 printf("Idle blocks block number %lu\n", params->idle_blocks);
260                 printf("keysize %i, recsize %i, ptrsize %i, indirect_levels %i\n",
261                        params->keysize, params->recsize, params->ptrsize,
262                        params->indirect_levels);
263         }
264
265         if (params->ptrsize != 4 && params->ptrsize != 8) {
266                 printf("Invalid ptrsize (%i). Only 4 and 8 are supported\n",
267                        params->ptrsize);
268                 return -1;
269         }
270
271         if (params->keysize < 1 || params->recsize < 0) {
272                 printf("Too small key(%i) or recorod(%i)\n",
273                         params->keysize, params->recsize);
274                 return -1;
275         }
276
277         if ((params->keysize + params->recsize +
278             (int)sizeof(struct iam_leaf_head)) > (params->blocksize / 3)) {
279                 printf("Too large record + key or too small block, %i, %i\n",
280                         (params->keysize + params->recsize +
281                          (int)sizeof(struct iam_leaf_head)),
282                         params->blocksize);
283                 return -1;
284         }
285
286         counted_limit = root_limit(params->root_gap, params->node_gap,
287                                    params->blocksize,
288                                    params->keysize + params->ptrsize);
289
290
291         if (__le16_to_cpu(limit->limit) != counted_limit) {
292                 fprintf(stderr, "Wrong limit %i, counted limit %i\n",
293                         __le16_to_cpu(limit->limit), counted_limit);
294                 params->rc = -1;
295         }
296
297         min = (counted_limit < __le16_to_cpu(limit->limit)) ?
298                         counted_limit : __le16_to_cpu(limit->limit);
299
300         if (__le16_to_cpu(limit->count) > __le16_to_cpu(limit->limit)) {
301                 printf("More elements (%i) then limit (%i)\n",
302                         __le16_to_cpu(root->limit.count),
303                         __le16_to_cpu(root->limit.limit));
304                 params->rc = -1;
305         }
306
307         min = (__le16_to_cpu(limit->count) < min) ?
308                         __le16_to_cpu(limit->count) : min;
309
310
311         if (verbose)
312                 printf("count %i, limit %i\n",
313                         __le16_to_cpu(root->limit.count),
314                         __le16_to_cpu(root->limit.limit));
315
316         /* cound - 1, because limit is entry itself */
317         if (check_entries(root->entries,
318                           size - offsetof(struct iam_lfix_root, entries),
319                           min - 1, params)) {
320                 printf("Broken entries\n");
321                 return -1;
322         }
323
324         return 0;
325 }
326
327 static int check_block(char *buf, struct iam_params *params)
328 {
329         struct iam_leaf_head *head;
330
331         head = (struct iam_leaf_head *)buf;
332
333         if (verbose)
334                 printf("Block %lu,", params->current_block);
335
336         switch (head->ill_magic) {
337         case __cpu_to_le16(IAM_LEAF_HEADER_MAGIC):
338                         if (verbose)
339                                 printf("FIX leaf,");
340                         params->node_info[params->current_block].node_type =
341                                 LEAF_NODE;
342                         break;
343         case __cpu_to_le16(IAM_LVAR_ROOT_MAGIC):
344                         if (verbose)
345                                 printf("LVAR leaf,");
346                         break;
347         case __cpu_to_le16(IAM_IDLE_HEADER_MAGIC):
348                         if (verbose)
349                                 printf("IDLE block");
350
351                         params->node_info[params->current_block].referenced = 1;
352
353                         if (check_idle_blocks(buf, params)) {
354                                 printf("Broken idle blocks\n");
355                                 params->rc = -1;
356                         }
357                         break;
358         default:
359                         if (check_index(buf, params)) {
360                                 printf("Broken index node\n");
361                                 params->rc = -1;
362                         }
363                         break;
364         }
365         if (verbose)
366                 printf("count %i\n", head->ill_count);
367
368         return 0;
369 }
370
371 static void print_node_type(int type)
372 {
373         switch (type) {
374         case ROOT_NODE:
375                         printf("ROOT\n");
376                         break;
377         case INDEX_NODE:
378                         printf("INDEX\n");
379                         break;
380         case LEAF_NODE:
381                         printf("LEAF\n");
382                         break;
383         case IDLE_NODE:
384                         printf("IDLE\n");
385                         break;
386         default:
387                         printf("UNKNOWN %i\n", type);
388                         break;
389         }
390 }
391 static int check_unconnected(struct iam_params *params)
392 {
393         unsigned long i;
394         int rc = 0;
395
396         for (i = 0; i < params->blocks_count; i++) {
397                 if (params->node_info[i].referenced &&
398                     params->node_info[i].recycled) {
399                         printf("Node %lu referenced and recycled. FAIL, ", i);
400                         print_node_type(params->node_info[i].node_type);
401                 }
402
403                 if (!params->node_info[i].referenced &&
404                     !params->node_info[i].recycled) {
405                         printf("Unconnected node %lu. FAIL, ", i);
406                         print_node_type(params->node_info[i].node_type);
407                         rc = -1;
408                 }
409         }
410         return rc;
411 }
412 int main(int argc, char **argv)
413 {
414         struct iam_params params;
415         int rc = 0;
416         int opt;
417         void *buf;
418         int fd;
419         struct stat sb;
420
421         params.rc = 0;
422         do {
423                 opt = getopt(argc, argv, "hv");
424                 switch (opt) {
425                 case 'v':
426                                 verbose++;
427                 case -1:
428                                 break;
429                 default:
430                                 fprintf(stderr, "Unable to parse options.");
431                 case 'h':
432                                 printf("HERE\n");
433                                 usage(argv[0]);
434                                 return 0;
435                 }
436         } while (opt != -1);
437
438         if (optind >= argc) {
439                 fprintf(stderr, "Expected filename after options\n");
440                 return -1;
441         }
442
443         params.filename = argv[optind];
444         params.blocksize = 4096;
445         params.current_block = 0;
446         params.root_gap = sizeof(struct iam_lfix_root);
447         params.node_gap = 0;
448
449         fd = open(params.filename, O_RDONLY);
450         if (fd < 0) {
451                 fprintf(stderr, "Can not open file %s, %s\n",
452                         params.filename, strerror(errno));
453                 return -1;
454         }
455
456         if (fstat(fd, &sb) == -1) {
457                 fprintf(stderr, "Error stat file.\n");
458                 close(fd);
459                 return -1;
460         }
461         params.file_size = (unsigned long long)sb.st_size;
462         params.blocks_count = params.file_size / params.blocksize +
463                                 ((params.file_size % params.blocksize) ? 1 : 0);
464
465         if (verbose)
466                 printf("Filesize %llu, blocks count %lu\n", params.file_size,
467                        params.blocks_count);
468         buf = malloc(params.blocksize);
469         if (buf == NULL) {
470                 fprintf(stderr, "Can't allocate buffer\n");
471                 close(fd);
472                 return -1;
473         }
474         params.node_info = malloc(params.blocks_count *
475                                   sizeof(struct node_info));
476         memset(params.node_info, 0,
477                params.blocks_count * sizeof(struct node_info));
478
479         /* Read root block */
480         if (read(fd, buf, params.blocksize) < params.blocksize) {
481                 fprintf(stderr, "Can't read root block\n");
482                 params.rc = -1;
483                 goto err;
484         }
485
486         rc = check_root(buf, params.blocksize, &params);
487         if (rc) {
488                 printf("Root node is insane\n");
489                 params.rc = rc;
490         }
491
492         params.current_block++;
493
494         /* Read all another blocks */
495         while (read(fd, buf, params.blocksize)) {
496                 rc = check_block(buf, &params);
497                 if (rc) {
498                         printf("Node with offset 0x%lx in %s is broken\n",
499                                 params.current_block * params.blocksize,
500                                 params.filename);
501                         params.rc = rc;
502                 }
503                 params.current_block++;
504         }
505
506         rc = check_unconnected(&params);
507         if (rc)
508                 printf("There are unconnected nodes\n");
509 err:
510         if (!(rc ? rc : params.rc))
511                 printf("NO ERRORS\n");
512         else
513                 printf("FINISHED WITH ERRORS\n");
514
515         free(params.node_info);
516         free(buf);
517         close(fd);
518
519         return rc ?: params.rc;
520 }