From ab8ac5e4025ee6cffa5da63aec10277f0f9bb9c4 Mon Sep 17 00:00:00 2001
From: Pawel Dziepak <pdziepak@quarnos.org>
Date: Fri, 29 Jun 2012 17:33:10 +0200
Subject: [PATCH] Fix #8661: fcntl(fd, F_GETLK, ...) violates POSIX
The standard states that F_GETLK should check whether given lock would be
blocked by another one and return description of the conflicting one (or
set l_type to F_UNLCK if there is no collision).
Current implementation of F_GETLK performs completely different actions, it
"Retrieves the first lock that has been set by the current team". Moreover,
if there are no locks (advisory_locking == NULL) an error is returned
instead of l_type set to F_UNLCK.
---
src/system/kernel/fs/vfs.cpp | 81 +++++++++++++++++++++++++++---------------
1 file changed, 53 insertions(+), 28 deletions(-)
diff --git a/src/system/kernel/fs/vfs.cpp b/src/system/kernel/fs/vfs.cpp
index 6737a30..882c061 100644
a
|
b
|
create_advisory_locking(struct vnode* vnode)
|
1524 | 1524 | } |
1525 | 1525 | |
1526 | 1526 | |
1527 | | /*! Retrieves the first lock that has been set by the current team. |
| 1527 | /*! Returns \c true when either \a flock is \c NULL or the \a flock intersects |
| 1528 | with the advisory_lock \a lock. |
| 1529 | */ |
| 1530 | static bool |
| 1531 | advisory_lock_intersects(struct advisory_lock* lock, struct flock* flock) |
| 1532 | { |
| 1533 | if (flock == NULL) |
| 1534 | return true; |
| 1535 | |
| 1536 | return lock->start <= flock->l_start - 1 + flock->l_len |
| 1537 | && lock->end >= flock->l_start; |
| 1538 | } |
| 1539 | |
| 1540 | |
| 1541 | /*! Tests whether acquiring a lock would block. |
1528 | 1542 | */ |
1529 | 1543 | static status_t |
1530 | | get_advisory_lock(struct vnode* vnode, struct flock* flock) |
| 1544 | test_advisory_lock(struct vnode* vnode, struct flock* flock) |
1531 | 1545 | { |
| 1546 | flock->l_type = F_UNLCK; |
| 1547 | |
1532 | 1548 | struct advisory_locking* locking = get_advisory_locking(vnode); |
1533 | 1549 | if (locking == NULL) |
1534 | | return B_BAD_VALUE; |
| 1550 | return B_OK; |
1535 | 1551 | |
1536 | | // TODO: this should probably get the flock by its file descriptor! |
1537 | 1552 | team_id team = team_get_current_team_id(); |
1538 | | status_t status = B_BAD_VALUE; |
1539 | 1553 | |
| 1554 | // test for collisions |
1540 | 1555 | LockList::Iterator iterator = locking->locks.GetIterator(); |
1541 | 1556 | while (iterator.HasNext()) { |
1542 | 1557 | struct advisory_lock* lock = iterator.Next(); |
1543 | 1558 | |
1544 | | if (lock->team == team) { |
1545 | | flock->l_start = lock->start; |
1546 | | flock->l_len = lock->end - lock->start + 1; |
1547 | | status = B_OK; |
1548 | | break; |
| 1559 | if (lock->team != team && advisory_lock_intersects(lock, flock)) { |
| 1560 | // locks do overlap |
| 1561 | if (flock->l_type != F_RDLCK || !lock->shared) { |
| 1562 | // collision |
| 1563 | flock->l_type = lock->shared ? F_RDLCK : F_WRLCK; |
| 1564 | flock->l_whence = SEEK_SET; |
| 1565 | flock->l_start = lock->start; |
| 1566 | flock->l_len = lock->end - lock->start + 1; |
| 1567 | flock->l_pid = lock->team; |
| 1568 | |
| 1569 | break; |
| 1570 | } |
1549 | 1571 | } |
1550 | 1572 | } |
1551 | 1573 | |
1552 | 1574 | put_advisory_locking(locking); |
1553 | | return status; |
1554 | | } |
1555 | | |
1556 | | |
1557 | | /*! Returns \c true when either \a flock is \c NULL or the \a flock intersects |
1558 | | with the advisory_lock \a lock. |
1559 | | */ |
1560 | | static bool |
1561 | | advisory_lock_intersects(struct advisory_lock* lock, struct flock* flock) |
1562 | | { |
1563 | | if (flock == NULL) |
1564 | | return true; |
1565 | | |
1566 | | return lock->start <= flock->l_start - 1 + flock->l_len |
1567 | | && lock->end >= flock->l_start; |
| 1575 | return B_OK; |
1568 | 1576 | } |
1569 | 1577 | |
1570 | 1578 | |
… |
… |
common_fcntl(int fd, int op, uint32 argument, bool kernel)
|
6006 | 6014 | |
6007 | 6015 | case F_GETLK: |
6008 | 6016 | if (vnode != NULL) { |
6009 | | status = get_advisory_lock(vnode, &flock); |
| 6017 | struct flock tmp; |
| 6018 | memcpy(&tmp, &flock, sizeof(struct flock)); |
| 6019 | |
| 6020 | status = normalize_flock(descriptor, &tmp); |
| 6021 | if (status != B_OK) |
| 6022 | break; |
| 6023 | |
| 6024 | status = test_advisory_lock(vnode, &tmp); |
6010 | 6025 | if (status == B_OK) { |
6011 | 6026 | // copy back flock structure |
6012 | | status = user_memcpy((struct flock*)argument, &flock, |
6013 | | sizeof(struct flock)); |
| 6027 | if (tmp.l_type == F_UNLCK) { |
| 6028 | flock.l_type = F_UNLCK; |
| 6029 | |
| 6030 | status = user_memcpy((struct flock*)argument, &flock, |
| 6031 | sizeof(struct flock)); |
| 6032 | } else { |
| 6033 | if (tmp.l_len == OFF_MAX) |
| 6034 | tmp.l_len = 0; |
| 6035 | |
| 6036 | status = user_memcpy((struct flock*)argument, &tmp, |
| 6037 | sizeof(struct flock)); |
| 6038 | } |
6014 | 6039 | } |
6015 | 6040 | } else |
6016 | 6041 | status = B_BAD_VALUE; |