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