LCOV - code coverage report
Current view: top level - lib/tdb/test - lock-tracking.c (source / functions) Hit Total Coverage
Test: coverage report for master 2f515e9b Lines: 73 82 89.0 %
Date: 2024-04-21 15:09:00 Functions: 2 2 100.0 %

          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             : }

Generated by: LCOV version 1.14