tdb: enforce hashing, via example hash in old rwlocks entry in header.
[ccan] / ccan / tdb / test / lock-tracking.c
1 /* We save the locks so we can reaquire them. */
2 #include <unistd.h>
3 #include <fcntl.h>
4 #include <stdarg.h>
5 #include <stdlib.h>
6 #include <ccan/tap/tap.h>
7 #include <ccan/tdb/tdb_private.h>
8
9 struct lock {
10         struct lock *next;
11         unsigned int off;
12         unsigned int len;
13         int type;
14 };
15 static struct lock *locks;
16 int locking_errors = 0;
17 bool suppress_lockcheck = false;
18 void (*unlock_callback)(int fd);
19
20 int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
21 {
22         va_list ap;
23         int ret, arg3;
24         struct flock *fl;
25
26         if (cmd != F_SETLK && cmd != F_SETLKW) {
27                 /* This may be totally bogus, but we don't know in general. */
28                 va_start(ap, cmd);
29                 arg3 = va_arg(ap, int);
30                 va_end(ap);
31
32                 return fcntl(fd, cmd, arg3);
33         }
34
35         va_start(ap, cmd);
36         fl = va_arg(ap, struct flock *);
37         va_end(ap);
38
39         if (fl->l_type == F_UNLCK) {
40                 struct lock **l;
41                 struct lock *old = NULL;
42                         
43                 for (l = &locks; *l; l = &(*l)->next) {
44                         if ((*l)->off == fl->l_start
45                             && (*l)->len == fl->l_len) {
46                                 old = *l;
47                                 *l = (*l)->next;
48                                 free(old);
49                                 break;
50                         }
51                 }
52                 if (!old && !suppress_lockcheck) {
53                         diag("Unknown unlock %u@%u",
54                              (int)fl->l_len, (int)fl->l_start);
55                         locking_errors++;
56                 }
57         } else {
58                 struct lock *new, *i;
59                 unsigned int fl_end = fl->l_start + fl->l_len;
60                 if (fl->l_len == 0)
61                         fl_end = (unsigned int)-1;
62
63                 /* Check for overlaps: we shouldn't do this. */
64                 for (i = locks; i; i = i->next) {
65                         unsigned int i_end = i->off + i->len;
66                         if (i->len == 0)
67                                 i_end = (unsigned int)-1;
68
69                         if (fl->l_start >= i->off && fl->l_start < i_end)
70                                 break;
71                         if (fl_end >= i->off && fl_end < i_end)
72                                 break;
73
74                         /* tdb_allrecord_lock does this, handle adjacent: */
75                         if (fl->l_start == i_end && fl->l_type == i->type) {
76                                 i->len = fl->l_len ? i->len + fl->l_len : 0;
77                                 goto ok;
78                         }
79                 }
80                 if (i) {
81                         /* Special case: upgrade of allrecord lock. */
82                         if (i->type == F_RDLCK && fl->l_type == F_WRLCK
83                             && i->off == FREELIST_TOP
84                             && fl->l_start == FREELIST_TOP
85                             && i->len == 0
86                             && fl->l_len == 0) {
87                                 i->type = F_WRLCK;
88                                 goto ok;
89                         }
90                         if (!suppress_lockcheck) {
91                                 diag("%s lock %u@%u overlaps %u@%u",
92                                      fl->l_type == F_WRLCK ? "write" : "read",
93                                      (int)fl->l_len, (int)fl->l_start,
94                                      i->len, (int)i->off);
95                                 locking_errors++;
96                         }
97                 }
98                 new = malloc(sizeof *new);
99                 new->off = fl->l_start;
100                 new->len = fl->l_len;
101                 new->type = fl->l_type;
102                 new->next = locks;
103                 locks = new;
104         }
105 ok:
106         ret = fcntl(fd, cmd, fl);
107         if (ret == 0 && fl->l_type == F_UNLCK && unlock_callback)
108                 unlock_callback(fd);
109         return ret;
110 }
111
112 int forget_locking(void)
113 {
114         unsigned int num = 0;
115         while (locks) {
116                 struct lock *next = locks->next;
117                 free(locks);
118                 locks = next;
119                 num++;
120         }
121         return num;
122 }