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