- /* okay, we might have ranges byte-locked. we need to make sure that
- * we restore the appropriate ranges once we're done. so, we check
- * for overlap on an unlock and relock.
- * XXX: in the future, all the byte locks will be sorted and contiguous.
- * we just want to upgrade all the locks and then downgrade them
- * here. */
- if (!adf->adf_excl) {
- err = fcntl(adf->adf_fd, F_SETLK, &lock);
- }
- else {
- err = 0;
- }
- if (!err && (lock.l_type == F_UNLCK))
- adf_relockrange(adf, adf->adf_fd, lock.l_start, len);
+ /* byte_lock(len=-1) lock whole file */
+ if (len == BYTELOCK_MAX) {
+ lock.l_len -= lock.l_start; /* otherwise EOVERFLOW error */
+ }
+
+ /* see if it's locked by another fork.
+ * NOTE: this guarantees that any existing locks must be at most
+ * read locks. we use ADLOCK_WR/RD because F_RD/WRLCK aren't
+ * guaranteed to be ORable. */
+ if (adf_findxlock(adf, fork, ADLOCK_WR |
+ ((type & ADLOCK_WR) ? ADLOCK_RD : 0),
+ lock.l_start, lock.l_len) > -1) {
+ errno = EACCES;
+ return -1;
+ }
+
+ /* look for any existing lock that we may have */
+ i = adf_findlock(adf, fork, ADLOCK_RD | ADLOCK_WR, lock.l_start, lock.l_len);
+ adflock = (i < 0) ? NULL : adf->adf_lock + i;
+
+ /* here's what we check for:
+ 1) we're trying to re-lock a lock, but we didn't specify an update.
+ 2) we're trying to free only part of a lock.
+ 3) we're trying to free a non-existent lock. */
+ if ( (!adflock && (lock.l_type == F_UNLCK))
+ ||
+ (adflock
+ && !(type & ADLOCK_UPGRADE)
+ && ((lock.l_type != F_UNLCK)
+ || (adflock->lock.l_start != lock.l_start)
+ || (adflock->lock.l_len != lock.l_len) ))
+ ) {
+ errno = EINVAL;
+ return -1;
+ }
+
+
+ /* now, update our list of locks */
+ /* clear the lock */
+ if (lock.l_type == F_UNLCK) {
+ adf_freelock(adf, i);
+ return 0;
+ }
+
+ /* attempt to lock the file. */
+ if (set_lock(adf->adf_fd, F_SETLK, &lock) < 0)
+ return -1;
+
+ /* we upgraded this lock. */
+ if (adflock && (type & ADLOCK_UPGRADE)) {
+ memcpy(&adflock->lock, &lock, sizeof(lock));
+ return 0;
+ }
+
+ /* it wasn't an upgrade */
+ oldlock = -1;
+ if (lock.l_type == F_RDLCK) {
+ oldlock = adf_findxlock(adf, fork, ADLOCK_RD, lock.l_start, lock.l_len);
+ }
+
+ /* no more space. this will also happen if lockmax == lockcount == 0 */
+ if (adf->adf_lockmax == adf->adf_lockcount) {
+ adf_lock_t *tmp = (adf_lock_t *)
+ realloc(adf->adf_lock, sizeof(adf_lock_t)*
+ (adf->adf_lockmax + ARRAY_BLOCK_SIZE));
+ if (!tmp)
+ goto fcntl_lock_err;
+ adf->adf_lock = tmp;
+ adf->adf_lockmax += ARRAY_BLOCK_SIZE;
+ }
+ adflock = adf->adf_lock + adf->adf_lockcount;
+
+ /* fill in fields */
+ memcpy(&adflock->lock, &lock, sizeof(lock));
+ adflock->user = fork;
+ if (oldlock > -1) {
+ adflock->refcount = (adf->adf_lock + oldlock)->refcount;
+ } else if ((adflock->refcount = calloc(1, sizeof(int))) == NULL) {
+ goto fcntl_lock_err;
+ }
+
+ (*adflock->refcount)++;
+ adf->adf_lockcount++;
+ return 0;