F_SETLK_NP

Matthew Wilcox matthew at wil.cx
Sun May 6 23:57:00 GMT 2001


Here's an initial implementation of F_SETLK_NP as previously discussed
on linux-fsdevel.  The _ONLY_ difference between these locks and POSIX
locks is that they are not removed on a close(dup(fd)).

I have a question about the interaction between POSIX and RANGE locks.
If they overlap, should they be merged?  And if so, what type should
the resulting lock have?  My initial thought is that they should _not_
be merged but _should_ conflict, which is going to require a slightly
bigger patch.  This patch merges them and gives the merged lock the type
of the lock which arrived first.

Anyway, here's the test program (I checked what was going on via
/proc/locks, you can too).

/* lock-test.c Copyright (c) Matthew Wilcox, Hewlett-Packard.  GPL License */

#define _GNU_SOURCE

#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

#define F_SETLK_NP 15

int main(int argc, char **argv) {
	struct flock64 lock;
	int fd;

	lock.l_type = F_RDLCK;
	lock.l_whence = 0;
	lock.l_start = 50;
	lock.l_len = 100;

	fd = open(argv[0], O_RDONLY);
	fcntl(fd, F_SETLK_NP, &lock);
	printf("Applied lock to file -- check and press the `Enter' key\n");
	getchar();
	close(dup(fd));
	printf("Closed dup -- check and press the `Enter' key\n");
	getchar();
	close(fd);
	printf("Closed original -- check and press the `Enter' key\n");
	getchar();
	return 0;
}

And now for the patch itself.  It was generated against 2.4.5-pre1 but
will probably apply against any 2.4.x kernel.  I've taken the liberty
of getting rid of some old cruft which wasn't used any more.  This patch is
i386 only -- just add constants to other arch include files as required.

diff -urNX dontdiff linux-245p1/fs/fcntl.c linux-245p1-vfs/fs/fcntl.c
--- linux-245p1/fs/fcntl.c	Sun May  6 09:16:20 2001
+++ linux-245p1-vfs/fs/fcntl.c	Sun May  6 05:52:35 2001
@@ -260,6 +260,10 @@
 		case F_SETLKW:
 			err = fcntl_setlk(fd, cmd, (struct flock *) arg);
 			break;
+		case F_SETLK_NP:
+		case F_SETLKW_NP:
+			err = fcntl_setlk_np(fd, cmd, (struct flock64 *) arg);
+			break;
 		case F_GETOWN:
 			/*
 			 * XXX If f_owner is a process group, the
diff -urNX dontdiff linux-245p1/fs/locks.c linux-245p1-vfs/fs/locks.c
--- linux-245p1/fs/locks.c	Sun May  6 09:14:08 2001
+++ linux-245p1-vfs/fs/locks.c	Sun May  6 05:38:12 2001
@@ -547,7 +547,7 @@
 	/* POSIX locks owned by the same process do not conflict with
 	 * each other.
 	 */
