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