Whamcloud - gitweb
LU-6635 lfsck: block replacing the OST-object for test
[fs/lustre-release.git] / lustre / utils / libiam.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  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
24  * Use is subject to license terms.
25  *
26  * Copyright (c) 2014, 2015, Intel Corporation.
27  */
28 /*
29  * This file is part of Lustre, http://www.lustre.org/
30  * Lustre is a trademark of Sun Microsystems, Inc.
31  *
32  * lustre/utils/libiam.c
33  *
34  * iam user level library
35  *
36  * Author: Wang Di <wangdi@clusterfs.com>
37  * Author: Nikita Danilov <nikita@clusterfs.com>
38  * Author: Fan Yong <fanyong@clusterfs.com>
39  */
40
41 #include <unistd.h>
42 #include <stdlib.h>
43 #include <stdio.h>
44 #include <fcntl.h>
45 #include <string.h>
46 #include <endian.h>
47 #include <errno.h>
48 #include <assert.h>
49 #include <sys/ioctl.h>
50 #include <sys/types.h>
51
52 #include <libcfs/util/string.h>
53 #include <lustre/libiam.h>
54
55 typedef __u32 lvar_hash_t;
56
57 enum {
58         IAM_LFIX_ROOT_MAGIC = 0xbedabb1edULL,
59         IAM_LVAR_ROOT_MAGIC = 0xb01dface
60 };
61
62 struct iam_lfix_root {
63         u_int64_t  ilr_magic;
64         u_int16_t  ilr_keysize;
65         u_int16_t  ilr_recsize;
66         u_int16_t  ilr_ptrsize;
67         u_int16_t  ilr_indirect_levels;
68 };
69
70 enum {
71         IAM_LEAF_HEADER_MAGIC = 0x1976,
72         IAM_LVAR_LEAF_MAGIC   = 0x1973
73 };
74
75 struct iam_leaf_head {
76         u_int16_t ill_magic;
77         u_int16_t ill_count;
78 };
79
80 struct dx_countlimit {
81         u_int16_t limit;
82         u_int16_t count;
83 };
84
85 struct lvar_leaf_header {
86         u_int16_t vlh_magic; /* magic number IAM_LVAR_LEAF_MAGIC */
87         u_int16_t vlh_used;  /* used bytes, including header */
88 };
89
90 struct lvar_root {
91         u_int32_t vr_magic;
92         u_int16_t vr_recsize;
93         u_int16_t vr_ptrsize;
94         u_int8_t  vr_indirect_levels;
95         u_int8_t  vr_padding0;
96         u_int16_t vr_padding1;
97 };
98
99 struct lvar_leaf_entry {
100         u_int32_t vle_hash;
101         u_int16_t vle_keysize;
102         u_int8_t  vle_key[0];
103 };
104
105 enum {
106         LVAR_PAD   = 4,
107         LVAR_ROUND = LVAR_PAD - 1
108 };
109
110 /**
111  * Stores \a val at \a dst, where the latter is possibly unaligned. Uses
112  * memcpy(). This macro is needed to avoid dependency of user level tools on
113  * the kernel headers.
114  */
115 #define STORE_UNALIGNED(val, dst)               \
116 ({                                              \
117         typeof(*(dst)) __val = (val);           \
118                                                 \
119         memcpy(dst, &__val, sizeof *(dst));     \
120 })
121
122 static int root_limit(int rootgap, int blocksize, int size)
123 {
124         int limit;
125         int nlimit;
126
127         limit = (blocksize - rootgap) / size;
128         nlimit = blocksize / size;
129         if (limit == nlimit)
130                 limit--;
131         return limit;
132 }
133
134 static int lfix_root_limit(int blocksize, int size)
135 {
136         return root_limit(sizeof(struct iam_lfix_root), blocksize, size);
137 }
138
139 static void lfix_root(void *buf,
140                       int blocksize, int keysize, int ptrsize, int recsize)
141 {
142         struct iam_lfix_root *root;
143         struct dx_countlimit *limit;
144         void                 *entry;
145
146         root = buf;
147         *root = (typeof(*root)) {
148                 .ilr_magic           = htole64(IAM_LFIX_ROOT_MAGIC),
149                 .ilr_keysize         = htole16(keysize),
150                 .ilr_recsize         = htole16(recsize),
151                 .ilr_ptrsize         = htole16(ptrsize),
152                 .ilr_indirect_levels = 0
153         };
154
155         limit = (void *)(root + 1);
156         *limit = (typeof(*limit)){
157                 /*
158                  * limit itself + one pointer to the leaf.
159                  */
160                 .count = htole16(2),
161                 .limit = lfix_root_limit(blocksize, keysize + ptrsize)
162         };
163
164         entry = root + 1;
165         /*
166          * Skip over @limit.
167          */
168         entry += keysize + ptrsize;
169
170         /*
171          * Entry format is <key> followed by <ptr>. In the minimal tree
172          * consisting of a root and single node, <key> is a minimal possible
173          * key.
174          *
175          * XXX: this key is hard-coded to be a sequence of 0's.
176          */
177         entry += keysize;
178         /* now @entry points to <ptr> */
179         if (ptrsize == 4)
180                 STORE_UNALIGNED(htole32(1), (u_int32_t *)entry);
181         else
182                 STORE_UNALIGNED(htole64(1), (u_int64_t *)entry);
183 }
184
185 static void lfix_leaf(void *buf,
186                       int blocksize, int keysize, int ptrsize, int recsize)
187 {
188         struct iam_leaf_head *head;
189
190         /* form leaf */
191         head = buf;
192         *head = (typeof(*head)) {
193                 .ill_magic = htole16(IAM_LEAF_HEADER_MAGIC),
194                 /*
195                  * Leaf contains an entry with the smallest possible key
196                  * (created by zeroing).
197                  */
198                 .ill_count = htole16(1),
199         };
200 }
201
202 static int lvar_root_limit(int blocksize, int size)
203 {
204         return root_limit(sizeof(struct lvar_root), blocksize, size);
205 }
206
207 static void lvar_root(void *buf,
208                       int blocksize, int keysize, int ptrsize, int recsize)
209 {
210         struct lvar_root *root;
211         struct dx_countlimit *limit;
212         void                 *entry;
213         int isize;
214
215         isize = sizeof(lvar_hash_t) + ptrsize;
216         root = buf;
217         *root = (typeof(*root)) {
218                 .vr_magic            = htole32(IAM_LVAR_ROOT_MAGIC),
219                 .vr_recsize          = htole16(recsize),
220                 .vr_ptrsize          = htole16(ptrsize),
221                 .vr_indirect_levels  = 0
222         };
223
224         limit = (void *)(root + 1);
225         *limit = (typeof(*limit)) {
226                 /*
227                  * limit itself + one pointer to the leaf.
228                  */
229                 .count = htole16(2),
230                 .limit = lvar_root_limit(blocksize, keysize + ptrsize)
231         };
232
233         entry = root + 1;
234         /*
235          * Skip over @limit.
236          */
237         entry += isize;
238
239         /*
240          * Entry format is <key> followed by <ptr>. In the minimal tree
241          * consisting of a root and single node, <key> is a minimal possible
242          * key.
243          *
244          * XXX: this key is hard-coded to be a sequence of 0's.
245          */
246         entry += sizeof(lvar_hash_t);
247         /* now @entry points to <ptr> */
248         if (ptrsize == 4)
249                 STORE_UNALIGNED(htole32(1), (u_int32_t *)entry);
250         else
251                 STORE_UNALIGNED(htole64(1), (u_int64_t *)entry);
252 }
253
254 static int lvar_esize(int namelen, int recsize)
255 {
256         return (offsetof(struct lvar_leaf_entry, vle_key) +
257                 namelen + recsize + LVAR_ROUND) & ~LVAR_ROUND;
258 }
259
260 static void lvar_leaf(void *buf,
261                       int blocksize, int keysize, int ptrsize, int recsize)
262 {
263         struct lvar_leaf_header *head;
264         char                    *rec;
265
266         /* form leaf */
267         head = buf;
268         *head = (typeof(*head)) {
269                 .vlh_magic = htole16(IAM_LVAR_LEAF_MAGIC),
270                 .vlh_used  = htole16(sizeof *head + lvar_esize(0, recsize))
271         };
272         rec = (void *)(head + 1);
273         rec[offsetof(struct lvar_leaf_entry, vle_key)] = recsize;
274 }
275
276
277 struct iam_uapi_op {
278         void *iul_key;
279         void *iul_rec;
280 };
281
282 struct iam_uapi_it {
283         struct iam_uapi_op iui_op;
284         __u16              iui_state;
285 };
286
287 enum iam_ioctl_cmd {
288         IAM_IOC_INIT      = _IOW('i', 1, struct iam_uapi_info),
289         IAM_IOC_GETINFO   = _IOR('i', 2, struct iam_uapi_info),
290         IAM_IOC_INSERT    = _IOR('i', 3, struct iam_uapi_op),
291         IAM_IOC_LOOKUP    = _IOWR('i', 4, struct iam_uapi_op),
292         IAM_IOC_DELETE    = _IOR('i', 5, struct iam_uapi_op),
293         IAM_IOC_IT_START  = _IOR('i', 6, struct iam_uapi_it),
294         IAM_IOC_IT_NEXT   = _IOW('i', 7, struct iam_uapi_it),
295         IAM_IOC_IT_STOP   = _IOR('i', 8, struct iam_uapi_it),
296         IAM_IOC_POLYMORPH = _IOR('i', 9, unsigned long)
297 };
298
299 static unsigned char hex2dec(unsigned char hex)
300 {
301         if (('0' <= hex) && (hex <= '9'))
302                 return hex - '0';
303         else if (('a' <= hex) && (hex <= 'f'))
304                 return hex - 'a' + 10;
305         else if (('A' <= hex) && (hex <= 'F'))
306                 return hex - 'A' + 10;
307         else
308                 exit(1);
309 }
310
311 static unsigned char *packdigit(unsigned char *number)
312 {
313         unsigned char *area;
314         unsigned char *scan;
315
316         area = calloc(strlen((char *)number) / 2 + 2, sizeof(char));
317         if (area != NULL) {
318                 for (scan = area; *number; number += 2, scan++)
319                         *scan = (hex2dec(number[0]) << 4) | hex2dec(number[1]);
320         }
321         return area;
322 }
323
324 static char *iam_convert(int size, int need_convert, char *source)
325 {
326         char *ptr;
327         unsigned char *opt;
328
329         if (source == NULL)
330                 return NULL;
331
332         ptr = calloc(size + 1, sizeof(char));
333         if (ptr == NULL)
334                 return NULL;
335
336         if (need_convert) {
337                 opt = packdigit((unsigned char*)source);
338                 if (opt == NULL) {
339                         free(ptr);
340                         return NULL;
341                 } else {
342                         memcpy(ptr, opt, size + 1);
343                         free(opt);
344                 }
345         } else {
346                 strlcpy(ptr, source, size + 1);
347         }
348
349         return ptr;
350 }
351
352 static int iam_doop(int fd, struct iam_uapi_info *ua, int cmd,
353                     int key_need_convert, char *key_buf,
354                     int *keysize, char *save_key,
355                     int rec_need_convert, char *rec_buf,
356                     int *recsize, char *save_rec)
357 {
358         int ret;
359         char *key;
360         char *rec;
361         struct iam_uapi_op op;
362
363         key = iam_convert(ua->iui_keysize, key_need_convert, key_buf);
364         if (key == NULL)
365                 return -1;
366
367         rec = iam_convert(ua->iui_recsize, rec_need_convert, rec_buf);
368         if (rec == NULL) {
369                 free(key);
370                 return -1;
371         }
372
373         op.iul_key = key;
374         op.iul_rec = rec;
375         ret = ioctl(fd, cmd, &op);
376         if (ret == 0) {
377                 if ((keysize != NULL) && (*keysize > 0) && (save_key != NULL)) {
378                         if (*keysize > ua->iui_keysize)
379                                 *keysize = ua->iui_keysize;
380                         memcpy(save_key, key, *keysize);
381                 }
382                 if ((recsize != NULL) && (*recsize > 0) && (save_rec != NULL)) {
383                         if (*recsize > ua->iui_recsize)
384                                 *recsize = ua->iui_recsize;
385                         memcpy(save_rec, rec, *recsize);
386                 }
387         }
388         free(key);
389         free(rec);
390         return ret;
391 }
392
393
394 /*
395  * Creat an iam file, but do NOT open it.
396  * Return 0 if success, else -1.
397  */
398 int iam_creat(char *filename, enum iam_fmt_t fmt,
399               int blocksize, int keysize, int recsize, int ptrsize)
400 {
401         int fd;
402         char *buf;
403
404         if (filename == NULL) {
405                 errno = EINVAL;
406                 return -1;
407         }
408
409         if ((fmt != FMT_LFIX) && (fmt != FMT_LVAR)) {
410                 errno = EOPNOTSUPP;
411                 return -1;
412         }
413
414         if (blocksize <= 100) {
415                 errno = EINVAL;
416                 return -1;
417         }
418
419         if (keysize < 1) {
420                 errno = EINVAL;
421                 return -1;
422         }
423
424         if (recsize < 0) {
425                 errno = EINVAL;
426                 return -1;
427         }
428
429         if (ptrsize != 4 && ptrsize != 8) {
430                 errno = EINVAL;
431                 return -1;
432         }
433
434         if (keysize + recsize + sizeof(struct iam_leaf_head) > blocksize / 3) {
435                 errno = EINVAL;
436                 return -1;
437         }
438
439         fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0600);
440         if (fd < 0) {
441                 return -1;
442         }
443
444         buf = malloc(blocksize);
445         if (buf == NULL) {
446                 close(fd);
447                 return -1;
448         }
449
450         memset(buf, 0, blocksize);
451         if (fmt == FMT_LFIX)
452                 lfix_root(buf, blocksize, keysize, ptrsize, recsize);
453         else
454                 lvar_root(buf, blocksize, keysize, ptrsize, recsize);
455
456         if (write(fd, buf, blocksize) != blocksize) {
457                 close(fd);
458                 free(buf);
459                 return -1;
460         }
461
462         memset(buf, 0, blocksize);
463         if (fmt == FMT_LFIX)
464                 lfix_leaf(buf, blocksize, keysize, ptrsize, recsize);
465         else
466                 lvar_leaf(buf, blocksize, keysize, ptrsize, recsize);
467
468         if (write(fd, buf, blocksize) != blocksize) {
469                 close(fd);
470                 free(buf);
471                 return -1;
472         }
473
474         close(fd);
475         free(buf);
476         return 0;
477 }
478
479 /*
480  * Open an iam file, but do NOT creat it if the file doesn't exist.
481  * Please use iam_creat for creating the file before use iam_open.
482  * Return file id (fd) if success, else -1.
483  */
484 int iam_open(char *filename, struct iam_uapi_info *ua)
485 {
486         int fd;
487
488         if (filename == NULL) {
489                 errno = EINVAL;
490                 return -1;
491         }
492
493         if (ua == NULL) {
494                 errno = EINVAL;
495                 return -1;
496         }
497
498         fd = open(filename, O_RDONLY);
499         if (fd < 0) {
500                 return -1;
501         }
502
503         if (ioctl(fd, IAM_IOC_INIT, ua) != 0) {
504                 close(fd);
505                 return -1;
506         }
507
508         if (ioctl(fd, IAM_IOC_GETINFO, ua) != 0) {
509                 close(fd);
510                 return -1;
511         }
512
513         return fd;
514 }
515
516 /*
517  * Close file opened by iam_open.
518  */
519 int iam_close(int fd)
520 {
521         return close(fd);
522 }
523
524 /*
525  * Please use iam_open before use this function.
526  */
527 int iam_insert(int fd, struct iam_uapi_info *ua,
528                int key_need_convert, char *key_buf,
529                int rec_need_convert, char *rec_buf)
530 {
531         return iam_doop(fd, ua, IAM_IOC_INSERT,
532                         key_need_convert, key_buf, NULL, NULL,
533                         rec_need_convert, rec_buf, NULL, NULL);
534 }
535
536 /*
537  * Please use iam_open before use this function.
538  */
539 int iam_lookup(int fd, struct iam_uapi_info *ua,
540                int key_need_convert, char *key_buf,
541                int *keysize, char *save_key,
542                int rec_need_convert, char *rec_buf,
543                int *recsize, char *save_rec)
544 {
545         return iam_doop(fd, ua, IAM_IOC_LOOKUP,
546                        key_need_convert, key_buf, keysize, save_key,
547                        rec_need_convert, rec_buf, recsize, save_rec);
548 }
549
550 /*
551  * Please use iam_open before use this function.
552  */
553 int iam_delete(int fd, struct iam_uapi_info *ua,
554                int key_need_convert, char *key_buf,
555                int rec_need_convert, char *rec_buf)
556 {
557         return iam_doop(fd, ua, IAM_IOC_DELETE,
558                         key_need_convert, key_buf, NULL, NULL,
559                         rec_need_convert, rec_buf, NULL, NULL);
560 }
561
562 /*
563  * Please use iam_open before use this function.
564  */
565 int iam_it_start(int fd, struct iam_uapi_info *ua,
566                  int key_need_convert, char *key_buf,
567                  int *keysize, char *save_key,
568                  int rec_need_convert, char *rec_buf,
569                  int *recsize, char *save_rec)
570 {
571         return iam_doop(fd, ua, IAM_IOC_IT_START,
572                        key_need_convert, key_buf, keysize, save_key,
573                        rec_need_convert, rec_buf, recsize, save_rec);
574 }
575
576 /*
577  * Please use iam_open before use this function.
578  */
579 int iam_it_next(int fd, struct iam_uapi_info *ua,
580                 int key_need_convert, char *key_buf,
581                 int *keysize, char *save_key,
582                 int rec_need_convert, char *rec_buf,
583                 int *recsize, char *save_rec)
584 {
585         return iam_doop(fd, ua, IAM_IOC_IT_NEXT,
586                        key_need_convert, key_buf, keysize, save_key,
587                        rec_need_convert, rec_buf, recsize, save_rec);
588 }
589
590 /*
591  * Please use iam_open before use this function.
592  */
593 int iam_it_stop(int fd, struct iam_uapi_info *ua,
594                 int key_need_convert, char *key_buf,
595                 int rec_need_convert, char *rec_buf)
596 {
597         return iam_doop(fd, ua, IAM_IOC_IT_STOP,
598                         key_need_convert, key_buf, NULL, NULL,
599                         rec_need_convert, rec_buf, NULL, NULL);
600 }
601
602 /*
603  * Change iam file mode.
604  */
605 int iam_polymorph(char *filename, unsigned long mode)
606 {
607         int fd;
608         int ret;
609
610         if (filename == NULL) {
611                 errno = EINVAL;
612                 return -1;
613         }
614
615         fd = open(filename, O_RDONLY);
616         if (fd < 0) {
617                 return -1;
618         }
619
620         ret = ioctl(fd, IAM_IOC_POLYMORPH, mode);
621         close(fd);
622         return ret;
623 }