-	if (!(sys_fl->fl_flags & FL_POSIX) ||
+	if (!(sys_fl->fl_flags & (FL_POSIX | FL_RANGE)) ||
 	    locks_same_owner(caller_fl, sys_fl))
 		return (0);
 
@@ -620,7 +620,7 @@
 
 	lock_kernel();
 	for (cfl = filp->f_dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
-		if (!(cfl->fl_flags & FL_POSIX))
+		if (!(cfl->fl_flags & (FL_POSIX | FL_RANGE)))
 			continue;
 		if (posix_locks_conflict(cfl, fl))
 			break;
@@ -863,7 +863,7 @@
 	if (caller->fl_type != F_UNLCK) {
   repeat:
 		for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-			if (!(fl->fl_flags & FL_POSIX))
+			if (!(fl->fl_flags & (FL_POSIX | FL_RANGE)))
 				continue;
 			if (!posix_locks_conflict(caller, fl))
 				continue;
@@ -892,7 +892,7 @@
 
 	/* First skip locks owned by other processes.
 	 */
-	while ((fl = *before) && (!(fl->fl_flags & FL_POSIX) ||
+	while ((fl = *before) && (!(fl->fl_flags & (FL_POSIX | FL_RANGE)) ||
 				  !locks_same_owner(caller, fl))) {
 		before = &fl->fl_next;
 	}
@@ -1458,23 +1458,72 @@
 		break;
 	case F_UNLCK:
 		break;
-	case F_SHLCK:
-	case F_EXLCK:
-#ifdef __sparc__
-/* warn a bit for now, but don't overdo it */
-{
-	static int count = 0;
-	if (!count) {
-		count=1;
-		printk(KERN_WARNING
-		       "fcntl_setlk() called by process %d (%s) with broken flock() emulation\n",
-		       current->pid, current->comm);
+	default:
+		error = -EINVAL;
+		goto out_putf;
+	}
+
+	if (filp->f_op && filp->f_op->lock != NULL) {
+		error = filp->f_op->lock(filp, cmd, file_lock);
+		if (error < 0)
+			goto out_putf;
 	}
+	error = posix_lock_file(filp, file_lock, cmd == F_SETLKW);
+
+out_putf:
+	fput(filp);
+out:
+	locks_free_lock(file_lock);
+	return error;
 }
-		if (!(filp->f_mode & 3))
+
+/* Apply the lock described by l to an open file descriptor.
+ * This implements both the F_SETLK_NP and F_SETLKW_NP commands of fcntl().
+ */
+#if BITS_PER_LONG == 64
+#define flock64 flock
+#define flock64_to_posix_lock flock_to_posix_lock
+#endif
+int fcntl_setlk_np(unsigned int fd, unsigned int cmd, struct flock64 *l)
+{
+	struct file *filp;
+	struct file_lock *file_lock = locks_alloc_lock(0);
+	struct flock64 flock;
+	struct inode *inode;
+	int error;
+
+	/* This might block, so we do it before checking the inode. */
+	error = -EFAULT;
+	if (copy_from_user(&flock, l, sizeof(flock)))
+		goto out;
+
+	/* Get arguments and validate them */
+
+	error = -EBADF;
+	filp = fget(fd);
+	if (!filp)
+		goto out;
+
+	inode = filp->f_dentry->d_inode;
+
+	error = -EINVAL;
+	if (!flock64_to_posix_lock(filp, file_lock, &flock))
+		goto out_putf;
+
+	file_lock->fl_flags = FL_RANGE;
+
+	error = -EBADF;
+	switch (flock.l_type) {
+	case F_RDLCK:
+		if (!(filp->f_mode & FMODE_READ))
 			goto out_putf;
 		break;
-#endif
+	case F_WRLCK:
+		if (!(filp->f_mode & FMODE_WRITE))
+			goto out_putf;
+		break;
+	case F_UNLCK:
+		break;
 	default:
 		error = -EINVAL;
 		goto out_putf;
@@ -1611,8 +1660,6 @@
 		break;
 	case F_UNLCK:
 		break;
-	case F_SHLCK:
-	case F_EXLCK:
 	default:
 		error = -EINVAL;
 		goto out_putf;
@@ -1683,7 +1730,7 @@
 	before = &inode->i_flock;
 
 	while ((fl = *before) != NULL) {
-		if ((fl->fl_flags & (FL_FLOCK|FL_LEASE))
+		if ((fl->fl_flags & (FL_FLOCK | FL_LEASE | FL_RANGE))
 		    && (fl->fl_file == filp)) {
 			locks_delete_lock(before, 0);
 			continue;
@@ -1727,35 +1774,37 @@
 		inode = fl->fl_file->f_dentry->d_inode;
 
 	out += sprintf(out, "%d:%s ", id, pfx);
-	if (fl->fl_flags & FL_POSIX) {
-		out += sprintf(out, "%6s %s ",
+	if (fl->fl_flags & (FL_POSIX | FL_RANGE)) {
+		if (fl->fl_flags & FL_RANGE) {
+			out += sprintf(out, "RANGE  ADVISORY  ");
+		} else {
+			out += sprintf(out, "%6s %s ",
 			     (fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
 			     (inode == NULL) ? "*NOINODE*" :
 			     (IS_MANDLOCK(inode) &&
 			      (inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ?
 			     "MANDATORY" : "ADVISORY ");
+		}
 	} else if (fl->fl_flags & FL_FLOCK) {
-#ifdef MSNFS
 		if (fl->fl_type & LOCK_MAND) {
 			out += sprintf(out, "FLOCK  MSNFS     ");
-		} else
-#endif
+		} else {
 			out += sprintf(out, "FLOCK  ADVISORY  ");
+		}
 	} else if (fl->fl_flags & FL_LEASE) {
 		out += sprintf(out, "LEASE  MANDATORY ");
 	} else {
 		out += sprintf(out, "UNKNOWN UNKNOWN  ");
 	}
-#ifdef MSNFS
 	if (fl->fl_type & LOCK_MAND) {
 		out += sprintf(out, "%s ",
 			       (fl->fl_type & LOCK_READ)
 			       ? (fl->fl_type & LOCK_WRITE) ? "RW   " : "READ "
 			       : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE ");
-	} else
-#endif
+	} else {
 		out += sprintf(out, "%s ",
 			       (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
+	}
 	out += sprintf(out, "%d %s:%ld ",
 		     fl->fl_pid,
 		     inode ? kdevname(inode->i_dev) : "<none>",
@@ -1855,7 +1904,7 @@
 	int result = 1;
 	lock_kernel();
 	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-		if (fl->fl_flags == FL_POSIX) {
+		if (fl->fl_flags & (FL_POSIX | FL_RANGE)) {
 			if (fl->fl_type == F_RDLCK)
 				continue;
 			if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
@@ -1893,7 +1942,7 @@
 	int result = 1;
 	lock_kernel();
 	for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
-		if (fl->fl_flags == FL_POSIX) {
+		if (fl->fl_flags & (FL_POSIX | FL_RANGE)) {
 			if ((fl->fl_end < start) || (fl->fl_start > (start + len)))
 				continue;
 		} else if (fl->fl_flags == FL_FLOCK) {
diff -urNX dontdiff linux-245p1/fs/nfs/file.c linux-245p1-vfs/fs/nfs/file.c
--- linux-245p1/fs/nfs/file.c	Sun May  6 09:13:47 2001
+++ linux-245p1-vfs/fs/nfs/file.c	Sun May  6 05:25:36 2001
@@ -270,13 +270,12 @@
 	}
 
 	/*
-	 * No BSD flocks over NFS allowed.
-	 * Note: we could try to fake a POSIX lock request here by
-	 * using ((u32) filp | 0x80000000) or some such as the pid.
-	 * Not sure whether that would be unique, though, or whether
-	 * that would break in other places.
+	 * Only POSIX locks allowed over NFS.  Pretending that a BSD flock
+	 * is a POSIX lock breaks rather badly, don't even try to fake it.
+	 * Other lock types (leases, range, etc) also have different
+	 * semantics, so don't fake those either.
 	 */
-	if (!fl->fl_owner || (fl->fl_flags & (FL_POSIX|FL_BROKEN)) != FL_POSIX)
+	if ((fl->fl_flags & FL_POSIX) == 0)
 		return -ENOLCK;
 
 	/*
diff -urNX dontdiff linux-245p1/include/asm-i386/fcntl.h linux-245p1-vfs/include/asm-i386/fcntl.h
--- linux-245p1/include/asm-i386/fcntl.h	Sun May  6 09:13:24 2001
+++ linux-245p1-vfs/include/asm-i386/fcntl.h	Sun May  6 04:59:14 2001
@@ -39,6 +39,9 @@
 #define F_SETLK64	13
 #define F_SETLKW64	14
 
+#define F_SETLK_NP	15	/* Nonposix semantics */
+#define F_SETLKW_NP	16	/* Nonposix semantics */
+
 /* for F_[GET|SET]FL */
 #define FD_CLOEXEC	1	/* actually anything with low bit set goes */
 
diff -urNX dontdiff linux-245p1/include/linux/fs.h linux-245p1-vfs/include/linux/fs.h
--- linux-245p1/include/linux/fs.h	Sun May  6 09:12:44 2001
+++ linux-245p1-vfs/include/linux/fs.h	Sun May  6 08:05:54 2001
@@ -509,7 +509,7 @@
 
 #define FL_POSIX	1
 #define FL_FLOCK	2
-#define FL_BROKEN	4	/* broken flock() emulation */
+#define FL_RANGE	4	/* non-posix semantics */
 #define FL_ACCESS	8	/* for processes suspended by mandatory locking */
 #define FL_LOCKD	16	/* lock held by rpc.lockd */
 #define FL_LEASE	32	/* lease held on this file */
@@ -560,6 +560,8 @@
 
 extern int fcntl_getlk(unsigned int, struct flock *);
 extern int fcntl_setlk(unsigned int, unsigned int, struct flock *);
+
+extern int fcntl_setlk_np(unsigned int, unsigned int, struct flock64 *);
 
 extern int fcntl_getlk64(unsigned int, struct flock64 *);
 extern int fcntl_setlk64(unsigned int, unsigned int, struct flock64 *);

-- 
Revolutions do not require corporate support.




More information about the samba-technical mailing list