FBSD: Handle F_UNLCK in VOP_ADVLOCK 79/12579/4
authorTim Creech <tcreech@tcreech.com>
Sun, 5 Mar 2017 23:17:23 +0000 (18:17 -0500)
committerBenjamin Kaduk <kaduk@mit.edu>
Fri, 23 Aug 2019 02:54:49 +0000 (22:54 -0400)
When a_fl->type is F_UNLCK, FreeBSD gives our VOP_ADVLOCK an a_op of
F_UNLCK, instead of F_SETLK like we expect. This causes afs_lockctl to
return EINVAL, since F_UNLCK isn't a normal fcntl lock op, and so
userspace requests to unlock fcntl-style locks always fail. This can
be seen, for example, when trying to use sqlite3 to access a database
that lives in afs.

This F_UNLCK behavior in FreeBSD seems a bit peculiar, but has been
around effectively forever (since 4.4BSD-Lite). So just work around
it.

[adeason@dson.org: minor style adjustments and commit message/comment
rewording.]

Change-Id: I8bfaff9274e40761aa291930430a08b83b524d1b
Reviewed-on: https://gerrit.openafs.org/12579
Reviewed-by: Tim Creech <tcreech@tcreech.com>
Tested-by: BuildBot <buildbot@rampaginggeek.com>
Reviewed-by: Benjamin Kaduk <kaduk@mit.edu>

src/afs/FBSD/osi_vnodeops.c

index df97f34..9ebf840 100644 (file)
@@ -1601,14 +1601,26 @@ afs_vop_advlock(ap)
                                 * int  a_flags;
                                 * } */ *ap;
 {
-    int error;
+    int error, a_op;
     struct ucred cr = *osi_curcred();
 
+    a_op = ap->a_op;
+    if (a_op == F_UNLCK) {
+       /*
+        * When a_fl->type is F_UNLCK, FreeBSD passes in an a_op of F_UNLCK.
+        * This is (confusingly) different than how you actually release a lock
+        * with fcntl(), which is done with an a_op of F_SETLK and an l_type of
+        * F_UNLCK. Pretend we were given an a_op of F_SETLK in this case,
+        * since this is what afs_lockctl expects.
+        */
+       a_op = F_SETLK;
+    }
+
     AFS_GLOCK();
     error =
        afs_lockctl(VTOAFS(ap->a_vp),
                ap->a_fl,
-               ap->a_op, &cr,
+               a_op, &cr,
                (int)(intptr_t)ap->a_id);       /* XXX: no longer unique! */
     AFS_GUNLOCK();
     return error;