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