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