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:
commit
f7789dc0d4
@ -3521,7 +3521,8 @@ F: include/scsi/libfcoe.h
|
|||||||
F: include/uapi/scsi/fc/
|
F: include/uapi/scsi/fc/
|
||||||
|
|
||||||
FILE LOCKING (flock() and fcntl()/lockf())
|
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
|
L: linux-fsdevel@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: include/linux/fcntl.h
|
F: include/linux/fcntl.h
|
||||||
|
@ -203,6 +203,9 @@ asmlinkage long sys_oabi_fcntl64(unsigned int fd, unsigned int cmd,
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
|
case F_GETLKP:
|
||||||
|
case F_SETLKP:
|
||||||
|
case F_SETLKPW:
|
||||||
case F_GETLK64:
|
case F_GETLK64:
|
||||||
case F_SETLK64:
|
case F_SETLK64:
|
||||||
case F_SETLKW64:
|
case F_SETLKW64:
|
||||||
|
35
fs/compat.c
35
fs/compat.c
@ -399,12 +399,28 @@ static int put_compat_flock64(struct flock *kfl, struct compat_flock64 __user *u
|
|||||||
}
|
}
|
||||||
#endif
|
#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_SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
|
||||||
compat_ulong_t, arg)
|
compat_ulong_t, arg)
|
||||||
{
|
{
|
||||||
mm_segment_t old_fs;
|
mm_segment_t old_fs;
|
||||||
struct flock f;
|
struct flock f;
|
||||||
long ret;
|
long ret;
|
||||||
|
unsigned int conv_cmd;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case F_GETLK:
|
case F_GETLK:
|
||||||
@ -441,16 +457,18 @@ COMPAT_SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
|
|||||||
case F_GETLK64:
|
case F_GETLK64:
|
||||||
case F_SETLK64:
|
case F_SETLK64:
|
||||||
case F_SETLKW64:
|
case F_SETLKW64:
|
||||||
|
case F_GETLKP:
|
||||||
|
case F_SETLKP:
|
||||||
|
case F_SETLKPW:
|
||||||
ret = get_compat_flock64(&f, compat_ptr(arg));
|
ret = get_compat_flock64(&f, compat_ptr(arg));
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
break;
|
break;
|
||||||
old_fs = get_fs();
|
old_fs = get_fs();
|
||||||
set_fs(KERNEL_DS);
|
set_fs(KERNEL_DS);
|
||||||
ret = sys_fcntl(fd, (cmd == F_GETLK64) ? F_GETLK :
|
conv_cmd = convert_fcntl_cmd(cmd);
|
||||||
((cmd == F_SETLK64) ? F_SETLK : F_SETLKW),
|
ret = sys_fcntl(fd, conv_cmd, (unsigned long)&f);
|
||||||
(unsigned long)&f);
|
|
||||||
set_fs(old_fs);
|
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 */
|
/* need to return lock information - see above for commentary */
|
||||||
if (f.l_start > COMPAT_LOFF_T_MAX)
|
if (f.l_start > COMPAT_LOFF_T_MAX)
|
||||||
ret = -EOVERFLOW;
|
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_SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd,
|
||||||
compat_ulong_t, arg)
|
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 -EINVAL;
|
||||||
|
}
|
||||||
return compat_sys_fcntl64(fd, cmd, arg);
|
return compat_sys_fcntl64(fd, cmd, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
37
fs/fcntl.c
37
fs/fcntl.c
@ -272,9 +272,19 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
|
|||||||
case F_SETFL:
|
case F_SETFL:
|
||||||
err = setfl(fd, filp, arg);
|
err = setfl(fd, filp, arg);
|
||||||
break;
|
break;
|
||||||
|
#if BITS_PER_LONG != 32
|
||||||
|
/* 32-bit arches must use fcntl64() */
|
||||||
|
case F_GETLKP:
|
||||||
|
#endif
|
||||||
case F_GETLK:
|
case F_GETLK:
|
||||||
err = fcntl_getlk(filp, (struct flock __user *) arg);
|
err = fcntl_getlk(filp, cmd, (struct flock __user *) arg);
|
||||||
break;
|
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_SETLK:
|
||||||
case F_SETLKW:
|
case F_SETLKW:
|
||||||
err = fcntl_setlk(fd, filp, cmd, (struct flock __user *) arg);
|
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;
|
goto out1;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case F_GETLK64:
|
case F_GETLK64:
|
||||||
err = fcntl_getlk64(f.file, (struct flock64 __user *) arg);
|
case F_GETLKP:
|
||||||
break;
|
err = fcntl_getlk64(f.file, cmd, (struct flock64 __user *) arg);
|
||||||
case F_SETLK64:
|
break;
|
||||||
case F_SETLKW64:
|
case F_SETLK64:
|
||||||
err = fcntl_setlk64(fd, f.file, cmd,
|
case F_SETLKW64:
|
||||||
(struct flock64 __user *) arg);
|
case F_SETLKP:
|
||||||
break;
|
case F_SETLKPW:
|
||||||
default:
|
err = fcntl_setlk64(fd, f.file, cmd,
|
||||||
err = do_fcntl(fd, cmd, arg, f.file);
|
(struct flock64 __user *) arg);
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
err = do_fcntl(fd, cmd, arg, f.file);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
out1:
|
out1:
|
||||||
fdput(f);
|
fdput(f);
|
||||||
|
@ -235,7 +235,7 @@ static void __fput(struct file *file)
|
|||||||
* in the file cleanup chain.
|
* in the file cleanup chain.
|
||||||
*/
|
*/
|
||||||
eventpoll_release(file);
|
eventpoll_release(file);
|
||||||
locks_remove_flock(file);
|
locks_remove_file(file);
|
||||||
|
|
||||||
if (unlikely(file->f_flags & FASYNC)) {
|
if (unlikely(file->f_flags & FASYNC)) {
|
||||||
if (file->f_op->fasync)
|
if (file->f_op->fasync)
|
||||||
|
441
fs/locks.c
441
fs/locks.c
@ -135,6 +135,7 @@
|
|||||||
#define IS_POSIX(fl) (fl->fl_flags & FL_POSIX)
|
#define IS_POSIX(fl) (fl->fl_flags & FL_POSIX)
|
||||||
#define IS_FLOCK(fl) (fl->fl_flags & FL_FLOCK)
|
#define IS_FLOCK(fl) (fl->fl_flags & FL_FLOCK)
|
||||||
#define IS_LEASE(fl) (fl->fl_flags & (FL_LEASE|FL_DELEG))
|
#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)
|
static bool lease_breaking(struct file_lock *fl)
|
||||||
{
|
{
|
||||||
@ -344,105 +345,81 @@ static int assign_type(struct file_lock *fl, long type)
|
|||||||
return 0;
|
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
|
/* Verify a "struct flock" and copy it to a "struct file_lock" as a POSIX
|
||||||
* style lock.
|
* style lock.
|
||||||
*/
|
*/
|
||||||
static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
|
static int flock_to_posix_lock(struct file *filp, struct file_lock *fl,
|
||||||
struct flock *l)
|
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) {
|
return flock64_to_posix_lock(filp, fl, &ll);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#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 */
|
/* default lease lock manager operations */
|
||||||
static void lease_break_callback(struct file_lock *fl)
|
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! */
|
/* Must be called with the i_lock held! */
|
||||||
static inline void
|
static void locks_insert_global_locks(struct file_lock *fl)
|
||||||
locks_insert_global_locks(struct file_lock *fl)
|
|
||||||
{
|
{
|
||||||
lg_local_lock(&file_lock_lglock);
|
lg_local_lock(&file_lock_lglock);
|
||||||
fl->fl_link_cpu = smp_processor_id();
|
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! */
|
/* Must be called with the i_lock held! */
|
||||||
static inline void
|
static void locks_delete_global_locks(struct file_lock *fl)
|
||||||
locks_delete_global_locks(struct file_lock *fl)
|
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Avoid taking lock if already unhashed. This is safe since this check
|
* 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;
|
return (unsigned long)fl->fl_owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static void locks_insert_global_blocked(struct file_lock *waiter)
|
||||||
locks_insert_global_blocked(struct file_lock *waiter)
|
|
||||||
{
|
{
|
||||||
hash_add(blocked_hash, &waiter->fl_link, posix_owner_key(waiter));
|
hash_add(blocked_hash, &waiter->fl_link, posix_owner_key(waiter));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void
|
static void locks_delete_global_blocked(struct file_lock *waiter)
|
||||||
locks_delete_global_blocked(struct file_lock *waiter)
|
|
||||||
{
|
{
|
||||||
hash_del(&waiter->fl_link);
|
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.
|
* it seems like the reasonable thing to do.
|
||||||
*
|
*
|
||||||
* Must be called with both the i_lock and blocked_lock_lock held. The fl_block
|
* 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
|
* 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.
|
* 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));
|
BUG_ON(!list_empty(&waiter->fl_block));
|
||||||
waiter->fl_next = blocker;
|
waiter->fl_next = blocker;
|
||||||
list_add_tail(&waiter->fl_block, &blocker->fl_block);
|
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);
|
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);
|
locks_insert_global_locks(fl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Delete a lock and then free it.
|
* locks_delete_lock - Delete a lock and then free it.
|
||||||
* Wake up processes that are blocked waiting for this lock,
|
* @thisfl_p: pointer that points to the fl_next field of the previous
|
||||||
* notify the FS that the lock has been cleared and
|
* inode->i_flock list entry
|
||||||
* finally free the lock.
|
*
|
||||||
|
* 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!
|
* 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;
|
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);
|
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);
|
locks_free_lock(fl);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -769,8 +757,16 @@ EXPORT_SYMBOL(posix_test_lock);
|
|||||||
* Note: the above assumption may not be true when handling 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
|
* 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.
|
* of tasks (such as posix threads) sharing the same open file table.
|
||||||
*
|
|
||||||
* To handle those cases, we just bail out after a few iterations.
|
* 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
|
#define MAX_DEADLK_ITERATIONS 10
|
||||||
@ -793,6 +789,13 @@ static int posix_locks_deadlock(struct file_lock *caller_fl,
|
|||||||
{
|
{
|
||||||
int i = 0;
|
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))) {
|
while ((block_fl = what_owner_is_waiting_for(block_fl))) {
|
||||||
if (i++ > MAX_DEADLK_ITERATIONS)
|
if (i++ > MAX_DEADLK_ITERATIONS)
|
||||||
return 0;
|
return 0;
|
||||||
@ -1152,13 +1155,14 @@ EXPORT_SYMBOL(posix_lock_file_wait);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* locks_mandatory_locked - Check for an active lock
|
* 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.
|
* Searches the inode's list of locks to find any POSIX locks which conflict.
|
||||||
* This function is called from locks_verify_locked() only.
|
* 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;
|
fl_owner_t owner = current->files;
|
||||||
struct file_lock *fl;
|
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) {
|
for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
|
||||||
if (!IS_POSIX(fl))
|
if (!IS_POSIX(fl))
|
||||||
continue;
|
continue;
|
||||||
if (fl->fl_owner != owner)
|
if (fl->fl_owner != owner && fl->fl_owner != (fl_owner_t)file)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
spin_unlock(&inode->i_lock);
|
spin_unlock(&inode->i_lock);
|
||||||
@ -1195,19 +1199,30 @@ int locks_mandatory_area(int read_write, struct inode *inode,
|
|||||||
{
|
{
|
||||||
struct file_lock fl;
|
struct file_lock fl;
|
||||||
int error;
|
int error;
|
||||||
|
bool sleep = false;
|
||||||
|
|
||||||
locks_init_lock(&fl);
|
locks_init_lock(&fl);
|
||||||
fl.fl_owner = current->files;
|
|
||||||
fl.fl_pid = current->tgid;
|
fl.fl_pid = current->tgid;
|
||||||
fl.fl_file = filp;
|
fl.fl_file = filp;
|
||||||
fl.fl_flags = FL_POSIX | FL_ACCESS;
|
fl.fl_flags = FL_POSIX | FL_ACCESS;
|
||||||
if (filp && !(filp->f_flags & O_NONBLOCK))
|
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_type = (read_write == FLOCK_VERIFY_WRITE) ? F_WRLCK : F_RDLCK;
|
||||||
fl.fl_start = offset;
|
fl.fl_start = offset;
|
||||||
fl.fl_end = offset + count - 1;
|
fl.fl_end = offset + count - 1;
|
||||||
|
|
||||||
for (;;) {
|
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);
|
error = __posix_lock_file(inode, &fl, NULL);
|
||||||
if (error != FILE_LOCK_DEFERRED)
|
if (error != FILE_LOCK_DEFERRED)
|
||||||
break;
|
break;
|
||||||
@ -1472,6 +1487,32 @@ int fcntl_getlease(struct file *filp)
|
|||||||
return type;
|
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)
|
static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp)
|
||||||
{
|
{
|
||||||
struct file_lock *fl, **before, **my_before = NULL, *lease;
|
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;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = -EAGAIN;
|
error = check_conflicting_open(dentry, arg);
|
||||||
if ((arg == F_RDLCK) && (atomic_read(&inode->i_writecount) > 0))
|
if (error)
|
||||||
goto out;
|
|
||||||
if ((arg == F_WRLCK)
|
|
||||||
&& ((d_count(dentry) > 1)
|
|
||||||
|| (atomic_read(&inode->i_count) > 1)))
|
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1549,7 +1586,19 @@ static int generic_add_lease(struct file *filp, long arg, struct file_lock **flp
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
locks_insert_lock(before, lease);
|
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:
|
out:
|
||||||
if (is_deleg)
|
if (is_deleg)
|
||||||
mutex_unlock(&inode->i_mutex);
|
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)
|
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
|
#if BITS_PER_LONG == 32
|
||||||
/*
|
/*
|
||||||
* Make sure we can represent the posix lock via
|
* 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
|
#if BITS_PER_LONG == 32
|
||||||
static void posix_lock_to_flock64(struct flock64 *flock, struct file_lock *fl)
|
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_start = fl->fl_start;
|
||||||
flock->l_len = fl->fl_end == OFFSET_MAX ? 0 :
|
flock->l_len = fl->fl_end == OFFSET_MAX ? 0 :
|
||||||
fl->fl_end - fl->fl_start + 1;
|
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.
|
/* Report the first existing lock that would conflict with l.
|
||||||
* This implements the F_GETLK command of fcntl().
|
* 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 file_lock file_lock;
|
||||||
struct flock flock;
|
struct flock flock;
|
||||||
@ -1893,6 +1942,16 @@ int fcntl_getlk(struct file *filp, struct flock __user *l)
|
|||||||
if (error)
|
if (error)
|
||||||
goto out;
|
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);
|
error = vfs_test_lock(filp, &file_lock);
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
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);
|
error = flock_to_posix_lock(filp, file_lock, &flock);
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
if (cmd == F_SETLKW) {
|
|
||||||
file_lock->fl_flags |= FL_SLEEP;
|
/*
|
||||||
}
|
* If the cmd is requesting file-private locks, then set the
|
||||||
|
* FL_FILE_PVT flag and override the owner.
|
||||||
error = -EBADF;
|
*/
|
||||||
switch (flock.l_type) {
|
switch (cmd) {
|
||||||
case F_RDLCK:
|
case F_SETLKP:
|
||||||
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:
|
|
||||||
error = -EINVAL;
|
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);
|
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.
|
/* Report the first existing lock that would conflict with l.
|
||||||
* This implements the F_GETLK command of fcntl().
|
* 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 file_lock file_lock;
|
||||||
struct flock64 flock;
|
struct flock64 flock;
|
||||||
@ -2078,6 +2144,16 @@ int fcntl_getlk64(struct file *filp, struct flock64 __user *l)
|
|||||||
if (error)
|
if (error)
|
||||||
goto out;
|
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);
|
error = vfs_test_lock(filp, &file_lock);
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
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);
|
error = flock64_to_posix_lock(filp, file_lock, &flock);
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
if (cmd == F_SETLKW64) {
|
|
||||||
file_lock->fl_flags |= FL_SLEEP;
|
/*
|
||||||
}
|
* If the cmd is requesting file-private locks, then set the
|
||||||
|
* FL_FILE_PVT flag and override the owner.
|
||||||
error = -EBADF;
|
*/
|
||||||
switch (flock.l_type) {
|
switch (cmd) {
|
||||||
case F_RDLCK:
|
case F_SETLKP:
|
||||||
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:
|
|
||||||
error = -EINVAL;
|
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);
|
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.
|
* 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 inode * inode = file_inode(filp);
|
||||||
struct file_lock *fl;
|
struct file_lock *fl;
|
||||||
@ -2218,6 +2301,8 @@ void locks_remove_flock(struct file *filp)
|
|||||||
if (!inode->i_flock)
|
if (!inode->i_flock)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
locks_remove_posix(filp, (fl_owner_t)filp);
|
||||||
|
|
||||||
if (filp->f_op->flock) {
|
if (filp->f_op->flock) {
|
||||||
struct file_lock fl = {
|
struct file_lock fl = {
|
||||||
.fl_pid = current->tgid,
|
.fl_pid = current->tgid,
|
||||||
@ -2236,16 +2321,28 @@ void locks_remove_flock(struct file *filp)
|
|||||||
|
|
||||||
while ((fl = *before) != NULL) {
|
while ((fl = *before) != NULL) {
|
||||||
if (fl->fl_file == filp) {
|
if (fl->fl_file == filp) {
|
||||||
if (IS_FLOCK(fl)) {
|
|
||||||
locks_delete_lock(before);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (IS_LEASE(fl)) {
|
if (IS_LEASE(fl)) {
|
||||||
lease_modify(before, F_UNLCK);
|
lease_modify(before, F_UNLCK);
|
||||||
continue;
|
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;
|
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);
|
seq_printf(f, "%lld:%s ", id, pfx);
|
||||||
if (IS_POSIX(fl)) {
|
if (IS_POSIX(fl)) {
|
||||||
seq_printf(f, "%6s %s ",
|
if (fl->fl_flags & FL_ACCESS)
|
||||||
(fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
|
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*" :
|
(inode == NULL) ? "*NOINODE*" :
|
||||||
mandatory_lock(inode) ? "MANDATORY" : "ADVISORY ");
|
mandatory_lock(inode) ? "MANDATORY" : "ADVISORY ");
|
||||||
} else if (IS_FLOCK(fl)) {
|
} 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)
|
static void *locks_start(struct seq_file *f, loff_t *pos)
|
||||||
|
__acquires(&blocked_lock_lock)
|
||||||
{
|
{
|
||||||
struct locks_iterator *iter = f->private;
|
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)
|
static void locks_stop(struct seq_file *f, void *v)
|
||||||
|
__releases(&blocked_lock_lock)
|
||||||
{
|
{
|
||||||
spin_unlock(&blocked_lock_lock);
|
spin_unlock(&blocked_lock_lock);
|
||||||
lg_global_unlock(&file_lock_lglock);
|
lg_global_unlock(&file_lock_lglock);
|
||||||
|
@ -2569,7 +2569,7 @@ static int handle_truncate(struct file *filp)
|
|||||||
/*
|
/*
|
||||||
* Refuse to truncate files with mandatory locks held on them.
|
* Refuse to truncate files with mandatory locks held on them.
|
||||||
*/
|
*/
|
||||||
error = locks_verify_locked(inode);
|
error = locks_verify_locked(filp);
|
||||||
if (!error)
|
if (!error)
|
||||||
error = security_path_truncate(path);
|
error = security_path_truncate(path);
|
||||||
if (!error) {
|
if (!error) {
|
||||||
|
@ -893,6 +893,7 @@ static inline int file_check_writeable(struct file *filp)
|
|||||||
#define FL_SLEEP 128 /* A blocking lock */
|
#define FL_SLEEP 128 /* A blocking lock */
|
||||||
#define FL_DOWNGRADE_PENDING 256 /* Lease is being downgraded */
|
#define FL_DOWNGRADE_PENDING 256 /* Lease is being downgraded */
|
||||||
#define FL_UNLOCK_PENDING 512 /* Lease is being broken */
|
#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
|
* 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);
|
extern void send_sigio(struct fown_struct *fown, int fd, int band);
|
||||||
|
|
||||||
#ifdef CONFIG_FILE_LOCKING
|
#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,
|
extern int fcntl_setlk(unsigned int, struct file *, unsigned int,
|
||||||
struct flock __user *);
|
struct flock __user *);
|
||||||
|
|
||||||
#if BITS_PER_LONG == 32
|
#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,
|
extern int fcntl_setlk64(unsigned int, struct file *, unsigned int,
|
||||||
struct flock64 __user *);
|
struct flock64 __user *);
|
||||||
#endif
|
#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 *, struct file_lock *);
|
||||||
extern void __locks_copy_lock(struct file_lock *, const 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_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 locks_release_private(struct file_lock *);
|
||||||
extern void posix_test_lock(struct file *, 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 *);
|
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_read(struct inode *, loff_t start, unsigned long count);
|
||||||
extern int lock_may_write(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 */
|
#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;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -1047,7 +1049,8 @@ static inline int fcntl_setlk(unsigned int fd, struct file *file,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if BITS_PER_LONG == 32
|
#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;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
@ -1088,7 +1091,7 @@ static inline void locks_remove_posix(struct file *filp, fl_owner_t owner)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void locks_remove_flock(struct file *filp)
|
static inline void locks_remove_file(struct file *filp)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1916,6 +1919,11 @@ extern int current_umask(void);
|
|||||||
extern void ihold(struct inode * inode);
|
extern void ihold(struct inode * inode);
|
||||||
extern void iput(struct inode *);
|
extern void iput(struct inode *);
|
||||||
|
|
||||||
|
static inline struct inode *file_inode(struct file *f)
|
||||||
|
{
|
||||||
|
return f->f_inode;
|
||||||
|
}
|
||||||
|
|
||||||
/* /sys/fs */
|
/* /sys/fs */
|
||||||
extern struct kobject *fs_kobj;
|
extern struct kobject *fs_kobj;
|
||||||
|
|
||||||
@ -1925,7 +1933,7 @@ extern struct kobject *fs_kobj;
|
|||||||
#define FLOCK_VERIFY_WRITE 2
|
#define FLOCK_VERIFY_WRITE 2
|
||||||
|
|
||||||
#ifdef CONFIG_FILE_LOCKING
|
#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);
|
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);
|
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))
|
if (mandatory_lock(file_inode(file)))
|
||||||
return locks_mandatory_locked(inode);
|
return locks_mandatory_locked(file);
|
||||||
return 0;
|
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)
|
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)
|
if (inode->i_flock)
|
||||||
return __break_lease(inode, mode, FL_LEASE);
|
return __break_lease(inode, mode, FL_LEASE);
|
||||||
return 0;
|
return 0;
|
||||||
@ -2006,7 +2020,7 @@ static inline int break_deleg_wait(struct inode **delegated_inode)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#else /* !CONFIG_FILE_LOCKING */
|
#else /* !CONFIG_FILE_LOCKING */
|
||||||
static inline int locks_mandatory_locked(struct inode *inode)
|
static inline int locks_mandatory_locked(struct file *file)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -2028,7 +2042,7 @@ static inline int mandatory_lock(struct inode *inode)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int locks_verify_locked(struct inode *inode)
|
static inline int locks_verify_locked(struct file *file)
|
||||||
{
|
{
|
||||||
return 0;
|
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);
|
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)
|
static inline void file_start_write(struct file *file)
|
||||||
{
|
{
|
||||||
if (!S_ISREG(file_inode(file)->i_mode))
|
if (!S_ISREG(file_inode(file)->i_mode))
|
||||||
|
@ -132,6 +132,22 @@
|
|||||||
#define F_GETOWNER_UIDS 17
|
#define F_GETOWNER_UIDS 17
|
||||||
#endif
|
#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_TID 0
|
||||||
#define F_OWNER_PID 1
|
#define F_OWNER_PID 1
|
||||||
#define F_OWNER_PGRP 2
|
#define F_OWNER_PGRP 2
|
||||||
@ -186,8 +202,6 @@ struct flock {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_64BIT
|
|
||||||
|
|
||||||
#ifndef HAVE_ARCH_STRUCT_FLOCK64
|
#ifndef HAVE_ARCH_STRUCT_FLOCK64
|
||||||
#ifndef __ARCH_FLOCK64_PAD
|
#ifndef __ARCH_FLOCK64_PAD
|
||||||
#define __ARCH_FLOCK64_PAD
|
#define __ARCH_FLOCK64_PAD
|
||||||
@ -202,6 +216,5 @@ struct flock64 {
|
|||||||
__ARCH_FLOCK64_PAD
|
__ARCH_FLOCK64_PAD
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
#endif /* !CONFIG_64BIT */
|
|
||||||
|
|
||||||
#endif /* _ASM_GENERIC_FCNTL_H */
|
#endif /* _ASM_GENERIC_FCNTL_H */
|
||||||
|
@ -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.
|
* Make sure there are no mandatory locks on the file.
|
||||||
*/
|
*/
|
||||||
if (locks_verify_locked(inode))
|
if (locks_verify_locked(file))
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
vm_flags |= VM_SHARED | VM_MAYSHARE;
|
vm_flags |= VM_SHARED | VM_MAYSHARE;
|
||||||
|
@ -995,7 +995,7 @@ static int validate_mmap_request(struct file *file,
|
|||||||
(file->f_mode & FMODE_WRITE))
|
(file->f_mode & FMODE_WRITE))
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
|
|
||||||
if (locks_verify_locked(file_inode(file)))
|
if (locks_verify_locked(file))
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
|
|
||||||
if (!(capabilities & BDI_CAP_MAP_DIRECT))
|
if (!(capabilities & BDI_CAP_MAP_DIRECT))
|
||||||
|
@ -3317,6 +3317,9 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd,
|
|||||||
case F_GETLK:
|
case F_GETLK:
|
||||||
case F_SETLK:
|
case F_SETLK:
|
||||||
case F_SETLKW:
|
case F_SETLKW:
|
||||||
|
case F_GETLKP:
|
||||||
|
case F_SETLKP:
|
||||||
|
case F_SETLKPW:
|
||||||
#if BITS_PER_LONG == 32
|
#if BITS_PER_LONG == 32
|
||||||
case F_GETLK64:
|
case F_GETLK64:
|
||||||
case F_SETLK64:
|
case F_SETLK64:
|
||||||
|
Loading…
Reference in New Issue
Block a user