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