Merge branch 'locks-3.15' of git://git.samba.org/jlayton/linux

Pull file locking updates from Jeff Layton:
 "Highlights:

   - maintainership change for fs/locks.c.  Willy's not interested in
     maintaining it these days, and is OK with Bruce and I taking it.
   - fix for open vs setlease race that Al ID'ed
   - cleanup and consolidation of file locking code
   - eliminate unneeded BUG() call
   - merge of file-private lock implementation"

* 'locks-3.15' of git://git.samba.org/jlayton/linux:
  locks: make locks_mandatory_area check for file-private locks
  locks: fix locks_mandatory_locked to respect file-private locks
  locks: require that flock->l_pid be set to 0 for file-private locks
  locks: add new fcntl cmd values for handling file private locks
  locks: skip deadlock detection on FL_FILE_PVT locks
  locks: pass the cmd value to fcntl_getlk/getlk64
  locks: report l_pid as -1 for FL_FILE_PVT locks
  locks: make /proc/locks show IS_FILE_PVT locks as type "FLPVT"
  locks: rename locks_remove_flock to locks_remove_file
  locks: consolidate checks for compatible filp->f_mode values in setlk handlers
  locks: fix posix lock range overflow handling
  locks: eliminate BUG() call when there's an unexpected lock on file close
  locks: add __acquires and __releases annotations to locks_start and locks_stop
  locks: remove "inline" qualifier from fl_link manipulation functions
  locks: clean up comment typo
  locks: close potential race between setlease and open
  MAINTAINERS: update entry for fs/locks.c
This commit is contained in:
Linus Torvalds 2014-04-04 14:21:20 -07:00
commit f7789dc0d4
12 changed files with 382 additions and 210 deletions

View file

@ -3521,7 +3521,8 @@ F: include/scsi/libfcoe.h
F: include/uapi/scsi/fc/
FILE LOCKING (flock() and fcntl()/lockf())
M: Matthew Wilcox <matthew@wil.cx>
M: Jeff Layton <jlayton@redhat.com>
M: J. Bruce Fields <bfields@fieldses.org>
L: linux-fsdevel@vger.kernel.org
S: Maintained
F: include/linux/fcntl.h

View file

