Whamcloud - gitweb
LU-14090 mgs: no local logs flag
[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 struct iam_uapi_op {
276         void *iul_key;
277         void *iul_rec;
278 };
279
280 struct iam_uapi_it {
281         struct iam_uapi_op iui_op;
282         __u16 iui_state;
283 };
284
285 enum iam_ioctl_cmd {
286         IAM_IOC_INIT      = _IOW('i', 1, struct iam_uapi_info),
287         IAM_IOC_GETINFO   = _IOR('i', 2, struct iam_uapi_info),
288         IAM_IOC_INSERT    = _IOR('i', 3, struct iam_uapi_op),
289         IAM_IOC_LOOKUP    = _IOWR('i', 4, struct iam_uapi_op),
290         IAM_IOC_DELETE    = _IOR('i', 5, struct iam_uapi_op),
291         IAM_IOC_IT_START  = _IOR('i', 6, struct iam_uapi_it),
292         IAM_IOC_IT_NEXT   = _IOW('i', 7, struct iam_uapi_it),
293         IAM_IOC_IT_STOP   = _IOR('i', 8, struct iam_uapi_it),
294         IAM_IOC_POLYMORPH = _IOR('i', 9, unsigned long)
295 };
296
297 static unsigned char hex2dec(unsigned char hex)
298 {
299         if (('0' <= hex) && (hex <= '9'))
300                 return hex - '0';
301         else if (('a' <= hex) && (hex <= 'f'))
302                 return hex - 'a' + 10;
303         else if (('A' <= hex) && (hex <= 'F'))
304                 return hex - 'A' + 10;
305         exit(1);
306 }
307
308 static unsigned char *packdigit(unsigned char *number)
309 {
310         unsigned char *area;
311         unsigned char *scan;
312
313         area = calloc(strlen((char *)number) / 2 + 2, sizeof(char));
314         if (area) {
315                 for (scan = area; *number; number += 2, scan++)
316                         *scan = (hex2dec(number[0]) << 4) | hex2dec(number[1]);
317         }
318         return area;
319 }
320
321 static char *iam_convert(int size, int need_convert, char *source)
322 {
323         char *ptr;
324         unsigned char *opt;
325
326         if (!source)
327                 return NULL;
328
329         if (need_convert) {
330                 ptr = calloc(size + 1, sizeof(char));
331                 if (!ptr)
332                         return NULL;
333
334                 opt = packdigit((unsigned char *)source);
335                 if (!opt) {
336                         free(ptr);
337                         return NULL;
338                 }
339                 memcpy(ptr, opt, size + 1);
340                 free(opt);
341         } else {
342                 ptr = strdup(source);
343         }
344
345         return ptr;
346 }
347
348 static int iam_doop(int fd, struct iam_uapi_info *ua, int cmd,
349                     int key_need_convert, char *key_buf,
350                     int *keysize, char *save_key,
351                     int rec_need_convert, char *rec_buf,
352                     int *recsize, char *save_rec)
353 {
354         int ret;
355         char *key;
356         char *rec;
357         struct iam_uapi_op op;
358
359         key = iam_convert(ua->iui_keysize, key_need_convert, key_buf);
360         if (!key)
361                 return -1;
362
363         rec = iam_convert(ua->iui_recsize, rec_need_convert, rec_buf);
364         if (!rec) {
365                 free(key);
366                 return -1;
367         }
368
369         op.iul_key = key;
370         op.iul_rec = rec;
371         ret = ioctl(fd, cmd, &op);
372         if (ret == 0) {
373                 if ((keysize) && (*keysize > 0) && (save_key)) {
374                         if (*keysize > ua->iui_keysize)
375                                 *keysize = ua->iui_keysize;
376                         memcpy(save_key, key, *keysize);
377                 }
378                 if ((recsize) && (*recsize > 0) && (save_rec)) {
379                         if (*recsize > ua->iui_recsize)
380                                 *recsize = ua->iui_recsize;
381                         memcpy(save_rec, rec, *recsize);
382                 }
383         }
384         free(key);
385         free(rec);
386         return ret;
387 }
388
389 /*
390  * Creat an iam file, but do NOT open it.
391  * Return 0 if success, else -1.
392  */
393 int iam_creat(char *filename, enum iam_fmt_t fmt,
394               int blocksize, int keysize, int recsize, int ptrsize)
395 {
396         int fd;
397         char *buf;
398
399         if (!filename) {
400                 errno = EINVAL;
401                 return -1;
402         }
403
404         if ((fmt != FMT_LFIX) && (fmt != FMT_LVAR)) {
405                 errno = EOPNOTSUPP;
406                 return -1;
407         }
408
409         if (blocksize <= 100) {
410                 errno = EINVAL;
411                 return -1;
412         }
413
414         if (keysize < 1) {
415                 errno = EINVAL;
416                 return -1;
417         }
418
419         if (recsize < 0) {
420                 errno = EINVAL;
421                 return -1;
422         }
423
424         if (ptrsize != 4 && ptrsize != 8) {
425                 errno = EINVAL;
426                 return -1;
427         }
428
429         if (keysize + recsize + sizeof(struct iam_leaf_head) > blocksize / 3) {
430                 errno = EINVAL;
431                 return -1;
432         }
433
434         fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0600);
435         if (fd < 0)
436                 return -1;
437
438         buf = malloc(blocksize);
439         if (!buf) {
440                 close(fd);
441                 return -1;
442         }
443
444         memset(buf, 0, blocksize);
445         if (fmt == FMT_LFIX)
446                 lfix_root(buf, blocksize, keysize, ptrsize, recsize);
447         else
448                 lvar_root(buf, blocksize, keysize, ptrsize, recsize);
449
450         if (write(fd, buf, blocksize) != blocksize) {
451                 close(fd);
452                 free(buf);
453                 return -1;
454         }
455
456         memset(buf, 0, blocksize);
457         if (fmt == FMT_LFIX)
458                 lfix_leaf(buf, blocksize, keysize, ptrsize, recsize);
459         else
460                 lvar_leaf(buf, blocksize, keysize, ptrsize, recsize);
461
462         if (write(fd, buf, blocksize) != blocksize) {
463                 close(fd);
464                 free(buf);
465                 return -1;
466         }
467
468         close(fd);
469         free(buf);
470         return 0;
471 }
472
473 /*
474  * Open an iam file, but do NOT creat it if the file doesn't exist.
475  * Please use iam_creat for creating the file before use iam_open.
476  * Return file id (fd) if success, else -1.
477  */
478 int iam_open(char *filename, struct iam_uapi_info *ua)
479 {
480         int fd;
481
482         if (!filename) {
483                 errno = EINVAL;
484                 return -1;
485         }
486
487         if (!ua) {
488                 errno = EINVAL;
489                 return -1;
490         }
491
492         fd = open(filename, O_RDONLY);
493         if (fd < 0)
494                 return -1;
495
496         if (ioctl(fd, IAM_IOC_INIT, ua) != 0) {
497                 close(fd);
498                 return -1;
499         }
500
501         if (ioctl(fd, IAM_IOC_GETINFO, ua) != 0) {
502                 close(fd);
503                 return -1;
504         }
505
506         return fd;
507 }
508
509 /*
510  * Close file opened by iam_open.
511  */
512 int iam_close(int fd)
513 {
514         return close(fd);
515 }
516
517 /*
518  * Please use iam_open before use this function.
519  */
520 int iam_insert(int fd, struct iam_uapi_info *ua,
521                int key_need_convert, char *key_buf,
522                int rec_need_convert, char *rec_buf)
523 {
524         return iam_doop(fd, ua, IAM_IOC_INSERT,
525                         key_need_convert, key_buf, NULL, NULL,
526                         rec_need_convert, rec_buf, NULL, NULL);
527 }
528
529 /*
530  * Please use iam_open before use this function.
531  */
532 int iam_lookup(int fd, struct iam_uapi_info *ua,
533                int key_need_convert, char *key_buf,
534                int *keysize, char *save_key,
535                int rec_need_convert, char *rec_buf,
536                int *recsize, char *save_rec)
537 {
538         return iam_doop(fd, ua, IAM_IOC_LOOKUP,
539                         key_need_convert, key_buf, keysize, save_key,
540                         rec_need_convert, rec_buf, recsize, save_rec);
541 }
542
543 /*
544  * Please use iam_open before use this function.
545  */
546 int iam_delete(int fd, struct iam_uapi_info *ua,
547                int key_need_convert, char *key_buf,
548                int rec_need_convert, char *rec_buf)
549 {
550         return iam_doop(fd, ua, IAM_IOC_DELETE,
551                         key_need_convert, key_buf, NULL, NULL,
552                         rec_need_convert, rec_buf, NULL, NULL);
553 }
554
555 /*
556  * Please use iam_open before use this function.
557  */
558 int iam_it_start(int fd, struct iam_uapi_info *ua,
559                  int key_need_convert, char *key_buf,
560                  int *keysize, char *save_key,
561                  int rec_need_convert, char *rec_buf,
562                  int *recsize, char *save_rec)
563 {
564         return iam_doop(fd, ua, IAM_IOC_IT_START,
565                         key_need_convert, key_buf, keysize, save_key,
566                         rec_need_convert, rec_buf, recsize, save_rec);
567 }
568
569 /*
570  * Please use iam_open before use this function.
571  */
572 int iam_it_next(int fd, struct iam_uapi_info *ua,
573                 int key_need_convert, char *key_buf,
574                 int *keysize, char *save_key,
575                 int rec_need_convert, char *rec_buf,
576                 int *recsize, char *save_rec)
577 {
578         return iam_doop(fd, ua, IAM_IOC_IT_NEXT,
579                         key_need_convert, key_buf, keysize, save_key,
580                         rec_need_convert, rec_buf, recsize, save_rec);
581 }
582
583 /*
584  * Please use iam_open before use this function.
585  */
586 int iam_it_stop(int fd, struct iam_uapi_info *ua,
587                 int key_need_convert, char *key_buf,
588                 int rec_need_convert, char *rec_buf)
589 {
590         return iam_doop(fd, ua, IAM_IOC_IT_STOP,
591                         key_need_convert, key_buf, NULL, NULL,
592                         rec_need_convert, rec_buf, NULL, NULL);
593 }
594
595 /*
596  * Change iam file mode.
597  */
598 int iam_polymorph(char *filename, unsigned long mode)
599 {
600         int fd;
601         int ret;
602
603         if (!filename) {
604                 errno = EINVAL;
605                 return -1;
606         }
607
608         fd = open(filename, O_RDONLY);
609         if (fd < 0)
610                 return -1;
611
612         ret = ioctl(fd, IAM_IOC_POLYMORPH, mode);
613         close(fd);
614         return ret;
615 }