Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 25 Apr 95 18:15:37 EST
From:      Stephen McKay <syssgm@devetir.qld.gov.au>
To:        current@FreeBSD.org
Cc:        syssgm@devetir.qld.gov.au
Subject:   fcntl F_SETLK backward compatibility hack implemented
Message-ID:  <199504250815.SAA15397@pandora.devetir.qld.gov.au>

next in thread | raw e-mail | index | archive | help
Well, since nobody objected :-) I went and implemented my proposed fix to
fcntl locking.  Here are patches to sys/fcntl.h and kern/kern_descrip.c
plus two test programs.  The 'break' program demonstrates how the scheme
can fail, and allows you to look at the pretty console warnings.
The ftest program sets a few random locks and checks for clashes.  If any
console messages turn up, it has failed.  I compile it on a 1.1.5 system
and a 2.0 system then run it like:

$ ./ftest.1.1.5 & ./ftest.2.0 ; sleep 3
048901919: 374: OK F_WRLCK SEEK_CUR -3722  6989 ==  2201 to  9189
048937427: 375: NO F_WRLCK SEEK_SET  6048  3952 ==  6048 to  9999
048940850: 375: -> F_WRLCK SEEK_SET  2201  6989 ==  2201 to  9189 PID:374
050970752: 375: OK F_WRLCK SEEK_SET  6048  3952 ==  6048 to  9999
051003186: 374: OK F_WRLCK SEEK_CUR    33  1554 ==  4191 to  5744
...

This shows a microsecond timestamp, the pid, success/failure/reason, and
the expanded flock structure.

I think the patch works well, and since it changes the real fcntl lock code
so little, it won't make things any worse!

