Line data Source code
1 : /* We save the locks so we can reacquire 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 2712 : int fcntl_with_lockcheck(int fd, int cmd, ... /* arg */ ) 24 : { 25 : va_list ap; 26 : int ret, arg3; 27 : struct flock *fl; 28 2712 : bool may_block = false; 29 : 30 2712 : if (cmd != F_SETLK && cmd != F_SETLKW) { 31 : /* This may be totally bogus, but we don't know in general. */ 32 414 : va_start(ap, cmd); 33 414 : arg3 = va_arg(ap, int); 34 414 : va_end(ap); 35 : 36 414 : return fcntl(fd, cmd, arg3); 37 : } 38 : 39 2298 : va_start(ap, cmd); 40 2298 : fl = va_arg(ap, struct flock *); 41 2298 : va_end(ap); 42 : 43 2298 : if (cmd == F_SETLKW && nonblocking_locks) { 44 619 : cmd = F_SETLK; 45 619 : may_block = true; 46 : } 47 2298 : ret = fcntl(fd, cmd, fl); 48 : 49 : /* Detect when we failed, but might have been OK if we waited. */ 50 2298 : if (may_block && ret == -1 && (errno == EAGAIN || errno == EACCES)) { 51 27 : locking_would_block++; 52 : } 53 : 54 2298 : if (fl->l_type == F_UNLCK) { 55 : struct testlock **l; 56 1027 : struct testlock *old = NULL; 57 : 58 1155 : for (l = &testlocks; *l; l = &(*l)->next) { 59 1008 : if ((*l)->off == fl->l_start 60 880 : && (*l)->len == fl->l_len) { 61 880 : if (ret == 0) { 62 880 : old = *l; 63 880 : *l = (*l)->next; 64 880 : free(old); 65 : } 66 880 : break; 67 : } 68 128 : if (((*l)->off == fl->l_start) 69 0 : && ((*l)->len == 0) 70 0 : && (ret == 0)) { 71 : /* 72 : * Remove a piece from the start of the 73 : * allrecord_lock 74 : */ 75 0 : old = *l; 76 0 : (*l)->off += fl->l_len; 77 0 : break; 78 : } 79 : } 80 1027 : if (!old && !suppress_lockcheck) { 81 0 : diag("Unknown unlock %u@%u - %i", 82 : (int)fl->l_len, (int)fl->l_start, ret); 83 0 : locking_errors++; 84 : } 85 : } else { 86 : struct testlock *new, *i; 87 1271 : unsigned int fl_end = fl->l_start + fl->l_len; 88 1271 : if (fl->l_len == 0) 89 247 : fl_end = (unsigned int)-1; 90 : 91 : /* Check for overlaps: we shouldn't do this. */ 92 2091 : for (i = testlocks; i; i = i->next) { 93 1012 : unsigned int i_end = i->off + i->len; 94 1012 : if (i->len == 0) 95 144 : i_end = (unsigned int)-1; 96 : 97 1012 : if (fl->l_start >= i->off && fl->l_start < i_end) 98 67 : break; 99 945 : if (fl_end >= i->off && fl_end < i_end) 100 0 : break; 101 : 102 : /* tdb_allrecord_lock does this, handle adjacent: */ 103 945 : if (fl->l_start == i_end && fl->l_type == i->type) { 104 125 : if (ret == 0) { 105 125 : i->len = fl->l_len 106 0 : ? i->len + fl->l_len 107 : : 0; 108 : } 109 125 : goto done; 110 : } 111 : } 112 1146 : if (i) { 113 : /* Special case: upgrade of allrecord lock. */ 114 67 : if (i->type == F_RDLCK && fl->l_type == F_WRLCK 115 62 : && i->off == FREELIST_TOP 116 60 : && fl->l_start == FREELIST_TOP 117 60 : && i->len == 0 118 60 : && fl->l_len == 0) { 119 60 : if (ret == 0) 120 58 : i->type = F_WRLCK; 121 60 : goto done; 122 : } 123 7 : if (!suppress_lockcheck) { 124 7 : 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 7 : locking_errors++; 129 : } 130 : } 131 : 132 1086 : if (ret == 0) { 133 1059 : new = malloc(sizeof *new); 134 1059 : new->off = fl->l_start; 135 1059 : new->len = fl->l_len; 136 1059 : new->type = fl->l_type; 137 1059 : new->next = testlocks; 138 1059 : testlocks = new; 139 : } 140 : } 141 27 : done: 142 2298 : if (ret == 0 && fl->l_type == F_UNLCK && unlock_callback) 143 767 : unlock_callback(fd); 144 2289 : return ret; 145 : } 146 : 147 120 : unsigned int forget_locking(void) 148 : { 149 120 : unsigned int num = 0; 150 288 : while (testlocks) { 151 168 : struct testlock *next = testlocks->next; 152 168 : free(testlocks); 153 168 : testlocks = next; 154 168 : num++; 155 : } 156 120 : return num; 157 : }