patch-2.4.0-test7 linux/fs/locks.c
Next file: linux/fs/minix/dir.c
Previous file: linux/fs/jffs/inode-v23.c
Back to the patch index
Back to the overall index
- Lines: 344
- Date:
Mon Aug 21 07:33:59 2000
- Orig file:
v2.4.0-test6/linux/fs/locks.c
- Orig date:
Mon Jul 10 16:47:26 2000
diff -u --recursive --new-file v2.4.0-test6/linux/fs/locks.c linux/fs/locks.c
@@ -103,6 +103,11 @@
*
* Fixed /proc/locks interface so that we can't overrun the buffer we are handed.
* Andy Walker (andy@lysaker.kvaerner.no), May 12, 1997.
+ *
+ * Use slab allocator instead of kmalloc/kfree.
+ * Use generic list implementation from <linux/list.h>.
+ * Sped up posix_locks_deadlock by only considering blocked locks.
+ * Matthew Wilcox <willy@thepuffingroup.com>, March, 2000.
*/
#include <linux/malloc.h>
@@ -205,8 +210,58 @@
/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
* style lock.
*/
-static int posix_make_lock(struct file *filp, struct file_lock *fl,
- struct flock *l)
+static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
+ struct flock *l)
+{
+ loff_t start;
+
+ switch (l->l_whence) {
+ case 0: /*SEEK_SET*/
+ start = 0;
+ break;
+ case 1: /*SEEK_CUR*/
+ start = filp->f_pos;
+ break;
+ case 2: /*SEEK_END*/
+ start = filp->f_dentry->d_inode->i_size;
+ break;
+ default:
+ return (0);
+ }
+
+ if (((start += l->l_start) < 0) || (l->l_len < 0))
+ return (0);
+ fl->fl_end = start + l->l_len - 1;
+ if (l->l_len > 0 && fl->fl_end < 0)
+ return (0);
+ fl->fl_start = start; /* we record the absolute position */
+ if (l->l_len == 0)
+ fl->fl_end = OFFSET_MAX;
+
+ fl->fl_owner = current->files;
+ fl->fl_pid = current->pid;
+ fl->fl_file = filp;
+ fl->fl_flags = FL_POSIX;
+ fl->fl_notify = NULL;
+ fl->fl_insert = NULL;
+ fl->fl_remove = NULL;
+
+ switch (l->l_type) {
+ case F_RDLCK:
+ case F_WRLCK:
+ case F_UNLCK:
+ fl->fl_type = l->l_type;
+ break;
+ default:
+ return (0);
+ }
+
+ return (1);
+}
+
+#if BITS_PER_LONG == 32
+static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
+ struct flock64 *l)
{
loff_t start;
@@ -253,6 +308,7 @@
return (1);
}
+#endif
/* Check if two locks overlap each other.
*/
@@ -300,8 +356,8 @@
locks_delete_block(waiter);
}
list_add_tail(&waiter->fl_block, &blocker->fl_block);
-// list_add(&waiter->fl_link, &blocked_list);
-// waiter->fl_next = blocker;
+ list_add(&waiter->fl_link, &blocked_list);
+ waiter->fl_next = blocker;
}
/* Wake up processes blocked waiting for blocker.
@@ -460,34 +516,27 @@
struct file_lock *block_fl)
{
struct list_head *tmp;
- void *caller_owner, *blocked_owner;
+ fl_owner_t caller_owner, blocked_owner;
unsigned int caller_pid, blocked_pid;
caller_owner = caller_fl->fl_owner;
caller_pid = caller_fl->fl_pid;
blocked_owner = block_fl->fl_owner;
blocked_pid = block_fl->fl_pid;
+ tmp = blocked_list.next;
next_task:
if (caller_owner == blocked_owner && caller_pid == blocked_pid)
return 1;
- list_for_each(tmp, &file_lock_list) {
- struct list_head *btmp;
+ while (tmp != &blocked_list) {
struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);
- if (fl->fl_owner == NULL || list_empty(&fl->fl_block))
- continue;
- list_for_each(btmp, &fl->fl_block) {
- struct file_lock *bfl = list_entry(tmp, struct file_lock, fl_block);
- if (bfl->fl_owner == blocked_owner &&
- bfl->fl_pid == blocked_pid) {
- if (fl->fl_owner == caller_owner &&
- fl->fl_pid == caller_pid) {
- return (1);
- }
- blocked_owner = fl->fl_owner;
- blocked_pid = fl->fl_pid;
- goto next_task;
- }
+ tmp = tmp->next;
+ if ((fl->fl_owner == blocked_owner)
+ && (fl->fl_pid == blocked_pid)) {
+ fl = fl->fl_next;
+ blocked_owner = fl->fl_owner;
+ blocked_pid = fl->fl_pid;
+ goto next_task;
}
}
return 0;
@@ -816,13 +865,12 @@
if (right) {
if (left == right) {
/* The new lock breaks the old one in two pieces,
- * so we have to use the second new lock (in this
- * case, even F_UNLCK may fail!).
+ * so we have to use the second new lock.
*/
- locks_copy_lock(new_fl2, right);
- locks_insert_lock(before, left);
left = new_fl2;
new_fl2 = NULL;
+ locks_copy_lock(left, right);
+ locks_insert_lock(before, left);
}
right->fl_start = caller->fl_end + 1;
locks_wake_up_blocks(right, 0);
@@ -909,7 +957,8 @@
if (!filp)
goto out;
- if (!posix_make_lock(filp, file_lock, &flock))
+ error = -EINVAL;
+ if (!flock_to_posix_lock(filp, file_lock, &flock))
goto out_putf;
if (filp->f_op->lock) {
@@ -928,6 +977,18 @@
flock.l_type = F_UNLCK;
if (fl != NULL) {
flock.l_pid = fl->fl_pid;
+#if BITS_PER_LONG == 32
+ /*
+ * Make sure we can represent the posix lock via
+ * legacy 32bit flock.
+ */
+ error = -EOVERFLOW;
+ if (fl->fl_start > OFFT_OFFSET_MAX)
+ goto out_putf;
+ if ((fl->fl_end != OFFSET_MAX)
+ && (fl->fl_end > OFFT_OFFSET_MAX))
+ goto out_putf;
+#endif
flock.l_start = fl->fl_start;
flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
fl->fl_end - fl->fl_start + 1;
@@ -993,7 +1054,7 @@
}
error = -EINVAL;
- if (!posix_make_lock(filp, file_lock, &flock))
+ if (!flock_to_posix_lock(filp, file_lock, &flock))
goto out_putf;
error = -EBADF;
@@ -1043,6 +1104,151 @@
locks_free_lock(file_lock);
return error;
}
+
+#if BITS_PER_LONG == 32
+/* Report the first existing lock that would conflict with l.
+ * This implements the F_GETLK command of fcntl().
+ */
+int fcntl_getlk64(unsigned int fd, struct flock64 *l)
+{
+ struct file *filp;
+ struct file_lock *fl, *file_lock = locks_alloc_lock();
+ struct flock64 flock;
+ int error;
+
+ error = -EFAULT;
+ if (copy_from_user(&flock, l, sizeof(flock)))
+ goto out;
+ error = -EINVAL;
+ if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK))
+ goto out;
+
+ error = -EBADF;
+ filp = fget(fd);
+ if (!filp)
+ goto out;
+
+ error = -EINVAL;
+ if (!flock64_to_posix_lock(filp, file_lock, &flock))
+ goto out_putf;
+
+ if (filp->f_op->lock) {
+ error = filp->f_op->lock(filp, F_GETLK, file_lock);
+ if (error < 0)
+ goto out_putf;
+ else if (error == LOCK_USE_CLNT)
+ /* Bypass for NFS with no locking - 2.0.36 compat */
+ fl = posix_test_lock(filp, file_lock);
+ else
+ fl = (file_lock->fl_type == F_UNLCK ? NULL : file_lock);
+ } else {
+ fl = posix_test_lock(filp, file_lock);
+ }
+
+ flock.l_type = F_UNLCK;
+ if (fl != NULL) {
+ flock.l_pid = fl->fl_pid;
+ flock.l_start = fl->fl_start;
+ flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
+ fl->fl_end - fl->fl_start + 1;
+ flock.l_whence = 0;
+ flock.l_type = fl->fl_type;
+ }
+ error = -EFAULT;
+ if (!copy_to_user(l, &flock, sizeof(flock)))
+ error = 0;
+
+out_putf:
+ fput(filp);
+out:
+ locks_free_lock(file_lock);
+ return error;
+}
+
+/* Apply the lock described by l to an open file descriptor.
+ * This implements both the F_SETLK and F_SETLKW commands of fcntl().
+ */
+int fcntl_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l)
+{
+ struct file *filp;
+ struct file_lock *file_lock = locks_alloc_lock();
+ 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;
+
+ error = -EINVAL;
+ inode = filp->f_dentry->d_inode;
+
+ /* Don't allow mandatory locks on files that may be memory mapped
+ * and shared.
+ */
+ if (IS_MANDLOCK(inode) &&
+ (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID) {
+ struct vm_area_struct *vma;
+ struct address_space *mapping = inode->i_mapping;
+ spin_lock(&mapping->i_shared_lock);
+ for(vma = mapping->i_mmap;vma;vma = vma->vm_next_share) {
+ if (!(vma->vm_flags & VM_MAYSHARE))
+ continue;
+ spin_unlock(&mapping->i_shared_lock);
+ error = -EAGAIN;
+ goto out_putf;
+ }
+ spin_unlock(&mapping->i_shared_lock);
+ }
+
+ error = -EINVAL;
+ if (!flock64_to_posix_lock(filp, file_lock, &flock))
+ goto out_putf;
+
+ error = -EBADF;
+ switch (flock.l_type) {
+ case F_RDLCK:
+ if (!(filp->f_mode & FMODE_READ))
+ goto out_putf;
+ break;
+ case F_WRLCK:
+ if (!(filp->f_mode & FMODE_WRITE))
+ goto out_putf;
+ break;
+ case F_UNLCK:
+ break;
+ case F_SHLCK:
+ case F_EXLCK:
+ default:
+ error = -EINVAL;
+ goto out_putf;
+ }
+
+ if (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;
+}
+#endif /* BITS_PER_LONG == 32 */
/*
* This function is called when the file is being removed
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)