Stephen.

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	diffs
#	Makefile
#	ftest.c
#	break.c
#
echo x - diffs
sed 's/^X//' >diffs << 'END-of-diffs'
X--- sys/fcntl.h.dist	Tue Aug  2 17:52:57 1994
X+++ sys/fcntl.h	Sun Apr 23 22:13:31 1995
X@@ -135,10 +135,13 @@
X #ifndef _POSIX_SOURCE
X #define	F_GETOWN	5		/* get SIGIO/SIGURG proc/pgrp */
X #define F_SETOWN	6		/* set SIGIO/SIGURG proc/pgrp */
X+#define	F_OGETLK	7		/* 4.3 backward compatible GETLK */
X+#define	F_OSETLK	8		/* 4.3 backward compatible SETLK */
X+#define	F_OSETLKW	9		/* 4.3 backward compatible SETLKW */
X #endif
X-#define	F_GETLK		7		/* get record locking information */
X-#define	F_SETLK		8		/* set record locking information */
X-#define	F_SETLKW	9		/* F_SETLK; wait if blocked */
X+#define	F_GETLK		10		/* get record locking information */
X+#define	F_SETLK		11		/* set record locking information */
X+#define	F_SETLKW	12		/* F_SETLK; wait if blocked */
X 
X /* file descriptor flags (F_GETFD, F_SETFD) */
X #define	FD_CLOEXEC	1		/* close-on-exec flag */
X@@ -164,6 +167,19 @@
X 	short	l_type;		/* lock type: read/write, etc. */
X 	short	l_whence;	/* type of l_start */
X };
X+
X+#ifndef _POSIX_SOURCE
X+/*
X+ * 4.3 backward compatible advisory lock structure.
X+ */
X+struct oflock {
X+	short	ol_type;	/* lock type: read/write, etc. */
X+	short	ol_whence;	/* type of l_start */
X+	long	ol_start;	/* starting offset */
X+	long	ol_len;		/* len = 0 means until end of file */
X+	short	ol_pid;		/* lock owner */
X+};
X+#endif
X 
X 
X #ifndef _POSIX_SOURCE
X--- kern/kern_descrip.c.dist	Mon Oct  3 03:35:11 1994
X+++ kern/kern_descrip.c	Tue Apr 25 18:07:52 1995
X@@ -263,6 +263,7 @@
X 		error = copyin((caddr_t)uap->arg, (caddr_t)&fl, sizeof (fl));
X 		if (error)
X 			return (error);
X+	f_setlk:
X 		if (fl.l_whence == SEEK_CUR)
X 			fl.l_start += fp->f_offset;
X 		switch (fl.l_type) {
X@@ -295,11 +296,138 @@
X 		error = copyin((caddr_t)uap->arg, (caddr_t)&fl, sizeof (fl));
X 		if (error)
X 			return (error);
X+	f_getlk:
X 		if (fl.l_whence == SEEK_CUR)
X 			fl.l_start += fp->f_offset;
X 		if ((error = VOP_ADVLOCK(vp,(caddr_t)p,F_GETLK,&fl,F_POSIX)))
X 			return (error);
X 		return (copyout((caddr_t)&fl, (caddr_t)uap->arg, sizeof (fl)));
X+
X+#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
X+
X+#define VALID_L_TYPE(t)	((t)==F_RDLCK || (t)==F_UNLCK || (t)==F_WRLCK)
X+#define VALID_WHENCE(w)	((w)==SEEK_SET || (w)==SEEK_CUR || (w)==SEEK_END)
X+
X+	/*
X+	 * Backward compatible fcntl lock calls weren't added when the
X+	 * flock structure was changed.  We have to use some guesswork
X+	 * to make up for this omission.
X+	 *
X+	 * Code by: Stephen McKay <syssgm@devetir.qld.gov.au>
X+	 */
X+	case F_OSETLKW:
X+	case F_OSETLK:
X+	case F_OGETLK: {
X+		int is_new = 1, is_old = 1;
X+		long start_high, len_high;
X+		struct oflock ofl;
X+
X+		if (fp->f_type != DTYPE_VNODE)
X+			return (EBADF);
X+		vp = (struct vnode *)fp->f_data;
X+
X+		/*
X+		 * Copy in the lock structure, being ultra cautious in case
X+		 * it is an old style structure at the end of memory.
X+		 */
X+		error = copyin((caddr_t)uap->arg, &fl, sizeof (fl));
X+		if (error) {
X+			error = copyin((caddr_t)uap->arg, &ofl, sizeof (ofl));
X+			if (error)
X+				return (error);
X+			is_new = 0;
X+		} else
X+			bcopy(&fl, &ofl, sizeof (ofl));
X+
X+		/*
X+		 * Since 1.1.5 and 2.0 binaries both use the old lock calls
X+		 * but with different flock structures, we have to guess
X+		 * which one we have been passed by using whatever consistency
X+		 * checks are available.
X+		 */
X+		if (!VALID_L_TYPE(fl.l_type) || !VALID_WHENCE(fl.l_whence))
X+			is_new = 0;
X+
X+		if (!VALID_L_TYPE(ofl.ol_type) || !VALID_WHENCE(ofl.ol_whence))
X+			is_old = 0;
X+
X+		if (fl.l_len < 0 || fl.l_whence == SEEK_SET && fl.l_start < 0)
X+			is_new = 0;
X+
X+		if (ofl.ol_len<0 || ofl.ol_whence==SEEK_SET && ofl.ol_start<0)
X+			is_old = 0;
X+
X+		if (fl.l_whence == SEEK_CUR && fl.l_start + fp->f_offset < 0)
X+			is_new = 0;
X+
X+		if (ofl.ol_whence==SEEK_CUR && ofl.ol_start + fp->f_offset < 0)
X+			is_old = 0;
X+
X+		start_high = fl.l_start >> 32;
X+		len_high = fl.l_len >> 32;
X+		if (start_high != 0 && start_high != -1 || len_high != 0)
X+			is_new = 0;
X+
X+		/*
X+		 * If we can't be sure, assume it is 1.1.5 or older, and
X+		 * be a little noisy about it, in case we are wrong.
X+		 *
X+		 * XXXX It may be even better to return an error here and
X+		 * make no uncertain guesses.
X+		 */
X+		if (is_old == is_new) {
X+			printf("pid %lu assuming old fcntl lock structure\n",
X+				(u_long)curproc->p_pid);
X+			is_new = 0;
X+			is_old = 1;
X+		}
X+
X+		if (is_old) {
X+			fl.l_type = ofl.ol_type;
X+			fl.l_whence = ofl.ol_whence;
X+			fl.l_start = ofl.ol_start;
X+			fl.l_len = ofl.ol_len;
X+			fl.l_pid = ofl.ol_pid;
X+		}
X+
X+		/*
X+		 * Now that we have the data in a new style flock structure
X+		 * most cases can be handled by jumping back into the normal
X+		 * locking code.  F_GETLK on an old style lock is the only
X+		 * exception.
X+		 */
X+		switch (uap->cmd) {
X+		case F_OSETLKW:
X+			flg |= F_WAIT;
X+			/* Fall into F_OSETLK */
X+
X+		case F_OSETLK:
X+			goto f_setlk;
X+
X+		case F_OGETLK:
X+			if (is_new)
X+			    goto f_getlk;
X+
X+			if (fl.l_whence == SEEK_CUR)
X+				fl.l_start += fp->f_offset;
X+			error = VOP_ADVLOCK(vp,(caddr_t)p,F_GETLK,&fl,F_POSIX);
X+			if (error)
X+				return (error);
X+
X+			ofl.ol_type = fl.l_type;
X+			ofl.ol_whence = fl.l_whence;
X+			ofl.ol_start = (long)fl.l_start;
X+			ofl.ol_len = (long)fl.l_len;
X+			ofl.ol_pid = (short)fl.l_pid;
X+
X+			return (copyout(&ofl, (caddr_t)uap->arg, sizeof (ofl)));
X+		}
X+	}
X+
X+#undef VALID_L_TYPE
X+#undef VALID_WHENCE
X+
X+#endif /* defined(COMPAT_43) || defined(COMPAT_SUNOS) */
X 
X 	default:
X 		return (EINVAL);
END-of-diffs
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
XCFLAGS=	-O2 -Wall
X
Xall: ftest break
X
Xftest: ftest.o
X	$(CC) -o ftest ftest.o
X
Xbreak: break.o
X	$(CC) -o break break.o
END-of-Makefile
echo x - ftest.c
sed 's/^X//' >ftest.c << 'END-of-ftest.c'
X/*
X * Simple tests to determine if the backward compatible fcntl lock
X * mechanism works.
X */
X
X#include <stdio.h>
X#include <stdlib.h>
X#include <unistd.h>
X#include <fcntl.h>
X#include <errno.h>
X#include <sys/time.h>
X
X#define FILE_SIZE	8192
X
Xchar junk[FILE_SIZE];
X
Xvoid lock_rnd(int fd);
Xvoid try_lock(int fd, struct flock *fl);
Xvoid unlock(int fd, struct flock *fl);
Xvoid log(char *msg, struct flock *fl, int do_pid);
X
X#define FALSE	0
X#define TRUE	1
X
X#define rnd(n)	(random()%(n))
X
Xoff_t where = 0;;
X
Xint
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X    {
X    int fd;
X    int i;
X    char *name = "TOAD";
X
X    srandom(getpid());
X
X    if ((fd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1)
X	perror(name), exit(1);
X
X    write(fd, junk, sizeof(junk));
X    lseek(fd, where, SEEK_SET);
X
X    for (i = 0; i < 5; i++)
X	lock_rnd(fd);
X
X    return 0;
X    }
X
X
Xvoid
Xlock_rnd(int fd)
X    {
X    struct flock fl;
X
X    fl.l_type = F_WRLCK;
X    switch (rnd(3))
X	{
X	case 0:
X	    fl.l_whence = SEEK_SET;
X	    fl.l_start = rnd(FILE_SIZE);
X	    fl.l_len = rnd(FILE_SIZE);
X	    break;
X
X	case 1:
X	    fl.l_whence = SEEK_CUR;
X	    where = lseek(fd, (off_t)rnd(FILE_SIZE), SEEK_SET);
X	    if (where < 0)
X		{
X		perror("lseek");
X		exit(1);
X		}
X	    fl.l_start = rnd(FILE_SIZE) - where;
X	    fl.l_len = rnd(FILE_SIZE);
X	    break;
X
X	case 2:
X	    fl.l_whence = SEEK_END;
X	    fl.l_start = rnd(FILE_SIZE) - FILE_SIZE;
X	    fl.l_len = rnd(FILE_SIZE);
X	    break;
X	}
X
X    try_lock(fd, &fl);
X    sleep(2);
X    unlock(fd, &fl);
X    }
X
X#define MAGIC1	0x9174e2fa
X#define MAGIC2	0x5d738fe2
X
Xvoid
Xtry_lock(int fd, struct flock *fl)
X    {
X    int set = F_SETLK;
X    volatile unsigned flank1 = MAGIC1;
X    volatile struct flock blocker;
X    volatile unsigned flank2 = MAGIC2;
X
X    for (;;)
X	{
X	if (fcntl(fd, set, fl) == 0)
X	    {
X	    log("OK", fl, FALSE);
X	    return;
X	    }
X	if (errno != EWOULDBLOCK)
X	    {
X	    perror("SETLK");
X	    exit(1);
X	    }
X	log("NO", fl, FALSE);
X	blocker = *fl;
X	if (fcntl(fd, F_GETLK, &blocker) < 0)
X	    {
X	    perror("GETLK");
X	    exit(1);
X	    }
X	log("->", (struct flock *)&blocker, TRUE);
X	if (flank1 != MAGIC1)
X	    {
X	    printf("GETLK overwrote flank1\n");
X	    exit(1);
X	    }
X	if (flank2 != MAGIC2)
X	    {
X	    printf("GETLK overwrote flank2\n");
X	    exit(1);
X	    }
X	set = F_SETLKW;
X	}
X    }
X
X
Xvoid
Xunlock(int fd, struct flock *fl)
X    {
X    struct flock x;
X
X    x = *fl;
X    x.l_type = F_UNLCK;
X    if (fcntl(fd, F_SETLK, &x) < 0)
X	{
X	perror("UNLCK");
X	exit(1);
X	}
X    }
X
X
Xchar *lock_names[] =
X    {
X    "", "F_RDLCK", "F_UNLCK", "F_WRLCK"
X    };
X
Xchar *seek_names[] =
X    {
X    "SEEK_SET", "SEEK_CUR", "SEEK_END"
X    };
X
Xvoid
Xlog(char *msg, struct flock *fl, int do_pid)
X    {
X    off_t start;
X    struct timeval tv;
X
X    gettimeofday(&tv, NULL);
X
X    printf("%09ld: ", (tv.tv_sec % 1000) * 1000000 + tv.tv_usec);
X    printf("%ld: %s", (long)getpid(), msg);
X    printf(" %s %s", lock_names[fl->l_type], seek_names[fl->l_whence]);
X    printf(" %5ld %5ld", (long)fl->l_start, (long)fl->l_len);
X    start = fl->l_start;
X    switch (fl->l_whence)
X	{
X	case SEEK_CUR:
X	    start += where;
X	    break;
X	case SEEK_END:
X	    start += FILE_SIZE;
X	    break;
X	}
X    printf(" == %5ld to %5ld", (long)start, (long)(start+fl->l_len-1));
X    if (do_pid)
X	printf(" PID:%ld", (long)fl->l_pid);
X    printf("\n");
X    }
END-of-ftest.c
echo x - break.c
sed 's/^X//' >break.c << 'END-of-break.c'
X/*
X * A test of the cases where the backward compatible fcntl locking
X * doesn't work.
X */
X
X#include <stdio.h>
X#include <stdlib.h>
X#include <unistd.h>
X#include <fcntl.h>
X#include <string.h>
X
Xint
Xmain(argc, argv)
Xint argc;
Xchar **argv;
X    {
X    int fd;
X    char *name = "TOAD";
X    struct
X	{
X	struct flock fl;
X	int pid;
X	short type;
X	short whence;
X	} it;
X
X    if ((fd = open(name, O_RDWR|O_CREAT|O_TRUNC, 0600)) == -1)
X	perror(name), exit(1);
X
X    /*
X     * This lock is ambiguous on a 2.0 system and will default to "old".
X     */
X    it.fl.l_type = F_WRLCK;
X    it.fl.l_whence = SEEK_SET;
X    it.fl.l_start = 0x00010001;
X    it.fl.l_len = 0;
X    if (fcntl(fd, F_SETLK, &it.fl) < 0)
X	perror("set1");
X
X    /*
X     * This lock is ambiguous on a 1.1.5 system and will default to "old".
X     * Note that without the bzero(), the uninitialised areas usually
X     * remove the ambiguity.
X     */
X    bzero(&it, sizeof(it));
X    it.fl.l_type = F_WRLCK;
X    it.fl.l_whence = SEEK_SET;
X    it.fl.l_start = 0;
X    it.fl.l_len = 0;
X    it.type = F_RDLCK;
X    it.whence = SEEK_CUR;
X    if (fcntl(fd, F_SETLK, &it.fl) < 0)
X	perror("set2");
X
X    return 0;
X    }
END-of-break.c
exit




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199504250815.SAA15397>