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