Merge branch 'locks' of git://linux-nfs.org/~bfields/linux
* 'locks' of git://linux-nfs.org/~bfields/linux: nfsd: remove IS_ISMNDLCK macro Rework /proc/locks via seq_files and seq_list helpers fs/locks.c: use list_for_each_entry() instead of list_for_each() NFS: clean up explicit check for mandatory locks AFS: clean up explicit check for mandatory locks 9PFS: clean up explicit check for mandatory locks GFS2: clean up explicit check for mandatory locks Cleanup macros for distinguishing mandatory locks Documentation: move locks.txt in filesystems/ locks: add warning about mandatory locking races Documentation: move mandatory locking documentation to filesystems/ locks: Fix potential OOPS in generic_setlease() Use list_first_entry in locks_wake_up_blocks locks: fix flock_lock_file() comment Memory shortage can result in inconsistent flocks state locks: kill redundant local variable locks: reverse order of posix_locks_conflict() arguments
This commit is contained in:
commit
541010e4b8
|
@ -145,7 +145,7 @@ fb/
|
||||||
feature-removal-schedule.txt
|
feature-removal-schedule.txt
|
||||||
- list of files and features that are going to be removed.
|
- list of files and features that are going to be removed.
|
||||||
filesystems/
|
filesystems/
|
||||||
- directory with info on the various filesystems that Linux supports.
|
- info on the vfs and the various filesystems that Linux supports.
|
||||||
firmware_class/
|
firmware_class/
|
||||||
- request_firmware() hotplug interface info.
|
- request_firmware() hotplug interface info.
|
||||||
floppy.txt
|
floppy.txt
|
||||||
|
@ -230,8 +230,6 @@ local_ops.txt
|
||||||
- semantics and behavior of local atomic operations.
|
- semantics and behavior of local atomic operations.
|
||||||
lockdep-design.txt
|
lockdep-design.txt
|
||||||
- documentation on the runtime locking correctness validator.
|
- documentation on the runtime locking correctness validator.
|
||||||
locks.txt
|
|
||||||
- info on file locking implementations, flock() vs. fcntl(), etc.
|
|
||||||
logo.gif
|
logo.gif
|
||||||
- full colour GIF image of Linux logo (penguin - Tux).
|
- full colour GIF image of Linux logo (penguin - Tux).
|
||||||
logo.txt
|
logo.txt
|
||||||
|
@ -240,8 +238,6 @@ m68k/
|
||||||
- directory with info about Linux on Motorola 68k architecture.
|
- directory with info about Linux on Motorola 68k architecture.
|
||||||
magic-number.txt
|
magic-number.txt
|
||||||
- list of magic numbers used to mark/protect kernel data structures.
|
- list of magic numbers used to mark/protect kernel data structures.
|
||||||
mandatory.txt
|
|
||||||
- info on the Linux implementation of Sys V mandatory file locking.
|
|
||||||
mca.txt
|
mca.txt
|
||||||
- info on supporting Micro Channel Architecture (e.g. PS/2) systems.
|
- info on supporting Micro Channel Architecture (e.g. PS/2) systems.
|
||||||
md.txt
|
md.txt
|
||||||
|
|
|
@ -52,6 +52,10 @@ isofs.txt
|
||||||
- info and mount options for the ISO 9660 (CDROM) filesystem.
|
- info and mount options for the ISO 9660 (CDROM) filesystem.
|
||||||
jfs.txt
|
jfs.txt
|
||||||
- info and mount options for the JFS filesystem.
|
- info and mount options for the JFS filesystem.
|
||||||
|
locks.txt
|
||||||
|
- info on file locking implementations, flock() vs. fcntl(), etc.
|
||||||
|
mandatory-locking.txt
|
||||||
|
- info on the Linux implementation of Sys V mandatory file locking.
|
||||||
ncpfs.txt
|
ncpfs.txt
|
||||||
- info on Novell Netware(tm) filesystem using NCP protocol.
|
- info on Novell Netware(tm) filesystem using NCP protocol.
|
||||||
ntfs.txt
|
ntfs.txt
|
||||||
|
|
|
@ -53,11 +53,11 @@ fcntl(), with all the problems that implies.
|
||||||
1.3 Mandatory Locking As A Mount Option
|
1.3 Mandatory Locking As A Mount Option
|
||||||
---------------------------------------
|
---------------------------------------
|
||||||
|
|
||||||
Mandatory locking, as described in 'Documentation/mandatory.txt' was prior
|
Mandatory locking, as described in 'Documentation/filesystems/mandatory.txt'
|
||||||
to this release a general configuration option that was valid for all
|
was prior to this release a general configuration option that was valid for
|
||||||
mounted filesystems. This had a number of inherent dangers, not the least
|
all mounted filesystems. This had a number of inherent dangers, not the
|
||||||
of which was the ability to freeze an NFS server by asking it to read a
|
least of which was the ability to freeze an NFS server by asking it to read
|
||||||
file for which a mandatory lock existed.
|
a file for which a mandatory lock existed.
|
||||||
|
|
||||||
From this release of the kernel, mandatory locking can be turned on and off
|
From this release of the kernel, mandatory locking can be turned on and off
|
||||||
on a per-filesystem basis, using the mount options 'mand' and 'nomand'.
|
on a per-filesystem basis, using the mount options 'mand' and 'nomand'.
|
|
@ -3,7 +3,26 @@
|
||||||
Andy Walker <andy@lysaker.kvaerner.no>
|
Andy Walker <andy@lysaker.kvaerner.no>
|
||||||
|
|
||||||
15 April 1996
|
15 April 1996
|
||||||
|
(Updated September 2007)
|
||||||
|
|
||||||
|
0. Why you should avoid mandatory locking
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
The Linux implementation is prey to a number of difficult-to-fix race
|
||||||
|
conditions which in practice make it not dependable:
|
||||||
|
|
||||||
|
- The write system call checks for a mandatory lock only once
|
||||||
|
at its start. It is therefore possible for a lock request to
|
||||||
|
be granted after this check but before the data is modified.
|
||||||
|
A process may then see file data change even while a mandatory
|
||||||
|
lock was held.
|
||||||
|
- Similarly, an exclusive lock may be granted on a file after
|
||||||
|
the kernel has decided to proceed with a read, but before the
|
||||||
|
read has actually completed, and the reading process may see
|
||||||
|
the file data in a state which should not have been visible
|
||||||
|
to it.
|
||||||
|
- Similar races make the claimed mutual exclusion between lock
|
||||||
|
and mmap similarly unreliable.
|
||||||
|
|
||||||
1. What is mandatory locking?
|
1. What is mandatory locking?
|
||||||
------------------------------
|
------------------------------
|
|
@ -105,7 +105,7 @@ static int v9fs_file_lock(struct file *filp, int cmd, struct file_lock *fl)
|
||||||
P9_DPRINTK(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl);
|
P9_DPRINTK(P9_DEBUG_VFS, "filp: %p lock: %p\n", filp, fl);
|
||||||
|
|
||||||
/* No mandatory locks */
|
/* No mandatory locks */
|
||||||
if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
|
if (__mandatory_lock(inode))
|
||||||
return -ENOLCK;
|
return -ENOLCK;
|
||||||
|
|
||||||
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {
|
||||||
|
|
|
@ -524,8 +524,7 @@ int afs_lock(struct file *file, int cmd, struct file_lock *fl)
|
||||||
(long long) fl->fl_start, (long long) fl->fl_end);
|
(long long) fl->fl_start, (long long) fl->fl_end);
|
||||||
|
|
||||||
/* AFS doesn't support mandatory locks */
|
/* AFS doesn't support mandatory locks */
|
||||||
if ((vnode->vfs_inode.i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
|
if (__mandatory_lock(&vnode->vfs_inode) && fl->fl_type != F_UNLCK)
|
||||||
fl->fl_type != F_UNLCK)
|
|
||||||
return -ENOLCK;
|
return -ENOLCK;
|
||||||
|
|
||||||
if (IS_GETLK(cmd))
|
if (IS_GETLK(cmd))
|
||||||
|
|
|
@ -535,7 +535,7 @@ static int gfs2_lock(struct file *file, int cmd, struct file_lock *fl)
|
||||||
|
|
||||||
if (!(fl->fl_flags & FL_POSIX))
|
if (!(fl->fl_flags & FL_POSIX))
|
||||||
return -ENOLCK;
|
return -ENOLCK;
|
||||||
if ((ip->i_inode.i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
|
if (__mandatory_lock(&ip->i_inode))
|
||||||
return -ENOLCK;
|
return -ENOLCK;
|
||||||
|
|
||||||
if (sdp->sd_args.ar_localflocks) {
|
if (sdp->sd_args.ar_localflocks) {
|
||||||
|
@ -636,7 +636,7 @@ static int gfs2_flock(struct file *file, int cmd, struct file_lock *fl)
|
||||||
|
|
||||||
if (!(fl->fl_flags & FL_FLOCK))
|
if (!(fl->fl_flags & FL_FLOCK))
|
||||||
return -ENOLCK;
|
return -ENOLCK;
|
||||||
if ((ip->i_inode.i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
|
if (__mandatory_lock(&ip->i_inode))
|
||||||
return -ENOLCK;
|
return -ENOLCK;
|
||||||
|
|
||||||
if (sdp->sd_args.ar_localflocks)
|
if (sdp->sd_args.ar_localflocks)
|
||||||
|
|
196
fs/locks.c
196
fs/locks.c
|
@ -534,7 +534,9 @@ static void locks_insert_block(struct file_lock *blocker,
|
||||||
static void locks_wake_up_blocks(struct file_lock *blocker)
|
static void locks_wake_up_blocks(struct file_lock *blocker)
|
||||||
{
|
{
|
||||||
while (!list_empty(&blocker->fl_block)) {
|
while (!list_empty(&blocker->fl_block)) {
|
||||||
struct file_lock *waiter = list_entry(blocker->fl_block.next,
|
struct file_lock *waiter;
|
||||||
|
|
||||||
|
waiter = list_first_entry(&blocker->fl_block,
|
||||||
struct file_lock, fl_block);
|
struct file_lock, fl_block);
|
||||||
__locks_delete_block(waiter);
|
__locks_delete_block(waiter);
|
||||||
if (waiter->fl_lmops && waiter->fl_lmops->fl_notify)
|
if (waiter->fl_lmops && waiter->fl_lmops->fl_notify)
|
||||||
|
@ -668,7 +670,7 @@ posix_test_lock(struct file *filp, struct file_lock *fl)
|
||||||
for (cfl = filp->f_path.dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
|
for (cfl = filp->f_path.dentry->d_inode->i_flock; cfl; cfl = cfl->fl_next) {
|
||||||
if (!IS_POSIX(cfl))
|
if (!IS_POSIX(cfl))
|
||||||
continue;
|
continue;
|
||||||
if (posix_locks_conflict(cfl, fl))
|
if (posix_locks_conflict(fl, cfl))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (cfl)
|
if (cfl)
|
||||||
|
@ -698,13 +700,12 @@ EXPORT_SYMBOL(posix_test_lock);
|
||||||
static int posix_locks_deadlock(struct file_lock *caller_fl,
|
static int posix_locks_deadlock(struct file_lock *caller_fl,
|
||||||
struct file_lock *block_fl)
|
struct file_lock *block_fl)
|
||||||
{
|
{
|
||||||
struct list_head *tmp;
|
struct file_lock *fl;
|
||||||
|
|
||||||
next_task:
|
next_task:
|
||||||
if (posix_same_owner(caller_fl, block_fl))
|
if (posix_same_owner(caller_fl, block_fl))
|
||||||
return 1;
|
return 1;
|
||||||
list_for_each(tmp, &blocked_list) {
|
list_for_each_entry(fl, &blocked_list, fl_link) {
|
||||||
struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);
|
|
||||||
if (posix_same_owner(fl, block_fl)) {
|
if (posix_same_owner(fl, block_fl)) {
|
||||||
fl = fl->fl_next;
|
fl = fl->fl_next;
|
||||||
block_fl = fl;
|
block_fl = fl;
|
||||||
|
@ -715,8 +716,7 @@ next_task:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks
|
/* Try to create a FLOCK lock on filp. We always insert new FLOCK locks
|
||||||
* at the head of the list, but that's secret knowledge known only to
|
* after any leases, but before any posix locks.
|
||||||
* flock_lock_file and posix_lock_file.
|
|
||||||
*
|
*
|
||||||
* Note that if called with an FL_EXISTS argument, the caller may determine
|
* Note that if called with an FL_EXISTS argument, the caller may determine
|
||||||
* whether or not a lock was successfully freed by testing the return
|
* whether or not a lock was successfully freed by testing the return
|
||||||
|
@ -733,6 +733,15 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
|
||||||
lock_kernel();
|
lock_kernel();
|
||||||
if (request->fl_flags & FL_ACCESS)
|
if (request->fl_flags & FL_ACCESS)
|
||||||
goto find_conflict;
|
goto find_conflict;
|
||||||
|
|
||||||
|
if (request->fl_type != F_UNLCK) {
|
||||||
|
error = -ENOMEM;
|
||||||
|
new_fl = locks_alloc_lock();
|
||||||
|
if (new_fl == NULL)
|
||||||
|
goto out;
|
||||||
|
error = 0;
|
||||||
|
}
|
||||||
|
|
||||||
for_each_lock(inode, before) {
|
for_each_lock(inode, before) {
|
||||||
struct file_lock *fl = *before;
|
struct file_lock *fl = *before;
|
||||||
if (IS_POSIX(fl))
|
if (IS_POSIX(fl))
|
||||||
|
@ -754,10 +763,6 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = -ENOMEM;
|
|
||||||
new_fl = locks_alloc_lock();
|
|
||||||
if (new_fl == NULL)
|
|
||||||
goto out;
|
|
||||||
/*
|
/*
|
||||||
* If a higher-priority process was blocked on the old file lock,
|
* If a higher-priority process was blocked on the old file lock,
|
||||||
* give it the opportunity to lock the file.
|
* give it the opportunity to lock the file.
|
||||||
|
@ -819,7 +824,7 @@ static int __posix_lock_file(struct inode *inode, struct file_lock *request, str
|
||||||
lock_kernel();
|
lock_kernel();
|
||||||
if (request->fl_type != F_UNLCK) {
|
if (request->fl_type != F_UNLCK) {
|
||||||
for_each_lock(inode, before) {
|
for_each_lock(inode, before) {
|
||||||
struct file_lock *fl = *before;
|
fl = *before;
|
||||||
if (!IS_POSIX(fl))
|
if (!IS_POSIX(fl))
|
||||||
continue;
|
continue;
|
||||||
if (!posix_locks_conflict(request, fl))
|
if (!posix_locks_conflict(request, fl))
|
||||||
|
@ -1113,7 +1118,7 @@ int locks_mandatory_area(int read_write, struct inode *inode,
|
||||||
* If we've been sleeping someone might have
|
* If we've been sleeping someone might have
|
||||||
* changed the permissions behind our back.
|
* changed the permissions behind our back.
|
||||||
*/
|
*/
|
||||||
if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
|
if (__mandatory_lock(inode))
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1337,6 +1342,7 @@ int fcntl_getlease(struct file *filp)
|
||||||
int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
|
int generic_setlease(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;
|
||||||
|
struct file_lock *new_fl = NULL;
|
||||||
struct dentry *dentry = filp->f_path.dentry;
|
struct dentry *dentry = filp->f_path.dentry;
|
||||||
struct inode *inode = dentry->d_inode;
|
struct inode *inode = dentry->d_inode;
|
||||||
int error, rdlease_count = 0, wrlease_count = 0;
|
int error, rdlease_count = 0, wrlease_count = 0;
|
||||||
|
@ -1363,6 +1369,11 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
|
||||||
|| (atomic_read(&inode->i_count) > 1)))
|
|| (atomic_read(&inode->i_count) > 1)))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
error = -ENOMEM;
|
||||||
|
new_fl = locks_alloc_lock();
|
||||||
|
if (new_fl == NULL)
|
||||||
|
goto out;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* At this point, we know that if there is an exclusive
|
* At this point, we know that if there is an exclusive
|
||||||
* lease on this file, then we hold it on this filp
|
* lease on this file, then we hold it on this filp
|
||||||
|
@ -1405,18 +1416,15 @@ int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
|
||||||
if (!leases_enable)
|
if (!leases_enable)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
error = -ENOMEM;
|
locks_copy_lock(new_fl, lease);
|
||||||
fl = locks_alloc_lock();
|
locks_insert_lock(before, new_fl);
|
||||||
if (fl == NULL)
|
|
||||||
goto out;
|
|
||||||
|
|
||||||
locks_copy_lock(fl, lease);
|
*flp = new_fl;
|
||||||
|
return 0;
|
||||||
|
|
||||||
locks_insert_lock(before, fl);
|
|
||||||
|
|
||||||
*flp = fl;
|
|
||||||
error = 0;
|
|
||||||
out:
|
out:
|
||||||
|
if (new_fl != NULL)
|
||||||
|
locks_free_lock(new_fl);
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(generic_setlease);
|
EXPORT_SYMBOL(generic_setlease);
|
||||||
|
@ -1752,9 +1760,7 @@ int fcntl_setlk(unsigned int fd, struct file *filp, unsigned int cmd,
|
||||||
/* Don't allow mandatory locks on files that may be memory mapped
|
/* Don't allow mandatory locks on files that may be memory mapped
|
||||||
* and shared.
|
* and shared.
|
||||||
*/
|
*/
|
||||||
if (IS_MANDLOCK(inode) &&
|
if (mandatory_lock(inode) && mapping_writably_mapped(filp->f_mapping)) {
|
||||||
(inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
|
|
||||||
mapping_writably_mapped(filp->f_mapping)) {
|
|
||||||
error = -EAGAIN;
|
error = -EAGAIN;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -1878,9 +1884,7 @@ int fcntl_setlk64(unsigned int fd, struct file *filp, unsigned int cmd,
|
||||||
/* Don't allow mandatory locks on files that may be memory mapped
|
/* Don't allow mandatory locks on files that may be memory mapped
|
||||||
* and shared.
|
* and shared.
|
||||||
*/
|
*/
|
||||||
if (IS_MANDLOCK(inode) &&
|
if (mandatory_lock(inode) && mapping_writably_mapped(filp->f_mapping)) {
|
||||||
(inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
|
|
||||||
mapping_writably_mapped(filp->f_mapping)) {
|
|
||||||
error = -EAGAIN;
|
error = -EAGAIN;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
@ -2062,138 +2066,114 @@ int vfs_cancel_lock(struct file *filp, struct file_lock *fl)
|
||||||
|
|
||||||
EXPORT_SYMBOL_GPL(vfs_cancel_lock);
|
EXPORT_SYMBOL_GPL(vfs_cancel_lock);
|
||||||
|
|
||||||
static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx)
|
#ifdef CONFIG_PROC_FS
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
|
||||||
|
static void lock_get_status(struct seq_file *f, struct file_lock *fl,
|
||||||
|
int id, char *pfx)
|
||||||
{
|
{
|
||||||
struct inode *inode = NULL;
|
struct inode *inode = NULL;
|
||||||
|
|
||||||
if (fl->fl_file != NULL)
|
if (fl->fl_file != NULL)
|
||||||
inode = fl->fl_file->f_path.dentry->d_inode;
|
inode = fl->fl_file->f_path.dentry->d_inode;
|
||||||
|
|
||||||
out += sprintf(out, "%d:%s ", id, pfx);
|
seq_printf(f, "%d:%s ", id, pfx);
|
||||||
if (IS_POSIX(fl)) {
|
if (IS_POSIX(fl)) {
|
||||||
out += sprintf(out, "%6s %s ",
|
seq_printf(f, "%6s %s ",
|
||||||
(fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
|
(fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
|
||||||
(inode == NULL) ? "*NOINODE*" :
|
(inode == NULL) ? "*NOINODE*" :
|
||||||
(IS_MANDLOCK(inode) &&
|
mandatory_lock(inode) ? "MANDATORY" : "ADVISORY ");
|
||||||
(inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ?
|
|
||||||
"MANDATORY" : "ADVISORY ");
|
|
||||||
} else if (IS_FLOCK(fl)) {
|
} else if (IS_FLOCK(fl)) {
|
||||||
if (fl->fl_type & LOCK_MAND) {
|
if (fl->fl_type & LOCK_MAND) {
|
||||||
out += sprintf(out, "FLOCK MSNFS ");
|
seq_printf(f, "FLOCK MSNFS ");
|
||||||
} else {
|
} else {
|
||||||
out += sprintf(out, "FLOCK ADVISORY ");
|
seq_printf(f, "FLOCK ADVISORY ");
|
||||||
}
|
}
|
||||||
} else if (IS_LEASE(fl)) {
|
} else if (IS_LEASE(fl)) {
|
||||||
out += sprintf(out, "LEASE ");
|
seq_printf(f, "LEASE ");
|
||||||
if (fl->fl_type & F_INPROGRESS)
|
if (fl->fl_type & F_INPROGRESS)
|
||||||
out += sprintf(out, "BREAKING ");
|
seq_printf(f, "BREAKING ");
|
||||||
else if (fl->fl_file)
|
else if (fl->fl_file)
|
||||||
out += sprintf(out, "ACTIVE ");
|
seq_printf(f, "ACTIVE ");
|
||||||
else
|
else
|
||||||
out += sprintf(out, "BREAKER ");
|
seq_printf(f, "BREAKER ");
|
||||||
} else {
|
} else {
|
||||||
out += sprintf(out, "UNKNOWN UNKNOWN ");
|
seq_printf(f, "UNKNOWN UNKNOWN ");
|
||||||
}
|
}
|
||||||
if (fl->fl_type & LOCK_MAND) {
|
if (fl->fl_type & LOCK_MAND) {
|
||||||
out += sprintf(out, "%s ",
|
seq_printf(f, "%s ",
|
||||||
(fl->fl_type & LOCK_READ)
|
(fl->fl_type & LOCK_READ)
|
||||||
? (fl->fl_type & LOCK_WRITE) ? "RW " : "READ "
|
? (fl->fl_type & LOCK_WRITE) ? "RW " : "READ "
|
||||||
: (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE ");
|
: (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE ");
|
||||||
} else {
|
} else {
|
||||||
out += sprintf(out, "%s ",
|
seq_printf(f, "%s ",
|
||||||
(fl->fl_type & F_INPROGRESS)
|
(fl->fl_type & F_INPROGRESS)
|
||||||
? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ "
|
? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ "
|
||||||
: (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
|
: (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
|
||||||
}
|
}
|
||||||
if (inode) {
|
if (inode) {
|
||||||
#ifdef WE_CAN_BREAK_LSLK_NOW
|
#ifdef WE_CAN_BREAK_LSLK_NOW
|
||||||
out += sprintf(out, "%d %s:%ld ", fl->fl_pid,
|
seq_printf(f, "%d %s:%ld ", fl->fl_pid,
|
||||||
inode->i_sb->s_id, inode->i_ino);
|
inode->i_sb->s_id, inode->i_ino);
|
||||||
#else
|
#else
|
||||||
/* userspace relies on this representation of dev_t ;-( */
|
/* userspace relies on this representation of dev_t ;-( */
|
||||||
out += sprintf(out, "%d %02x:%02x:%ld ", fl->fl_pid,
|
seq_printf(f, "%d %02x:%02x:%ld ", fl->fl_pid,
|
||||||
MAJOR(inode->i_sb->s_dev),
|
MAJOR(inode->i_sb->s_dev),
|
||||||
MINOR(inode->i_sb->s_dev), inode->i_ino);
|
MINOR(inode->i_sb->s_dev), inode->i_ino);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
out += sprintf(out, "%d <none>:0 ", fl->fl_pid);
|
seq_printf(f, "%d <none>:0 ", fl->fl_pid);
|
||||||
}
|
}
|
||||||
if (IS_POSIX(fl)) {
|
if (IS_POSIX(fl)) {
|
||||||
if (fl->fl_end == OFFSET_MAX)
|
if (fl->fl_end == OFFSET_MAX)
|
||||||
out += sprintf(out, "%Ld EOF\n", fl->fl_start);
|
seq_printf(f, "%Ld EOF\n", fl->fl_start);
|
||||||
else
|
else
|
||||||
out += sprintf(out, "%Ld %Ld\n", fl->fl_start,
|
seq_printf(f, "%Ld %Ld\n", fl->fl_start, fl->fl_end);
|
||||||
fl->fl_end);
|
|
||||||
} else {
|
} else {
|
||||||
out += sprintf(out, "0 EOF\n");
|
seq_printf(f, "0 EOF\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void move_lock_status(char **p, off_t* pos, off_t offset)
|
static int locks_show(struct seq_file *f, void *v)
|
||||||
{
|
{
|
||||||
int len;
|
struct file_lock *fl, *bfl;
|
||||||
len = strlen(*p);
|
|
||||||
if(*pos >= offset) {
|
fl = list_entry(v, struct file_lock, fl_link);
|
||||||
/* the complete line is valid */
|
|
||||||
*p += len;
|
lock_get_status(f, fl, (long)f->private, "");
|
||||||
*pos += len;
|
|
||||||
return;
|
list_for_each_entry(bfl, &fl->fl_block, fl_block)
|
||||||
}
|
lock_get_status(f, bfl, (long)f->private, " ->");
|
||||||
if(*pos+len > offset) {
|
|
||||||
/* use the second part of the line */
|
f->private++;
|
||||||
int i = offset-*pos;
|
return 0;
|
||||||
memmove(*p,*p+i,len-i);
|
|
||||||
*p += len-i;
|
|
||||||
*pos += len;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* discard the complete line */
|
|
||||||
*pos += len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void *locks_start(struct seq_file *f, loff_t *pos)
|
||||||
* get_locks_status - reports lock usage in /proc/locks
|
|
||||||
* @buffer: address in userspace to write into
|
|
||||||
* @start: ?
|
|
||||||
* @offset: how far we are through the buffer
|
|
||||||
* @length: how much to read
|
|
||||||
*/
|
|
||||||
|
|
||||||
int get_locks_status(char *buffer, char **start, off_t offset, int length)
|
|
||||||
{
|
{
|
||||||
struct list_head *tmp;
|
|
||||||
char *q = buffer;
|
|
||||||
off_t pos = 0;
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
lock_kernel();
|
lock_kernel();
|
||||||
list_for_each(tmp, &file_lock_list) {
|
f->private = (void *)1;
|
||||||
struct list_head *btmp;
|
return seq_list_start(&file_lock_list, *pos);
|
||||||
struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);
|
|
||||||
lock_get_status(q, fl, ++i, "");
|
|
||||||
move_lock_status(&q, &pos, offset);
|
|
||||||
|
|
||||||
if(pos >= offset+length)
|
|
||||||
goto done;
|
|
||||||
|
|
||||||
list_for_each(btmp, &fl->fl_block) {
|
|
||||||
struct file_lock *bfl = list_entry(btmp,
|
|
||||||
struct file_lock, fl_block);
|
|
||||||
lock_get_status(q, bfl, i, " ->");
|
|
||||||
move_lock_status(&q, &pos, offset);
|
|
||||||
|
|
||||||
if(pos >= offset+length)
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
done:
|
|
||||||
unlock_kernel();
|
|
||||||
*start = buffer;
|
|
||||||
if(q-buffer < length)
|
|
||||||
return (q-buffer);
|
|
||||||
return length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *locks_next(struct seq_file *f, void *v, loff_t *pos)
|
||||||
|
{
|
||||||
|
return seq_list_next(v, &file_lock_list, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void locks_stop(struct seq_file *f, void *v)
|
||||||
|
{
|
||||||
|
unlock_kernel();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct seq_operations locks_seq_operations = {
|
||||||
|
.start = locks_start,
|
||||||
|
.next = locks_next,
|
||||||
|
.stop = locks_stop,
|
||||||
|
.show = locks_show,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* lock_may_read - checks that the region is free of locks
|
* lock_may_read - checks that the region is free of locks
|
||||||
* @inode: the inode that is being read
|
* @inode: the inode that is being read
|
||||||
|
|
|
@ -577,8 +577,7 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
|
||||||
nfs_inc_stats(inode, NFSIOS_VFSLOCK);
|
nfs_inc_stats(inode, NFSIOS_VFSLOCK);
|
||||||
|
|
||||||
/* No mandatory locks over NFS */
|
/* No mandatory locks over NFS */
|
||||||
if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
|
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
|
||||||
fl->fl_type != F_UNLCK)
|
|
||||||
return -ENOLCK;
|
return -ENOLCK;
|
||||||
|
|
||||||
if (IS_GETLK(cmd))
|
if (IS_GETLK(cmd))
|
||||||
|
|
|
@ -2035,7 +2035,7 @@ static inline int
|
||||||
io_during_grace_disallowed(struct inode *inode, int flags)
|
io_during_grace_disallowed(struct inode *inode, int flags)
|
||||||
{
|
{
|
||||||
return nfs4_in_grace() && (flags & (RD_STATE | WR_STATE))
|
return nfs4_in_grace() && (flags & (RD_STATE | WR_STATE))
|
||||||
&& MANDATORY_LOCK(inode);
|
&& mandatory_lock(inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -61,12 +61,6 @@
|
||||||
#define NFSDDBG_FACILITY NFSDDBG_FILEOP
|
#define NFSDDBG_FACILITY NFSDDBG_FILEOP
|
||||||
|
|
||||||
|
|
||||||
/* We must ignore files (but only files) which might have mandatory
|
|
||||||
* locks on them because there is no way to know if the accesser has
|
|
||||||
* the lock.
|
|
||||||
*/
|
|
||||||
#define IS_ISMNDLK(i) (S_ISREG((i)->i_mode) && MANDATORY_LOCK(i))
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is a cache of readahead params that help us choose the proper
|
* This is a cache of readahead params that help us choose the proper
|
||||||
* readahead strategy. Initially, we set all readahead parameters to 0
|
* readahead strategy. Initially, we set all readahead parameters to 0
|
||||||
|
@ -689,7 +683,12 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,
|
||||||
err = nfserr_perm;
|
err = nfserr_perm;
|
||||||
if (IS_APPEND(inode) && (access & MAY_WRITE))
|
if (IS_APPEND(inode) && (access & MAY_WRITE))
|
||||||
goto out;
|
goto out;
|
||||||
if (IS_ISMNDLK(inode))
|
/*
|
||||||
|
* We must ignore files (but only files) which might have mandatory
|
||||||
|
* locks on them because there is no way to know if the accesser has
|
||||||
|
* the lock.
|
||||||
|
*/
|
||||||
|
if (S_ISREG((inode)->i_mode) && mandatory_lock(inode))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (!inode->i_fop)
|
if (!inode->i_fop)
|
||||||
|
|
|
@ -66,7 +66,6 @@ extern int get_stram_list(char *);
|
||||||
extern int get_filesystem_list(char *);
|
extern int get_filesystem_list(char *);
|
||||||
extern int get_exec_domain_list(char *);
|
extern int get_exec_domain_list(char *);
|
||||||
extern int get_dma_list(char *);
|
extern int get_dma_list(char *);
|
||||||
extern int get_locks_status (char *, char **, off_t, int);
|
|
||||||
|
|
||||||
static int proc_calc_metrics(char *page, char **start, off_t off,
|
static int proc_calc_metrics(char *page, char **start, off_t off,
|
||||||
int count, int *eof, int len)
|
int count, int *eof, int len)
|
||||||
|
@ -624,16 +623,18 @@ static int cmdline_read_proc(char *page, char **start, off_t off,
|
||||||
return proc_calc_metrics(page, start, off, count, eof, len);
|
return proc_calc_metrics(page, start, off, count, eof, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int locks_read_proc(char *page, char **start, off_t off,
|
static int locks_open(struct inode *inode, struct file *filp)
|
||||||
int count, int *eof, void *data)
|
|
||||||
{
|
{
|
||||||
int len = get_locks_status(page, start, off, count);
|
return seq_open(filp, &locks_seq_operations);
|
||||||
|
|
||||||
if (len < count)
|
|
||||||
*eof = 1;
|
|
||||||
return len;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct file_operations proc_locks_operations = {
|
||||||
|
.open = locks_open,
|
||||||
|
.read = seq_read,
|
||||||
|
.llseek = seq_lseek,
|
||||||
|
.release = seq_release,
|
||||||
|
};
|
||||||
|
|
||||||
static int execdomains_read_proc(char *page, char **start, off_t off,
|
static int execdomains_read_proc(char *page, char **start, off_t off,
|
||||||
int count, int *eof, void *data)
|
int count, int *eof, void *data)
|
||||||
{
|
{
|
||||||
|
@ -691,7 +692,6 @@ void __init proc_misc_init(void)
|
||||||
#endif
|
#endif
|
||||||
{"filesystems", filesystems_read_proc},
|
{"filesystems", filesystems_read_proc},
|
||||||
{"cmdline", cmdline_read_proc},
|
{"cmdline", cmdline_read_proc},
|
||||||
{"locks", locks_read_proc},
|
|
||||||
{"execdomains", execdomains_read_proc},
|
{"execdomains", execdomains_read_proc},
|
||||||
{NULL,}
|
{NULL,}
|
||||||
};
|
};
|
||||||
|
@ -709,6 +709,7 @@ void __init proc_misc_init(void)
|
||||||
entry->proc_fops = &proc_kmsg_operations;
|
entry->proc_fops = &proc_kmsg_operations;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
create_seq_entry("locks", 0, &proc_locks_operations);
|
||||||
create_seq_entry("devices", 0, &proc_devinfo_operations);
|
create_seq_entry("devices", 0, &proc_devinfo_operations);
|
||||||
create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations);
|
create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations);
|
||||||
#ifdef CONFIG_BLOCK
|
#ifdef CONFIG_BLOCK
|
||||||
|
|
|
@ -205,7 +205,7 @@ int rw_verify_area(int read_write, struct file *file, loff_t *ppos, size_t count
|
||||||
if (unlikely((pos < 0) || (loff_t) (pos + count) < 0))
|
if (unlikely((pos < 0) || (loff_t) (pos + count) < 0))
|
||||||
goto Einval;
|
goto Einval;
|
||||||
|
|
||||||
if (unlikely(inode->i_flock && MANDATORY_LOCK(inode))) {
|
if (unlikely(inode->i_flock && mandatory_lock(inode))) {
|
||||||
int retval = locks_mandatory_area(
|
int retval = locks_mandatory_area(
|
||||||
read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
|
read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
|
||||||
inode, file, pos, count);
|
inode, file, pos, count);
|
||||||
|
|
|
@ -883,6 +883,7 @@ extern int vfs_setlease(struct file *, long, struct file_lock **);
|
||||||
extern int lease_modify(struct file_lock **, int);
|
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);
|
||||||
|
extern struct seq_operations locks_seq_operations;
|
||||||
|
|
||||||
struct fasync_struct {
|
struct fasync_struct {
|
||||||
int magic;
|
int magic;
|
||||||
|
@ -1375,12 +1376,25 @@ extern int locks_mandatory_area(int, struct inode *, struct file *, loff_t, size
|
||||||
* Candidates for mandatory locking have the setgid bit set
|
* Candidates for mandatory locking have the setgid bit set
|
||||||
* but no group execute bit - an otherwise meaningless combination.
|
* but no group execute bit - an otherwise meaningless combination.
|
||||||
*/
|
*/
|
||||||
#define MANDATORY_LOCK(inode) \
|
|
||||||
(IS_MANDLOCK(inode) && ((inode)->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
|
static inline int __mandatory_lock(struct inode *ino)
|
||||||
|
{
|
||||||
|
return (ino->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ... and these candidates should be on MS_MANDLOCK mounted fs,
|
||||||
|
* otherwise these will be advisory locks
|
||||||
|
*/
|
||||||
|
|
||||||
|
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 inode *inode)
|
||||||
{
|
{
|
||||||
if (MANDATORY_LOCK(inode))
|
if (mandatory_lock(inode))
|
||||||
return locks_mandatory_locked(inode);
|
return locks_mandatory_locked(inode);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1391,7 +1405,7 @@ static inline int locks_verify_truncate(struct inode *inode,
|
||||||
struct file *filp,
|
struct file *filp,
|
||||||
loff_t size)
|
loff_t size)
|
||||||
{
|
{
|
||||||
if (inode->i_flock && MANDATORY_LOCK(inode))
|
if (inode->i_flock && mandatory_lock(inode))
|
||||||
return locks_mandatory_area(
|
return locks_mandatory_area(
|
||||||
FLOCK_VERIFY_WRITE, inode, filp,
|
FLOCK_VERIFY_WRITE, inode, filp,
|
||||||
size < inode->i_size ? size : inode->i_size,
|
size < inode->i_size ? size : inode->i_size,
|
||||||
|
|
Loading…
Reference in New Issue