Cygwin: lockf: Make lockf() return ENOLCK when too many locks
Previously, lockf() printed a warning message when the number of locks
per file exceeds the limit (MAX_LOCKF_CNT). This patch makes lockf()
return ENOLCK in that case rather than printing the warning message.
Addresses: https://cygwin.com/pipermail/cygwin/2024-October/256528.html
Fixes: 31390e4ca6
("(inode_t::get_all_locks_list): Use pre-allocated buffer in i_all_lf instead of allocating every lock. Return pointer to start of linked list of locks.")
Reported-by: Christian Franke <Christian.Franke@t-online.de>
Reviewed-by: Corinna Vinschen <corinna@vinschen.de>
Signed-off-by: Takashi Yano <takashi.yano@nifty.ne.jp>
This commit is contained in:
parent
f9cc21dc00
commit
ae181b0ff1
|
@ -297,6 +297,7 @@ class inode_t
|
||||||
HANDLE i_dir;
|
HANDLE i_dir;
|
||||||
HANDLE i_mtx;
|
HANDLE i_mtx;
|
||||||
uint32_t i_cnt; /* # of threads referencing this instance. */
|
uint32_t i_cnt; /* # of threads referencing this instance. */
|
||||||
|
uint32_t i_lock_cnt; /* # of locks for this file */
|
||||||
|
|
||||||
public:
|
public:
|
||||||
inode_t (dev_t dev, ino_t ino);
|
inode_t (dev_t dev, ino_t ino);
|
||||||
|
@ -321,6 +322,8 @@ class inode_t
|
||||||
void unlock_and_remove_if_unused ();
|
void unlock_and_remove_if_unused ();
|
||||||
|
|
||||||
lockf_t *get_all_locks_list ();
|
lockf_t *get_all_locks_list ();
|
||||||
|
uint32_t get_lock_count () /* needs get_all_locks_list() */
|
||||||
|
{ return i_lock_cnt; }
|
||||||
|
|
||||||
bool del_my_locks (long long id, HANDLE fhdl);
|
bool del_my_locks (long long id, HANDLE fhdl);
|
||||||
};
|
};
|
||||||
|
@ -503,7 +506,8 @@ inode_t::get (dev_t dev, ino_t ino, bool create_if_missing, bool lock)
|
||||||
}
|
}
|
||||||
|
|
||||||
inode_t::inode_t (dev_t dev, ino_t ino)
|
inode_t::inode_t (dev_t dev, ino_t ino)
|
||||||
: i_lockf (NULL), i_all_lf (NULL), i_dev (dev), i_ino (ino), i_cnt (0L)
|
: i_lockf (NULL), i_all_lf (NULL), i_dev (dev), i_ino (ino), i_cnt (0L),
|
||||||
|
i_lock_cnt (0)
|
||||||
{
|
{
|
||||||
HANDLE parent_dir;
|
HANDLE parent_dir;
|
||||||
WCHAR name[48];
|
WCHAR name[48];
|
||||||
|
@ -610,17 +614,16 @@ inode_t::get_all_locks_list ()
|
||||||
dbi->ObjectName.Buffer[LOCK_OBJ_NAME_LEN] = L'\0';
|
dbi->ObjectName.Buffer[LOCK_OBJ_NAME_LEN] = L'\0';
|
||||||
if (!newlock.from_obj_name (this, &i_all_lf, dbi->ObjectName.Buffer))
|
if (!newlock.from_obj_name (this, &i_all_lf, dbi->ObjectName.Buffer))
|
||||||
continue;
|
continue;
|
||||||
if (lock - i_all_lf >= MAX_LOCKF_CNT)
|
/* This should not be happen. The number of locks is limitted
|
||||||
{
|
in lf_setlock() and lf_clearlock() so that it does not
|
||||||
system_printf ("Warning, can't handle more than %d locks per file.",
|
exceed MAX_LOCKF_CNT. */
|
||||||
MAX_LOCKF_CNT);
|
assert (lock - i_all_lf < MAX_LOCKF_CNT);
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (lock > i_all_lf)
|
if (lock > i_all_lf)
|
||||||
lock[-1].lf_next = lock;
|
lock[-1].lf_next = lock;
|
||||||
new (lock++) lockf_t (newlock);
|
new (lock++) lockf_t (newlock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
i_lock_cnt = lock - i_all_lf;
|
||||||
/* If no lock has been found, return NULL. */
|
/* If no lock has been found, return NULL. */
|
||||||
if (lock == i_all_lf)
|
if (lock == i_all_lf)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -1190,6 +1193,12 @@ restart: /* Entry point after a restartable signal came in. */
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The total number of locks shall not exceed MAX_LOCKF_CNT.
|
||||||
|
If once it exceeds, lf_fildoverlap() cannot work correctly.
|
||||||
|
Therefore, lf_setlock() and lf_clearlock() control the
|
||||||
|
total number of locks not to exceed MAX_LOCKF_CNT. When
|
||||||
|
they detect that the operation will cause excess, they
|
||||||
|
return ENOCLK. */
|
||||||
/*
|
/*
|
||||||
* Set a byte-range lock.
|
* Set a byte-range lock.
|
||||||
*/
|
*/
|
||||||
|
@ -1346,14 +1355,31 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
|
||||||
*
|
*
|
||||||
* Handle any locks that overlap.
|
* Handle any locks that overlap.
|
||||||
*/
|
*/
|
||||||
|
node->get_all_locks_list (); /* Update lock count */
|
||||||
|
uint32_t lock_cnt = node->get_lock_count ();
|
||||||
|
/* lf_clearlock() sometimes increases the number of locks. Without
|
||||||
|
this room, the unlocking will never succeed in some situation. */
|
||||||
|
const uint32_t room_for_clearlock = 2;
|
||||||
|
const int incr[] = {1, 1, 2, 2, 3, 2};
|
||||||
|
int decr = 0;
|
||||||
|
|
||||||
prev = head;
|
prev = head;
|
||||||
block = *head;
|
block = *head;
|
||||||
needtolink = 1;
|
needtolink = 1;
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
ovcase = lf_findoverlap (block, lock, SELF, &prev, &overlap);
|
ovcase = lf_findoverlap (block, lock, SELF, &prev, &overlap);
|
||||||
|
/* Estimate the maximum increase in number of the locks that
|
||||||
|
can occur here. If this possibly exceeds the MAX_LOCKF_CNT,
|
||||||
|
return ENOLCK. */
|
||||||
if (ovcase)
|
if (ovcase)
|
||||||
|
{
|
||||||
block = overlap->lf_next;
|
block = overlap->lf_next;
|
||||||
|
HANDLE ov_obj = overlap->lf_obj;
|
||||||
|
decr = (ov_obj && get_obj_handle_count (ov_obj) == 1) ? 1 : 0;
|
||||||
|
}
|
||||||
|
if (needtolink)
|
||||||
|
lock_cnt += incr[ovcase] - decr;
|
||||||
/*
|
/*
|
||||||
* Six cases:
|
* Six cases:
|
||||||
* 0) no overlap
|
* 0) no overlap
|
||||||
|
@ -1368,6 +1394,8 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
|
||||||
case 0: /* no overlap */
|
case 0: /* no overlap */
|
||||||
if (needtolink)
|
if (needtolink)
|
||||||
{
|
{
|
||||||
|
if (lock_cnt > MAX_LOCKF_CNT - room_for_clearlock)
|
||||||
|
return ENOLCK;
|
||||||
*prev = lock;
|
*prev = lock;
|
||||||
lock->lf_next = overlap;
|
lock->lf_next = overlap;
|
||||||
lock->create_lock_obj ();
|
lock->create_lock_obj ();
|
||||||
|
@ -1380,6 +1408,8 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
|
||||||
* able to acquire it.
|
* able to acquire it.
|
||||||
* Cygwin: Always wake lock.
|
* Cygwin: Always wake lock.
|
||||||
*/
|
*/
|
||||||
|
if (lock_cnt > MAX_LOCKF_CNT - room_for_clearlock)
|
||||||
|
return ENOLCK;
|
||||||
lf_wakelock (overlap, fhdl);
|
lf_wakelock (overlap, fhdl);
|
||||||
overlap->lf_type = lock->lf_type;
|
overlap->lf_type = lock->lf_type;
|
||||||
overlap->create_lock_obj ();
|
overlap->create_lock_obj ();
|
||||||
|
@ -1397,6 +1427,11 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
|
||||||
*clean = lock;
|
*clean = lock;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (overlap->lf_start < lock->lf_start
|
||||||
|
&& overlap->lf_end > lock->lf_end)
|
||||||
|
lock_cnt++;
|
||||||
|
if (lock_cnt > MAX_LOCKF_CNT - room_for_clearlock)
|
||||||
|
return ENOLCK;
|
||||||
if (overlap->lf_start == lock->lf_start)
|
if (overlap->lf_start == lock->lf_start)
|
||||||
{
|
{
|
||||||
*prev = lock;
|
*prev = lock;
|
||||||
|
@ -1413,6 +1448,8 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 3: /* lock contains overlap */
|
case 3: /* lock contains overlap */
|
||||||
|
if (needtolink && lock_cnt > MAX_LOCKF_CNT - room_for_clearlock)
|
||||||
|
return ENOLCK;
|
||||||
/*
|
/*
|
||||||
* If downgrading lock, others may be able to
|
* If downgrading lock, others may be able to
|
||||||
* acquire it, otherwise take the list.
|
* acquire it, otherwise take the list.
|
||||||
|
@ -1440,6 +1477,8 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
|
||||||
/*
|
/*
|
||||||
* Add lock after overlap on the list.
|
* Add lock after overlap on the list.
|
||||||
*/
|
*/
|
||||||
|
if (lock_cnt > MAX_LOCKF_CNT - room_for_clearlock)
|
||||||
|
return ENOLCK;
|
||||||
lock->lf_next = overlap->lf_next;
|
lock->lf_next = overlap->lf_next;
|
||||||
overlap->lf_next = lock;
|
overlap->lf_next = lock;
|
||||||
overlap->lf_end = lock->lf_start - 1;
|
overlap->lf_end = lock->lf_start - 1;
|
||||||
|
@ -1456,6 +1495,8 @@ lf_setlock (lockf_t *lock, inode_t *node, lockf_t **clean, HANDLE fhdl)
|
||||||
*/
|
*/
|
||||||
if (needtolink)
|
if (needtolink)
|
||||||
{
|
{
|
||||||
|
if (lock_cnt > MAX_LOCKF_CNT - room_for_clearlock)
|
||||||
|
return ENOLCK;
|
||||||
*prev = lock;
|
*prev = lock;
|
||||||
lock->lf_next = overlap;
|
lock->lf_next = overlap;
|
||||||
lock->create_lock_obj ();
|
lock->create_lock_obj ();
|
||||||
|
@ -1483,12 +1524,35 @@ lf_clearlock (lockf_t *unlock, lockf_t **clean, HANDLE fhdl)
|
||||||
lockf_t *lf = *head;
|
lockf_t *lf = *head;
|
||||||
lockf_t *overlap, **prev;
|
lockf_t *overlap, **prev;
|
||||||
int ovcase;
|
int ovcase;
|
||||||
|
inode_t *node = lf->lf_inode;
|
||||||
|
tmp_pathbuf tp;
|
||||||
|
node->i_all_lf = (lockf_t *) tp.w_get ();
|
||||||
|
node->get_all_locks_list (); /* Update lock count */
|
||||||
|
uint32_t lock_cnt = node->get_lock_count ();
|
||||||
|
bool first_loop = true;
|
||||||
|
|
||||||
if (lf == NOLOCKF)
|
if (lf == NOLOCKF)
|
||||||
return 0;
|
return 0;
|
||||||
prev = head;
|
prev = head;
|
||||||
while ((ovcase = lf_findoverlap (lf, unlock, SELF, &prev, &overlap)))
|
while ((ovcase = lf_findoverlap (lf, unlock, SELF, &prev, &overlap)))
|
||||||
{
|
{
|
||||||
|
/* Estimate the maximum increase in number of the locks that
|
||||||
|
can occur here. If this possibly exceeds the MAX_LOCKF_CNT,
|
||||||
|
return ENOLCK. */
|
||||||
|
HANDLE ov_obj = overlap->lf_obj;
|
||||||
|
if (first_loop)
|
||||||
|
{
|
||||||
|
const int incr[] = {0, 0, 1, 1, 2, 1};
|
||||||
|
int decr = (ov_obj && get_obj_handle_count (ov_obj) == 1) ? 1 : 0;
|
||||||
|
lock_cnt += incr[ovcase] - decr;
|
||||||
|
if (ovcase == 2
|
||||||
|
&& overlap->lf_start < unlock->lf_start
|
||||||
|
&& overlap->lf_end > unlock->lf_end)
|
||||||
|
lock_cnt++;
|
||||||
|
if (lock_cnt > MAX_LOCKF_CNT)
|
||||||
|
return ENOLCK;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wakeup the list of locks to be retried.
|
* Wakeup the list of locks to be retried.
|
||||||
*/
|
*/
|
||||||
|
@ -1521,6 +1585,7 @@ lf_clearlock (lockf_t *unlock, lockf_t **clean, HANDLE fhdl)
|
||||||
lf = overlap->lf_next;
|
lf = overlap->lf_next;
|
||||||
overlap->lf_next = *clean;
|
overlap->lf_next = *clean;
|
||||||
*clean = overlap;
|
*clean = overlap;
|
||||||
|
first_loop = false;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case 4: /* overlap starts before lock */
|
case 4: /* overlap starts before lock */
|
||||||
|
@ -1528,6 +1593,7 @@ lf_clearlock (lockf_t *unlock, lockf_t **clean, HANDLE fhdl)
|
||||||
prev = &overlap->lf_next;
|
prev = &overlap->lf_next;
|
||||||
lf = overlap->lf_next;
|
lf = overlap->lf_next;
|
||||||
overlap->create_lock_obj ();
|
overlap->create_lock_obj ();
|
||||||
|
first_loop = false;
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case 5: /* overlap ends after lock */
|
case 5: /* overlap ends after lock */
|
||||||
|
|
|
@ -16,3 +16,7 @@ Fixes:
|
||||||
- Fix lockf() error which occurs when adding a new lock over multiple
|
- Fix lockf() error which occurs when adding a new lock over multiple
|
||||||
locks.
|
locks.
|
||||||
Addresses: https://cygwin.com/pipermail/cygwin/2024-October/256528.html
|
Addresses: https://cygwin.com/pipermail/cygwin/2024-October/256528.html
|
||||||
|
|
||||||
|
- Make lockf() return ENOLCK when the number of locks exceeds
|
||||||
|
MAX_LOCKF_CNT rather than printing a warning message.
|
||||||
|
Addresses: https://cygwin.com/pipermail/cygwin/2024-October/256528.html
|
||||||
|
|
Loading…
Reference in New Issue