tdb/test: avoid const warnings by using 'discard_const_p(uint8_t,' instead of '(void *)'
[samba.git] / lib / tdb / test / lock-tracking.c
1 /* We save the locks so we can reaquire them. */
2 #include "../common/tdb_private.h"
3 #include <unistd.h>
4 #include <fcntl.h>
5 #include <stdarg.h>
6 #include <stdlib.h>
7 #include "tap-interface.h"
8 #include "lock-tracking.h"
9
10 struct testlock {
11         struct testlock *next;
12         unsigned int off;
13         unsigned int len;
14         int type;
15 };
16 static struct testlock *testlocks;
17 int locking_errors = 0;
18 bool suppress_lockcheck = false;
19 bool nonblocking_locks;
20 int locking_would_block = 0;
21 void (*unlock_callback)(int fd);
22
23 int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ )
24 {
25         va_list ap;
26         int ret, arg3;
27         struct flock *fl;
28         bool may_block = false;
29
30         if (cmd != F_SETLK && cmd != F_SETLKW) {
31                 /* This may be totally bogus, but we don't know in general. */
32                 va_start(ap, cmd);
33                 arg3 = va_arg(ap, int);
34                 va_end(ap);
35
36                 return fcntl(fd, cmd, arg3);
37         }
38
39         va_start(ap, cmd);
40         fl = va_arg(ap, struct flock *);
41         va_end(ap);
42
43         if (cmd == F_SETLKW && nonblocking_locks) {
44                 cmd = F_SETLK;
45                 may_block = true;
46         }
47         ret = fcntl(fd, cmd, fl);
48
49         /* Detect when we failed, but might have been OK if we waited. */
50         if (may_block && ret == -1 && (errno == EAGAIN || errno == EACCES)) {
51                 locking_would_block++;
52         }
53
54         if (fl->l_type == F_UNLCK) {
55                 struct testlock **l;
56                 struct testlock *old = NULL;
57
58                 for (l = &testlocks; *l; l = &(*l)->next) {
59                         if ((*l)->off == fl->l_start
60                             && (*l)->len == fl->l_len) {
61                                 if (ret == 0) {
62                                         old = *l;
63                                         *l = (*l)->next;
64                                         free(old);
65                                 }
66                                 break;
67                         }
68                         if (((*l)->off == fl->l_start)
69                             && ((*l)->len == 0)
70                             && (ret == 0)) {
71                                 /*
72                                  * Remove a piece from the start of the
73                                  * allrecord_lock
74                                  */
75                                 old = *l;
76                                 (*l)->off += fl->l_len;
77                                 break;
78                         }
79                 }
80                 if (!old && !suppress_lockcheck) {
81                         diag("Unknown unlock %u@%u - %i",
82                              (int)fl->l_len, (int)fl->l_start, ret);
83                         locking_errors++;
84                 }
85         } else {
86                 struct testlock *new, *i;
87                 unsigned int fl_end = fl->l_start + fl->l_len;
88                 if (fl->l_len == 0)
89                         fl_end = (unsigned int)-1;
90
91                 /* Check for overlaps: we shouldn't do this. */
92                 for (i = testlocks; i; i = i->next) {
93                         unsigned int i_end = i->off + i->len;
94                         if (i->len == 0)
95                                 i_end = (unsigned int)-1;
96
97                         if (fl->l_start >= i->off && fl->l_start < i_end)
98                                 break;
99                         if (fl_end >= i->off && fl_end < i_end)
100                                 break;
101
102                         /* tdb_allrecord_lock does this, handle adjacent: */
103                         if (fl->l_start == i_end && fl->l_type == i->type) {
104                                 if (ret == 0) {
105                                         i->len = fl->l_len
106                                                 ? i->len + fl->l_len
107                                                 : 0;
108                                 }
109                                 goto done;
110                         }
111                 }
112                 if (i) {
113                         /* Special case: upgrade of allrecord lock. */
114                         if (i->type == F_RDLCK && fl->l_type == F_WRLCK
115                             && i->off == FREELIST_TOP
116                             && fl->l_start == FREELIST_TOP
117                             && i->len == 0
118                             && fl->l_len == 0) {
119                                 if (ret == 0)
120                                         i->type = F_WRLCK;
121                                 goto done;
122                         }
123                         if (!suppress_lockcheck) {
124                                 diag("%s testlock %u@%u overlaps %u@%u",
125                                      fl->l_type == F_WRLCK ? "write" : "read",
126                                      (int)fl->l_len, (int)fl->l_start,
127                                      i->len, (int)i->off);
128                                 locking_errors++;
129                         }
130                 }
131
132                 if (ret == 0) {
133                         new = malloc(sizeof *new);
134                         new->off = fl->l_start;
135                         new->len = fl->l_len;
136                         new->type = fl->l_type;
137                         new->next = testlocks;
138                         testlocks = new;
139                 }
140         }
141 done:
142         if (ret == 0 && fl->l_type == F_UNLCK && unlock_callback)
143                 unlock_callback(fd);
144         return ret;
145 }
146
147 unsigned int forget_locking(void)
148 {
149         unsigned int num = 0;
150         while (testlocks) {
151                 struct testlock *next = testlocks->next;
152                 free(testlocks);
153                 testlocks = next;
154                 num++;
155         }
156         return num;
157 }