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;
do_operation(enum operation op,const char * name)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
prepare_external_agent(void)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 }
shutdown_agent(struct agent * agent)158