@ -203,6 +203,9 @@ asmlinkage long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd,
int ret;
switch (cmd) {
case F_GETLKP:
case F_SETLKP:
case F_SETLKPW:
case F_GETLK64:
case F_SETLK64:
case F_SETLKW64:

View file

@ -399,12 +399,28 @@ static int put_compat_flock64(struct flock *kfl, struct compat_flock64 __user *u
}
#endif
static unsigned int
convert_fcntl_cmd(unsigned int cmd)
{
switch (cmd) {
case F_GETLK64:
return F_GETLK;
case F_SETLK64:
return F_SETLK;
case F_SETLKW64:
return F_SETLKW;
}
return cmd;
}
COMPAT_SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
compat_ulong_t, arg)
{
mm_segment_t old_fs;
struct flock f;
long ret;
unsigned int conv_cmd;
switch (cmd) {
case F_GETLK:
@ -441,16 +457,18 @@ COMPAT_SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
case F_GETLK64:
case F_SETLK64:
case F_SETLKW64:
case F_GETLKP:
case F_SETLKP:
case F_SETLKPW:
ret = get_compat_flock64(&f, compat_ptr(arg));
if (ret != 0)
break;
old_fs = get_fs();
set_fs(KERNEL_DS);
ret = sys_fcntl(fd, (cmd == F_GETLK64) ? F_GETLK :
((cmd == F_SETLK64) ? F_SETLK : F_SETLKW),
(unsigned long)&f);
conv_cmd = convert_fcntl_cmd(cmd);
ret = sys_fcntl(fd, conv_cmd, (unsigned long)&f);
set_fs(old_fs);
if (cmd == F_GETLK64 && ret == 0) {
if ((conv_cmd == F_GETLK || conv_cmd == F_GETLKP) && ret == 0) {
/* need to return lock information - see above for commentary */
if (f.l_start > COMPAT_LOFF_T_MAX)
ret = -EOVERFLOW;
@ -471,8 +489,15 @@ COMPAT_SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
COMPAT_SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd,
compat_ulong_t, arg)
{
if ((cmd == F_GETLK64) || (cmd == F_SETLK64) || (cmd == F_SETLKW64))
switch (cmd) {
case F_GETLK64:
case F_SETLK64:
case F_SETLKW64:
case F_GETLKP:
case F_SETLKP:
case F_SETLKPW:
return -EINVAL;
}
return compat_sys_fcntl64(fd, cmd, arg);
}

View file

@ -272,9 +272,19 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
case F_SETFL:
err = setfl(fd, filp, arg);
break;
#if BITS_PER_LONG != 32
/* 32-bit arches must use fcntl64() */
case F_GETLKP:
#endif
case F_GETLK:
err = fcntl_getlk(filp, (struct flock __user *) arg);
err = fcntl_getlk(filp, cmd, (struct flock __user *) arg);
break;
#if BITS_PER_LONG != 32
/* 32-bit arches must use fcntl64() */
case F_SETLKP:
case F_SETLKPW:
#endif
/* Fallthrough */
case F_SETLK:
case F_SETLKW:
err = fcntl_setlk(fd, filp, cmd, (struct flock __user *) arg);
@ -388,17 +398,20 @@ SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
goto out1;
switch (cmd) {
case F_GETLK64:
err = fcntl_getlk64(f.file, (struct flock64 __user *) arg);
break;
case F_SETLK64:
case F_SETLKW64:
err = fcntl_setlk64(fd, f.file, cmd,
(struct flock64 __user *) arg);
break;
default:
err = do_fcntl(fd, cmd, arg, f.file);
break;
case F_GETLK64:
case F_GETLKP:
err = fcntl_getlk64(f.file, cmd, (struct flock64 __user *) arg);
break;
case F_SETLK64:
case F_SETLKW64:
case F_SETLKP:
case F_SETLKPW:
err = fcntl_setlk64(fd, f.file, cmd,
(struct flock64 __user *) arg);
break;
default:
err = do_fcntl(fd, cmd, arg, f.file);
break;
}
out1:
fdput(f);

View file

@ -235,7 +235,7 @@ static void __fput(struct file *file)
* in the file cleanup chain.
*/
eventpoll_release(file);
locks_remove_flock(file);
locks_remove_file(file);
if (unlikely(file->f_flags & FASYNC)) {
if (file->f_op->fasync)

View file

@ -135,6 +135,7 @@
#define IS_POSIX(fl) (fl->fl_flags & FL_POSIX)
#define IS_FLOCK(fl) (fl->fl_flags & FL_FLOCK)
#define IS_LEASE(fl) (fl->fl_flags & (FL_LEASE|FL_DELEG))
#define IS_FILE_PVT(fl) (fl->fl_flags & FL_FILE_PVT)
static bool lease_breaking(struct file_lock *fl)
{
@ -344,105 +345,81 @@ static int assign_type(struct file_lock *fl, long type)
return 0;
}
static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
struct flock64 *l)
{
switch (l->l_whence) {
case SEEK_SET:
fl->fl_start = 0;
break;
case SEEK_CUR:
fl->fl_start = filp->f_pos;
break;
case SEEK_END:
fl->fl_start = i_size_read(file_inode(filp));
break;
default:
return -EINVAL;
}
if (l->l_start > OFFSET_MAX - fl->fl_start)
return -EOVERFLOW;
fl->fl_start += l->l_start;
if (fl->fl_start < 0)
return -EINVAL;
/* POSIX-1996 leaves the case l->l_len < 0 undefined;
POSIX-2001 defines it. */
if (l->l_len > 0) {
if (l->l_len - 1 > OFFSET_MAX - fl->fl_start)
return -EOVERFLOW;
fl->fl_end = fl->fl_start + l->l_len - 1;
} else if (l->l_len < 0) {
if (fl->fl_start + l->l_len < 0)
return -EINVAL;
fl->fl_end = fl->fl_start - 1;
fl->fl_start += l->l_len;
} else
fl->fl_end = OFFSET_MAX;
fl->fl_owner = current->files;
fl->fl_pid = current->tgid;
fl->fl_file = filp;
fl->fl_flags = FL_POSIX;
fl->fl_ops = NULL;
fl->fl_lmops = NULL;
/* Ensure that fl->fl_filp has compatible f_mode */
switch (l->l_type) {
case F_RDLCK:
if (!(filp->f_mode & FMODE_READ))
return -EBADF;
break;
case F_WRLCK:
if (!(filp->f_mode & FMODE_WRITE))
return -EBADF;
break;
}
return assign_type(fl, l->l_type);
}
/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
* style lock.
*/
static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
struct flock *l)
{
off_t start, end;
struct flock64 ll = {
.l_type = l->l_type,
.l_whence = l->l_whence,
.l_start = l->l_start,
.l_len = l->l_len,
};
switch (l->l_whence) {
case SEEK_SET:
start = 0;
break;
case SEEK_CUR:
start = filp->f_pos;
break;
case SEEK_END:
start = i_size_read(file_inode(filp));
break;
default:
return -EINVAL;
}
/* POSIX-1996 leaves the case l->l_len < 0 undefined;
POSIX-2001 defines it. */
start += l->l_start;
if (start < 0)
return -EINVAL;
fl->fl_end = OFFSET_MAX;
if (l->l_len > 0) {
end = start + l->l_len - 1;
fl->fl_end = end;
} else if (l->l_len < 0) {
end = start - 1;
fl->fl_end = end;
start += l->l_len;
if (start < 0)
return -EINVAL;
}
fl->fl_start = start; /* we record the absolute position */
if (fl->fl_end < fl->fl_start)
return -EOVERFLOW;
fl->fl_owner = current->files;
fl->fl_pid = current->tgid;
fl->fl_file = filp;
fl->fl_flags = FL_POSIX;
fl->fl_ops = NULL;
fl->fl_lmops = NULL;
return assign_type(fl, l->l_type);
return flock64_to_posix_lock(filp, fl, &ll);
}
#if BITS_PER_LONG == 32
static int flock64_to_posix_lock(struct file *filp, struct file_lock *fl,
struct flock64 *l)
{
loff_t start;
switch (l->l_whence) {
case SEEK_SET:
start = 0;
break;
case SEEK_CUR:
start = filp->f_pos;
break;
case SEEK_END:
start = i_size_read(file_inode(filp));
break;
default:
return -EINVAL;
}
start += l->l_start;
if (start < 0)
return -EINVAL;
fl->fl_end = OFFSET_MAX;
if (l->l_len > 0) {
fl->fl_end = start + l->l_len - 1;
} else if (l->l_len < 0) {
fl->fl_end = start - 1;
start += l->l_len;
if (start < 0)
return -EINVAL;
}
fl->fl_start = start; /* we record the absolute position */
if (fl->fl_end < fl->fl_start)
return -EOVERFLOW;
fl->fl_owner = current->files;
fl->fl_pid = current->tgid;
fl->fl_file = filp;
fl->fl_flags = FL_POSIX;
fl->fl_ops = NULL;
fl->fl_lmops = NULL;
return assign_type(fl, l->l_type);
}
#endif
/* default lease lock manager operations */
static void lease_break_callback(struct file_lock *fl)
{
@ -511,8 +488,7 @@ static int posix_same_owner(struct file_lock *fl1, struct file_lock *fl2)
}
/* Must be called with the i_lock held! */
static inline void
locks_insert_global_locks(struct file_lock *fl)
static void locks_insert_global_locks(struct file_lock *fl)
{
lg_local_lock(&file_lock_lglock);
fl->fl_link_cpu = smp_processor_id();
@ -521,8 +497,7 @@ locks_insert_global_locks(struct file_lock *fl)
}
/* Must be called with the i_lock held! */
static inline void
locks_delete_global_locks(struct file_lock *fl)
static void locks_delete_global_locks(struct file_lock *fl)
{
/*
* Avoid taking lock if already unhashed. This is safe since this check
@ -544,14 +519,12 @@ posix_owner_key(struct file_lock *fl)
return (unsigned long)fl->fl_owner;
}
static inline void
locks_insert_global_blocked(struct file_lock *waiter)
static void locks_insert_global_blocked(struct file_lock *waiter)
{
hash_add(blocked_hash, &waiter->fl_link, posix_owner_key(waiter));
}
static inline void
locks_delete_global_blocked(struct file_lock *waiter)
static void locks_delete_global_blocked(struct file_lock *waiter)
{
hash_del(&waiter->fl_link);
}
@ -581,7 +554,7 @@ static void locks_delete_block(struct file_lock *waiter)
* it seems like the reasonable thing to do.
*
* Must be called with both the i_lock and blocked_lock_lock held. The fl_block
* list itself is protected by the file_lock_list, but by ensuring that the
* list itself is protected by the blocked_lock_lock, but by ensuring that the
* i_lock is also held on insertions we can avoid taking the blocked_lock_lock
* in some cases when we see that the fl_block list is empty.
*/
@ -591,7 +564,7 @@ static void __locks_insert_block(struct file_lock *blocker,
BUG_ON(!list_empty(&waiter->fl_block));
waiter->fl_next = blocker;
list_add_tail(&waiter->fl_block, &blocker->fl_block);
if (IS_POSIX(blocker))
if (IS_POSIX(blocker) && !IS_FILE_PVT(blocker))
locks_insert_global_blocked(waiter);
}
@ -652,15 +625,18 @@ static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl)
locks_insert_global_locks(fl);
}
/*
* Delete a lock and then free it.
* Wake up processes that are blocked waiting for this lock,
* notify the FS that the lock has been cleared and
* finally free the lock.
/**
* locks_delete_lock - Delete a lock and then free it.
* @thisfl_p: pointer that points to the fl_next field of the previous
* inode->i_flock list entry
*
* Unlink a lock from all lists and free the namespace reference, but don't
* free it yet. Wake up processes that are blocked waiting for this lock and
* notify the FS that the lock has been cleared.
*
* Must be called with the i_lock held!
*/
static void locks_delete_lock(struct file_lock **thisfl_p)
static void locks_unlink_lock(struct file_lock **thisfl_p)
{
struct file_lock *fl = *thisfl_p;
@ -675,6 +651,18 @@ static void locks_delete_lock(struct file_lock **thisfl_p)
}
locks_wake_up_blocks(fl);
}
/*
* Unlink a lock from all lists and free it.
*
* Must be called with i_lock held!
*/
static void locks_delete_lock(struct file_lock **thisfl_p)
{
struct file_lock *fl = *thisfl_p;
locks_unlink_lock(thisfl_p);
locks_free_lock(fl);
}
@ -769,8 +757,16 @@ EXPORT_SYMBOL(posix_test_lock);
* Note: the above assumption may not be true when handling lock
* requests from a broken NFS client. It may also fail in the presence
* of tasks (such as posix threads) sharing the same open file table.
*
* To handle those cases, we just bail out after a few iterations.
*
* For FL_FILE_PVT locks, the owner is the filp, not the files_struct.
* Because the owner is not even nominally tied to a thread of
* execution, the deadlock detection below can't reasonably work well. Just
* skip it for those.
*
* In principle, we could do a more limited deadlock detection on FL_FILE_PVT
* locks that just checks for the case where two tasks are attempting to
* upgrade from read to write locks on the same inode.
*/
#define MAX_DEADLK_ITERATIONS 10
@ -793,6 +789,13 @@ static int posix_locks_deadlock(struct file_lock *caller_fl,
{
int i = 0;
/*
* This deadlock detector can't reasonably detect deadlocks with
* FL_FILE_PVT locks, since they aren't owned by a process, per-se.
*/
if (IS_FILE_PVT(caller_fl))
return 0;
while ((block_fl = what_owner_is_waiting_for(block_fl))) {
if (i++ > MAX_DEADLK_ITERATIONS)
return 0;
@ -1152,13 +1155,14 @@ EXPORT_SYMBOL(posix_lock_file_wait);
/**
* locks_mandatory_locked - Check for an active lock
* @inode: the file to check
* @file: the file to check
*
* Searches the inode's list of locks to find any POSIX locks which conflict.
* This function is called from locks_verify_locked() only.
*/
int locks_mandatory_locked(struct inode *inode)
int locks_mandatory_locked(struct file *file)
{
struct inode *inode = file_inode(file);
fl_owner_t owner = current->files;
struct file_lock *fl;
@ -1169,7 +1173,7 @@ int locks_mandatory_locked(struct inode *inode)
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
if (!IS_POSIX(fl))
continue;
if (fl->fl_owner != owner)
if (fl->fl_owner != owner && fl->fl_owner != (fl_owner_t)file)
break;
}
spin_unlock(&inode->i_lock);
@ -1195,19 +1199,30 @@ int locks_mandatory_area(int read_write, struct inode *inode,
{
struct file_lock fl;
int error;
bool sleep = false;
locks_init_lock(&fl);
fl.fl_owner = current->files;
fl.fl_pid = current->tgid;
fl.fl_file = filp;
fl.fl_flags = FL_POSIX | FL_ACCESS;
if (filp && !(filp->f_flags & O_NONBLOCK))
fl.fl_flags |= FL_SLEEP;
sleep = true;
fl.fl_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
fl.fl_start = offset;
fl.fl_end = offset + count - 1;
for (;;) {
if (filp) {
fl.fl_owner = (fl_owner_t)filp;
fl.fl_flags &= ~FL_SLEEP;
error = __posix_lock_file(inode, &fl, NULL);
if (!error)
break;
}
if (sleep)
fl.fl_flags |= FL_SLEEP;
fl.fl_owner = current->files;
error = __posix_lock_file(inode, &fl, NULL);
if (error != FILE_LOCK_DEFERRED)
break;
@ -1472,6 +1487,32 @@ int fcntl_getlease(struct file *filp)
return type;
}
/**
* check_conflicting_open - see if the given dentry points to a file that has
* an existing open that would conflict with the
* desired lease.
* @dentry: dentry to check
* @arg: type of lease that we're trying to acquire
*
* Check to see if there's an existing open fd on this file that would
* conflict with the lease we're trying to set.
*/
static int
check_conflicting_open(const struct dentry *dentry, const long arg)
{
int ret = 0;
struct inode *inode = dentry->d_inode;
if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
return -EAGAIN;
if ((arg == F_WRLCK) && ((d_count(dentry) > 1) ||
(atomic_read(&inode->i_count) > 1)))
ret = -EAGAIN;
return ret;
}
static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
{
struct file_lock *fl, **before, **my_before = NULL, *lease;
@ -1499,12 +1540,8 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
return -EINVAL;
}
error = -EAGAIN;
if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
goto out;
if ((arg == F_WRLCK)
&& ((d_count(dentry) > 1)
|| (atomic_read(&inode->i_count) > 1)))
error = check_conflicting_open(dentry, arg);
if (error)
goto out;
/*
@ -1549,7 +1586,19 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
goto out;
locks_insert_lock(before, lease);
error = 0;
/*
* The check in break_lease() is lockless. It's possible for another
* open to race in after we did the earlier check for a conflicting
* open but before the lease was inserted. Check again for a
* conflicting open and cancel the lease if there is one.
*
* We also add a barrier here to ensure that the insertion of the lock
* precedes these checks.
*/
smp_mb();
error = check_conflicting_open(dentry, arg);
if (error)
locks_unlink_lock(flp);
out:
if (is_deleg)
mutex_unlock(&inode->i_mutex);
@ -1842,7 +1891,7 @@ EXPORT_SYMBOL_GPL(vfs_test_lock);
static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl)
{
flock->l_pid = fl->fl_pid;
flock->l_pid = IS_FILE_PVT(fl) ? -1 : fl->fl_pid;
#if BITS_PER_LONG == 32
/*
* Make sure we can represent the posix lock via
@ -1864,7 +1913,7 @@ static int posix_lock_to_flock(struct flock *flock, struct file_lock *fl)
#if BITS_PER_LONG == 32
static void posix_lock_to_flock64(struct flock64 *flock, struct file_lock *fl)
{
flock->l_pid = fl->fl_pid;
flock->l_pid = IS_FILE_PVT(fl) ? -1 : 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;
@ -1876,7 +1925,7 @@ static void posix_lock_to_flock64(struct flock64 *flock, struct file_lock *fl)
/* Report the first existing lock that would conflict with l.
* This implements the F_GETLK command of fcntl().
*/
int fcntl_getlk(struct file *filp, struct flock __user *l)
int fcntl_getlk(struct file *filp, unsigned int cmd, struct flock __user *l)
{
struct file_lock file_lock;
struct flock flock;
@ -1893,6 +1942,16 @@ int fcntl_getlk(struct file *filp, struct flock __user *l)
if (error)
goto out;
if (cmd == F_GETLKP) {
error = -EINVAL;
if (flock.l_pid != 0)
goto out;
cmd = F_GETLK;
file_lock.fl_flags |= FL_FILE_PVT;
file_lock.fl_owner = (fl_owner_t)filp;
}
error = vfs_test_lock(filp, &file_lock);
if (error)
goto out;
@ -2012,25 +2071,32 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
error = flock_to_posix_lock(filp, file_lock, &flock);
if (error)
goto out;
if (cmd == F_SETLKW) {
file_lock->fl_flags |= FL_SLEEP;
}
error = -EBADF;
switch (flock.l_type) {
case F_RDLCK:
if (!(filp->f_mode & FMODE_READ))
goto out;
break;
case F_WRLCK:
if (!(filp->f_mode & FMODE_WRITE))
goto out;
break;
case F_UNLCK:
break;
default:
/*
* If the cmd is requesting file-private locks, then set the
* FL_FILE_PVT flag and override the owner.
*/
switch (cmd) {
case F_SETLKP:
error = -EINVAL;
goto out;
if (flock.l_pid != 0)
goto out;
cmd = F_SETLK;
file_lock->fl_flags |= FL_FILE_PVT;
file_lock->fl_owner = (fl_owner_t)filp;
break;
case F_SETLKPW:
error = -EINVAL;
if (flock.l_pid != 0)
goto out;
cmd = F_SETLKW;
file_lock->fl_flags |= FL_FILE_PVT;
file_lock->fl_owner = (fl_owner_t)filp;
/* Fallthrough */
case F_SETLKW:
file_lock->fl_flags |= FL_SLEEP;
}
error = do_lock_file_wait(filp, cmd, file_lock);
@ -2061,7 +2127,7 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
/* Report the first existing lock that would conflict with l.
* This implements the F_GETLK command of fcntl().
*/
int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
int fcntl_getlk64(struct file *filp, unsigned int cmd, struct flock64 __user *l)
{
struct file_lock file_lock;
struct flock64 flock;
@ -2078,6 +2144,16 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
if (error)
goto out;
if (cmd == F_GETLKP) {
error = -EINVAL;
if (flock.l_pid != 0)
goto out;
cmd = F_GETLK64;
file_lock.fl_flags |= FL_FILE_PVT;
file_lock.fl_owner = (fl_owner_t)filp;
}
error = vfs_test_lock(filp, &file_lock);
if (error)
goto out;
@ -2130,25 +2206,32 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
error = flock64_to_posix_lock(filp, file_lock, &flock);
if (error)
goto out;
if (cmd == F_SETLKW64) {
file_lock->fl_flags |= FL_SLEEP;
}
error = -EBADF;
switch (flock.l_type) {
case F_RDLCK:
if (!(filp->f_mode & FMODE_READ))
goto out;
break;
case F_WRLCK:
if (!(filp->f_mode & FMODE_WRITE))
goto out;
break;
case F_UNLCK:
break;
default:
/*
* If the cmd is requesting file-private locks, then set the
* FL_FILE_PVT flag and override the owner.
*/
switch (cmd) {
case F_SETLKP:
error = -EINVAL;
goto out;
if (flock.l_pid != 0)
goto out;
cmd = F_SETLK64;
file_lock->fl_flags |= FL_FILE_PVT;
file_lock->fl_owner = (fl_owner_t)filp;
break;
case F_SETLKPW:
error = -EINVAL;
if (flock.l_pid != 0)
goto out;
cmd = F_SETLKW64;
file_lock->fl_flags |= FL_FILE_PVT;
file_lock->fl_owner = (fl_owner_t)filp;
/* Fallthrough */
case F_SETLKW64:
file_lock->fl_flags |= FL_SLEEP;
}
error = do_lock_file_wait(filp, cmd, file_lock);
@ -2209,7 +2292,7 @@ EXPORT_SYMBOL(locks_remove_posix);
/*
* This function is called on the last close of an open file.
*/
void locks_remove_flock(struct file *filp)
void locks_remove_file(struct file *filp)
{
struct inode * inode = file_inode(filp);
struct file_lock *fl;
@ -2218,6 +2301,8 @@ void locks_remove_flock(struct file *filp)
if (!inode->i_flock)
return;
locks_remove_posix(filp, (fl_owner_t)filp);
if (filp->f_op->flock) {
struct file_lock fl = {
.fl_pid = current->tgid,
@ -2236,16 +2321,28 @@ void locks_remove_flock(struct file *filp)
while ((fl = *before) != NULL) {
if (fl->fl_file == filp) {
if (IS_FLOCK(fl)) {
locks_delete_lock(before);
continue;
}
if (IS_LEASE(fl)) {
lease_modify(before, F_UNLCK);
continue;
}
/* What? */
BUG();
/*
* There's a leftover lock on the list of a type that
* we didn't expect to see. Most likely a classic
* POSIX lock that ended up not getting released
* properly, or that raced onto the list somehow. Log
* some info about it and then just remove it from
* the list.
*/
WARN(!IS_FLOCK(fl),
"leftover lock: dev=%u:%u ino=%lu type=%hhd flags=0x%x start=%lld end=%lld\n",
MAJOR(inode->i_sb->s_dev),
MINOR(inode->i_sb->s_dev), inode->i_ino,
fl->fl_type, fl->fl_flags,
fl->fl_start, fl->fl_end);
locks_delete_lock(before);
continue;
}
before = &fl->fl_next;
}
@ -2314,8 +2411,14 @@ static void lock_get_status(struct seq_file *f, struct file_lock *fl,
seq_printf(f, "%lld:%s ", id, pfx);
if (IS_POSIX(fl)) {
seq_printf(f, "%6s %s ",
(fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
if (fl->fl_flags & FL_ACCESS)
seq_printf(f, "ACCESS");
else if (IS_FILE_PVT(fl))
seq_printf(f, "FLPVT ");
else
seq_printf(f, "POSIX ");
seq_printf(f, " %s ",
(inode == NULL) ? "*NOINODE*" :
mandatory_lock(inode) ? "MANDATORY" : "ADVISORY ");
} else if (IS_FLOCK(fl)) {
@ -2385,6 +2488,7 @@ static int locks_show(struct seq_file *f, void *v)
}
static void *locks_start(struct seq_file *f, loff_t *pos)
__acquires(&blocked_lock_lock)
{
struct locks_iterator *iter = f->private;
@ -2403,6 +2507,7 @@ static void *locks_next(struct seq_file *f, void *v, loff_t *pos)
}
static void locks_stop(struct seq_file *f, void *v)
__releases(&blocked_lock_lock)
{
spin_unlock(&blocked_lock_lock);
lg_global_unlock(&file_lock_lglock);

View file

@ -2569,7 +2569,7 @@ static int handle_truncate(struct file *filp)
/*
* Refuse to truncate files with mandatory locks held on them.
*/
error = locks_verify_locked(inode);
error = locks_verify_locked(filp);
if (!error)
error = security_path_truncate(path);
if (!error) {

View file

@ -893,6 +893,7 @@ static inline int file_check_writeable(struct file *filp)
#define FL_SLEEP 128 /* A blocking lock */
#define FL_DOWNGRADE_PENDING 256 /* Lease is being downgraded */
#define FL_UNLOCK_PENDING 512 /* Lease is being broken */
#define FL_FILE_PVT 1024 /* lock is private to the file */
/*
* Special return value from posix_lock_file() and vfs_lock_file() for
@ -997,12 +998,12 @@ struct file_lock {
extern void send_sigio(struct fown_struct *fown, int fd, int band);
#ifdef CONFIG_FILE_LOCKING
extern int fcntl_getlk(struct file *, struct flock __user *);
extern int fcntl_getlk(struct file *, unsigned int, struct flock __user *);
extern int fcntl_setlk(unsigned int, struct file *, unsigned int,
struct flock __user *);
#if BITS_PER_LONG == 32
extern int fcntl_getlk64(struct file *, struct flock64 __user *);
extern int fcntl_getlk64(struct file *, unsigned int, struct flock64 __user *);
extern int fcntl_setlk64(unsigned int, struct file *, unsigned int,
struct flock64 __user *);
#endif
@ -1017,7 +1018,7 @@ extern struct file_lock * locks_alloc_lock(void);
extern void locks_copy_lock(struct file_lock *, struct file_lock *);
extern void __locks_copy_lock(struct file_lock *, const struct file_lock *);
extern void locks_remove_posix(struct file *, fl_owner_t);
extern void locks_remove_flock(struct file *);
extern void locks_remove_file(struct file *);
extern void locks_release_private(struct file_lock *);
extern void posix_test_lock(struct file *, struct file_lock *);
extern int posix_lock_file(struct file *, struct file_lock *, struct file_lock *);
@ -1035,7 +1036,8 @@ extern int lease_modify(struct file_lock **, int);
extern int lock_may_read(struct inode *, loff_t start, unsigned long count);
extern int lock_may_write(struct inode *, loff_t start, unsigned long count);
#else /* !CONFIG_FILE_LOCKING */
static inline int fcntl_getlk(struct file *file, struct flock __user *user)
static inline int fcntl_getlk(struct file *file, unsigned int cmd,
struct flock __user *user)
{
return -EINVAL;
}
@ -1047,7 +1049,8 @@ static inline int fcntl_setlk(unsigned int fd, struct file *file,
}
#if BITS_PER_LONG == 32
static inline int fcntl_getlk64(struct file *file, struct flock64 __user *user)
static inline int fcntl_getlk64(struct file *file, unsigned int cmd,
struct flock64 __user *user)
{
return -EINVAL;
}
@ -1088,7 +1091,7 @@ static inline void locks_remove_posix(struct file *filp, fl_owner_t owner)
return;
}
static inline void locks_remove_flock(struct file *filp)
static inline void locks_remove_file(struct file *filp)
{
return;
}
@ -1916,6 +1919,11 @@ extern int current_umask(void);
extern void ihold(struct inode * inode);
extern void iput(struct inode *);
static inline struct inode *file_inode(struct file *f)
{
return f->f_inode;
}
/* /sys/fs */
extern struct kobject *fs_kobj;
@ -1925,7 +1933,7 @@ extern struct kobject *fs_kobj;
#define FLOCK_VERIFY_WRITE 2
#ifdef CONFIG_FILE_LOCKING
extern int locks_mandatory_locked(struct inode *);
extern int locks_mandatory_locked(struct file *);
extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size_t);
/*
@ -1948,10 +1956,10 @@ static inline int mandatory_lock(struct inode *ino)
return IS_MANDLOCK(ino) && __mandatory_lock(ino);
}
static inline int locks_verify_locked(struct inode *inode)
static inline int locks_verify_locked(struct file *file)
{
if (mandatory_lock(inode))
return locks_mandatory_locked(inode);
if (mandatory_lock(file_inode(file)))
return locks_mandatory_locked(file);
return 0;
}
@ -1971,6 +1979,12 @@ static inline int locks_verify_truncate(struct inode *inode,
static inline int break_lease(struct inode *inode, unsigned int mode)
{
/*
* Since this check is lockless, we must ensure that any refcounts
* taken are done before checking inode->i_flock. Otherwise, we could
* end up racing with tasks trying to set a new lease on this file.
*/
smp_mb();
if (inode->i_flock)
return __break_lease(inode, mode, FL_LEASE);
return 0;
@ -2006,7 +2020,7 @@ static inline int break_deleg_wait(struct inode **delegated_inode)
}
#else /* !CONFIG_FILE_LOCKING */
static inline int locks_mandatory_locked(struct inode *inode)
static inline int locks_mandatory_locked(struct file *file)
{
return 0;
}
@ -2028,7 +2042,7 @@ static inline int mandatory_lock(struct inode *inode)
return 0;
}
static inline int locks_verify_locked(struct inode *inode)
static inline int locks_verify_locked(struct file *file)
{
return 0;
}
@ -2302,11 +2316,6 @@ static inline bool execute_ok(struct inode *inode)
return (inode->i_mode & S_IXUGO) || S_ISDIR(inode->i_mode);
}
static inline struct inode *file_inode(struct file *f)
{
return f->f_inode;
}
static inline void file_start_write(struct file *file)
{
if (!S_ISREG(file_inode(file)->i_mode))

View file

@ -132,6 +132,22 @@
#define F_GETOWNER_UIDS 17
#endif
/*
* fd "private" POSIX locks.
*
* Usually POSIX locks held by a process are released on *any* close and are
* not inherited across a fork().
*
* These cmd values will set locks that conflict with normal POSIX locks, but
* are "owned" by the opened file, not the process. This means that they are
* inherited across fork() like BSD (flock) locks, and they are only released
* automatically when the last reference to the the open file against which
* they were acquired is put.
*/
#define F_GETLKP 36
#define F_SETLKP 37
#define F_SETLKPW 38
#define F_OWNER_TID 0
#define F_OWNER_PID 1
#define F_OWNER_PGRP 2
@ -186,8 +202,6 @@ struct flock {
};
#endif
#ifndef CONFIG_64BIT
#ifndef HAVE_ARCH_STRUCT_FLOCK64
#ifndef __ARCH_FLOCK64_PAD
#define __ARCH_FLOCK64_PAD
@ -202,6 +216,5 @@ struct flock64 {
__ARCH_FLOCK64_PAD
};
#endif
#endif /* !CONFIG_64BIT */
#endif /* _ASM_GENERIC_FCNTL_H */

View file

@ -1299,7 +1299,7 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
/*
* Make sure there are no mandatory locks on the file.
*/
if (locks_verify_locked(inode))
if (locks_verify_locked(file))
return -EAGAIN;
vm_flags |= VM_SHARED | VM_MAYSHARE;

View file

@ -995,7 +995,7 @@ static int validate_mmap_request(struct file *file,
(file->f_mode & FMODE_WRITE))
return -EACCES;
if (locks_verify_locked(file_inode(file)))
if (locks_verify_locked(file))
return -EAGAIN;
if (!(capabilities & BDI_CAP_MAP_DIRECT))

View file

@ -3317,6 +3317,9 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
case F_GETLK:
case F_SETLK:
case F_SETLKW:
case F_GETLKP:
case F_SETLKP:
case F_SETLKPW:
#if BITS_PER_LONG == 32
case F_GETLK64:
case F_SETLK64: