Whamcloud - gitweb
Branch HEAD
[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 static int root_limit(int rootgap, int blocksize, int size)
105 {
106         int limit;
107         int nlimit;
108
109         limit = (blocksize - rootgap) / size;
110         nlimit = blocksize / size;
111         if (limit == nlimit)
112                 limit--;
113         return limit;
114 }
115
116 static int lfix_root_limit(int blocksize, int size)
117 {
118         return root_limit(sizeof(struct iam_lfix_root), blocksize, size);
119 }
120
121 static void lfix_root(void *buf,
122                       int blocksize, int keysize, int ptrsize, int recsize)
123 {
124         struct iam_lfix_root *root;
125         struct dx_countlimit *limit;
126         void                 *entry;
127
128         root = buf;
129         *root = (typeof(*root)) {
130                 .ilr_magic           = cpu_to_le64(IAM_LFIX_ROOT_MAGIC),
131                 .ilr_keysize         = cpu_to_le16(keysize),
132                 .ilr_recsize         = cpu_to_le16(recsize),
133                 .ilr_ptrsize         = cpu_to_le16(ptrsize),
134                 .ilr_indirect_levels = 0
135         };
136
137         limit = (void *)(root + 1);
138         *limit = (typeof(*limit)){
139                 /*
140                  * limit itself + one pointer to the leaf.
141                  */
142                 .count = cpu_to_le16(2),
143                 .limit = lfix_root_limit(blocksize, keysize + ptrsize)
144         };
145
146         entry = root + 1;
147         /*
148          * Skip over @limit.
149          */
150         entry += keysize + ptrsize;
151
152         /*
153          * Entry format is <key> followed by <ptr>. In the minimal tree
154          * consisting of a root and single node, <key> is a minimal possible
155          * key.
156          *
157          * XXX: this key is hard-coded to be a sequence of 0's.
158          */
159         entry += keysize;
160         /* now @entry points to <ptr> */
161         if (ptrsize == 4)
162                 *(u_int32_t *)entry = cpu_to_le32(1);
163         else
164                 *(u_int64_t *)entry = cpu_to_le64(1);
165 }
166
167 static void lfix_leaf(void *buf,
168                       int blocksize, int keysize, int ptrsize, int recsize)
169 {
170         struct iam_leaf_head *head;
171
172         /* form leaf */
173         head = buf;
174         *head = (typeof(*head)) {
175                 .ill_magic = cpu_to_le16(IAM_LEAF_HEADER_MAGIC),
176                 /*
177                  * Leaf contains an entry with the smallest possible key
178                  * (created by zeroing).
179                  */
180                 .ill_count = cpu_to_le16(1),
181         };
182 }
183
184 static int lvar_root_limit(int blocksize, int size)
185 {
186         return root_limit(sizeof(struct lvar_root), blocksize, size);
187 }
188
189 static void lvar_root(void *buf,
190                       int blocksize, int keysize, int ptrsize, int recsize)
191 {
192         struct lvar_root *root;
193         struct dx_countlimit *limit;
194         void                 *entry;
195         int isize;
196
197         isize = sizeof(lvar_hash_t) + ptrsize;
198         root = buf;
199         *root = (typeof(*root)) {
200                 .vr_magic            = cpu_to_le32(IAM_LVAR_ROOT_MAGIC),
201                 .vr_recsize          = cpu_to_le16(recsize),
202                 .vr_ptrsize          = cpu_to_le16(ptrsize),
203                 .vr_indirect_levels  = 0
204         };
205
206         limit = (void *)(root + 1);
207         *limit = (typeof(*limit)) {
208                 /*
209                  * limit itself + one pointer to the leaf.
210                  */
211                 .count = cpu_to_le16(2),
212                 .limit = lvar_root_limit(blocksize, keysize + ptrsize)
213         };
214
215         entry = root + 1;
216         /*
217          * Skip over @limit.
218          */
219         entry += isize;
220
221         /*
222          * Entry format is <key> followed by <ptr>. In the minimal tree
223          * consisting of a root and single node, <key> is a minimal possible
224          * key.
225          *
226          * XXX: this key is hard-coded to be a sequence of 0's.
227          */
228         entry += sizeof(lvar_hash_t);
229         /* now @entry points to <ptr> */
230         if (ptrsize == 4)
231                 *(u_int32_t *)entry = cpu_to_le32(1);
232         else
233                 *(u_int64_t *)entry = cpu_to_le64(1);
234 }
235
236 static int lvar_esize(int namelen, int recsize)
237 {
238         return (offsetof(struct lvar_leaf_entry, vle_key) +
239                 namelen + recsize + LVAR_ROUND) & ~LVAR_ROUND;
240 }
241
242 static void lvar_leaf(void *buf,
243                       int blocksize, int keysize, int ptrsize, int recsize)
244 {
245         struct lvar_leaf_header *head;
246         char                    *rec;
247
248         /* form leaf */
249         head = buf;
250         *head = (typeof(*head)) {
251                 .vlh_magic = cpu_to_le16(IAM_LVAR_LEAF_MAGIC),
252                 .vlh_used  = cpu_to_le16(sizeof *head + lvar_esize(0, recsize))
253         };
254         rec = (void *)(head + 1);
255         rec[offsetof(struct lvar_leaf_entry, vle_key)] = recsize;
256 }
257
258
259 struct iam_uapi_op {
260         void *iul_key;
261         void *iul_rec;
262 };
263
264 struct iam_uapi_it {
265         struct iam_uapi_op iui_op;
266         __u16              iui_state;
267 };
268
269 enum iam_ioctl_cmd {
270         IAM_IOC_INIT      = _IOW('i', 1, struct iam_uapi_info),
271         IAM_IOC_GETINFO   = _IOR('i', 2, struct iam_uapi_info),
272         IAM_IOC_INSERT    = _IOR('i', 3, struct iam_uapi_op),
273         IAM_IOC_LOOKUP    = _IOWR('i', 4, struct iam_uapi_op),
274         IAM_IOC_DELETE    = _IOR('i', 5, struct iam_uapi_op),
275         IAM_IOC_IT_START  = _IOR('i', 6, struct iam_uapi_it),
276         IAM_IOC_IT_NEXT   = _IOW('i', 7, struct iam_uapi_it),
277         IAM_IOC_IT_STOP   = _IOR('i', 8, struct iam_uapi_it),
278         IAM_IOC_POLYMORPH = _IOR('i', 9, unsigned long)
279 };
280
281 static unsigned char hex2dec(unsigned char hex)
282 {
283         if (('0' <= hex) && (hex <= '9'))
284                 return hex - '0';
285         else if (('a' <= hex) && (hex <= 'f'))
286                 return hex - 'a' + 10;
287         else if (('A' <= hex) && (hex <= 'F'))
288                 return hex - 'A' + 10;
289         else
290                 exit(1);
291 }
292
293 static unsigned char *packdigit(unsigned char *number)
294 {
295         unsigned char *area;
296         unsigned char *scan;
297
298         area = calloc(strlen(number) / 2 + 2, sizeof(char));
299         if (area != NULL) {
300                 for (scan = area; *number; number += 2, scan++)
301                         *scan = (hex2dec(number[0]) << 4) | hex2dec(number[1]);
302         }
303         return area;
304 }
305
306 static char *iam_convert(int size, int need_convert, char *source)
307 {
308         char *ptr;
309         char *opt;
310
311         if (source == NULL)
312                 return NULL;
313
314         ptr = calloc(size + 1, sizeof(char));
315         if (ptr == NULL)
316                 return NULL;
317
318         if (need_convert) {
319                 opt = packdigit(source);
320                 if (opt == NULL) {
321                         free(ptr);
322                         return NULL;
323                 } else {
324                         memcpy(ptr, opt, size + 1);
325                         free(opt);
326                 }
327         } else {
328                 strncpy(ptr, source, size + 1);
329         }
330
331         return ptr;
332 }
333
334 static int iam_doop(int fd, struct iam_uapi_info *ua, int cmd,
335                     int key_need_convert, char *key_buf,
336                     int *keysize, char *save_key,
337                     int rec_need_convert, char *rec_buf,
338                     int *recsize, char *save_rec)
339 {
340         int ret;
341         char *key;
342         char *rec;
343         struct iam_uapi_op op;
344
345         key = iam_convert(ua->iui_keysize, key_need_convert, key_buf);
346         if (key == NULL)
347                 return -1;
348
349         rec = iam_convert(ua->iui_recsize, rec_need_convert, rec_buf);
350         if (rec == NULL) {
351                 free(key);
352                 return -1;
353         }
354
355         op.iul_key = key;
356         op.iul_rec = rec;
357         ret = ioctl(fd, cmd, &op);
358         if (ret == 0) {
359                 if ((keysize != NULL) && (*keysize > 0) && (save_key != NULL)) {
360                         if (*keysize > ua->iui_keysize)
361                                 *keysize = ua->iui_keysize;
362                         memcpy(save_key, key, *keysize);
363                 }
364                 if ((recsize != NULL) && (*recsize > 0) && (save_rec != NULL)) {
365                         if (*recsize > ua->iui_recsize)
366                                 *recsize = ua->iui_recsize;
367                         memcpy(save_rec, rec, *recsize);
368                 }
369         }
370         free(key);
371         free(rec);
372         return ret;
373 }
374
375
376 /*
377  * Creat an iam file, but do NOT open it.
378  * Return 0 if success, else -1.
379  */
380 int iam_creat(char *filename, enum iam_fmt_t fmt,
381               int blocksize, int keysize, int recsize, int ptrsize)
382 {
383         int fd;
384         char *buf;
385
386         if (filename == NULL) {
387                 errno = EINVAL;
388                 return -1;
389         }
390
391         if ((fmt != FMT_LFIX) && (fmt != FMT_LVAR)) {
392                 errno = EOPNOTSUPP;
393                 return -1;
394         }
395
396         if (blocksize <= 100) {
397                 errno = EINVAL;
398                 return -1;
399         }
400
401         if (keysize < 1) {
402                 errno = EINVAL;
403                 return -1;
404         }
405
406         if (recsize < 0) {
407                 errno = EINVAL;
408                 return -1;
409         }
410
411         if (ptrsize != 4 && ptrsize != 8) {
412                 errno = EINVAL;
413                 return -1;
414         }
415
416         if (keysize + recsize + sizeof(struct iam_leaf_head) > blocksize / 3) {
417                 errno = EINVAL;
418                 return -1;
419         }
420
421         fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0600);
422         if (fd < 0) {
423                 return -1;
424         }
425
426         buf = malloc(blocksize);
427         if (buf == NULL) {
428                 close(fd);
429                 return -1;
430         }
431
432         memset(buf, 0, blocksize);
433         if (fmt == FMT_LFIX)
434                 lfix_root(buf, blocksize, keysize, ptrsize, recsize);
435         else
436                 lvar_root(buf, blocksize, keysize, ptrsize, recsize);
437
438         if (write(fd, buf, blocksize) != blocksize) {
439                 close(fd);
440                 free(buf);
441                 return -1;
442         }
443
444         memset(buf, 0, blocksize);
445         if (fmt == FMT_LFIX)
446                 lfix_leaf(buf, blocksize, keysize, ptrsize, recsize);
447         else
448                 lvar_leaf(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         close(fd);
457         free(buf);
458         return 0;
459 }
460
461 /*
462  * Open an iam file, but do NOT creat it if the file doesn't exist.
463  * Please use iam_creat for creating the file before use iam_open.
464  * Return file id (fd) if success, else -1.
465  */
466 int iam_open(char *filename, struct iam_uapi_info *ua)
467 {
468         int fd;
469
470         if (filename == NULL) {
471                 errno = EINVAL;
472                 return -1;
473         }
474
475         if (ua == NULL) {
476                 errno = EINVAL;
477                 return -1;
478         }
479
480         fd = open(filename, O_RDONLY);
481         if (fd < 0) {
482                 return -1;
483         }
484
485         if (ioctl(fd, IAM_IOC_INIT, ua) != 0) {
486                 close(fd);
487                 return -1;
488         }
489
490         if (ioctl(fd, IAM_IOC_GETINFO, ua) != 0) {
491                 close(fd);
492                 return -1;
493         }
494
495         return fd;
496 }
497
498 /*
499  * Close file opened by iam_open.
500  */
501 int iam_close(int fd)
502 {
503         return close(fd);
504 }
505
506 /*
507  * Please use iam_open before use this function.
508  */
509 int iam_insert(int fd, struct iam_uapi_info *ua,
510                int key_need_convert, char *key_buf,
511                int rec_need_convert, char *rec_buf)
512 {
513         return iam_doop(fd, ua, IAM_IOC_INSERT,
514                         key_need_convert, key_buf, NULL, NULL,
515                         rec_need_convert, rec_buf, NULL, NULL);
516 }
517
518 /*
519  * Please use iam_open before use this function.
520  */
521 int iam_lookup(int fd, struct iam_uapi_info *ua,
522                int key_need_convert, char *key_buf,
523                int *keysize, char *save_key,
524                int rec_need_convert, char *rec_buf,
525                int *recsize, char *save_rec)
526 {
527         return iam_doop(fd, ua, IAM_IOC_LOOKUP,
528                        key_need_convert, key_buf, keysize, save_key,
529                        rec_need_convert, rec_buf, recsize, save_rec);
530 }
531
532 /*
533  * Please use iam_open before use this function.
534  */
535 int iam_delete(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_DELETE,
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_it_start(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_IT_START,
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_it_next(int fd, struct iam_uapi_info *ua,
562                 int key_need_convert, char *key_buf,
563                 int *keysize, char *save_key,
564                 int rec_need_convert, char *rec_buf,
565                 int *recsize, char *save_rec)
566 {
567         return iam_doop(fd, ua, IAM_IOC_IT_NEXT,
568                        key_need_convert, key_buf, keysize, save_key,
569                        rec_need_convert, rec_buf, recsize, save_rec);
570 }
571
572 /*
573  * Please use iam_open before use this function.
574  */
575 int iam_it_stop(int fd, struct iam_uapi_info *ua,
576                 int key_need_convert, char *key_buf,
577                 int rec_need_convert, char *rec_buf)
578 {
579         return iam_doop(fd, ua, IAM_IOC_IT_STOP,
580                         key_need_convert, key_buf, NULL, NULL,
581                         rec_need_convert, rec_buf, NULL, NULL);
582 }
583
584 /*
585  * Change iam file mode.
586  */
587 int iam_polymorph(char *filename, unsigned long mode)
588 {
589         int fd;
590         int ret;
591
592         if (filename == NULL) {
593                 errno = EINVAL;
594                 return -1;
595         }
596
597         fd = open(filename, O_RDONLY);
598         if (fd < 0) {
599                 return -1;
600         }
601
602         ret = ioctl(fd, IAM_IOC_POLYMORPH, mode);
603         close(fd);
604         return ret;
605 }