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:
Linus Torvalds 2007-10-15 16:07:40 -07:00
commit 541010e4b8
14 changed files with 158 additions and 147 deletions

View File

@ -145,7 +145,7 @@ fb/
feature-removal-schedule.txt
- list of files and features that are going to be removed.
filesystems/
- directory with info on the various filesystems that Linux supports.
- info on the vfs and the various filesystems that Linux supports.
firmware_class/
- request_firmware() hotplug interface info.
floppy.txt
@ -230,8 +230,6 @@ local_ops.txt
- semantics and behavior of local atomic operations.
lockdep-design.txt
- documentation on the runtime locking correctness validator.
locks.txt
- info on file locking implementations, flock() vs. fcntl(), etc.
logo.gif
- full colour GIF image of Linux logo (penguin - Tux).
logo.txt
@ -240,8 +238,6 @@ m68k/
- directory with info about Linux on Motorola 68k architecture.
magic-number.txt
- 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
- info on supporting Micro Channel Architecture (e.g. PS/2) systems.
md.txt

View File

@ -52,6 +52,10 @@ isofs.txt
- info and mount options for the ISO 9660 (CDROM) filesystem.
jfs.txt
- 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
- info on Novell Netware(tm) filesystem using NCP protocol.
ntfs.txt

View File

@ -53,11 +53,11 @@ fcntl(), with all the problems that implies.
1.3 Mandatory Locking As A Mount Option
---------------------------------------
Mandatory locking, as described in 'Documentation/mandatory.txt' was prior
to this release a general configuration option that was valid for all
mounted filesystems. This had a number of inherent dangers, not the least
of which was the ability to freeze an NFS server by asking it to read a
file for which a mandatory lock existed.
Mandatory locking, as described in 'Documentation/filesystems/mandatory.txt'
was prior to this release a general configuration option that was valid for
all mounted filesystems. This had a number of inherent dangers, not the
least of which was the ability to freeze an NFS server by asking it to read
a file for which a mandatory lock existed.
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'.

View File

@ -3,7 +3,26 @@
Andy Walker <andy@lysaker.kvaerner.no>
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?
------------------------------

View File

@ -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);
/* No mandatory locks */
if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
if (__mandatory_lock(inode))
return -ENOLCK;
if ((IS_SETLK(cmd) || IS_SETLKW(cmd)) && fl->fl_type != F_UNLCK) {

View File

@ -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);
/* AFS doesn't support mandatory locks */
if ((vnode->vfs_inode.i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
fl->fl_type != F_UNLCK)
if (__mandatory_lock(&vnode->vfs_inode) && fl->fl_type != F_UNLCK)
return -ENOLCK;
if (IS_GETLK(cmd))

View File

@ -535,7 +535,7 @@ static int gfs2_lock(struct file *file, int cmd, struct file_lock *fl)
if (!(fl->fl_flags & FL_POSIX))
return -ENOLCK;
if ((ip->i_inode.i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
if (__mandatory_lock(&ip->i_inode))
return -ENOLCK;
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))
return -ENOLCK;
if ((ip->i_inode.i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
if (__mandatory_lock(&ip->i_inode))
return -ENOLCK;
if (sdp->sd_args.ar_localflocks)

View File

@ -534,7 +534,9 @@ static void locks_insert_block(struct file_lock *blocker,
static void locks_wake_up_blocks(struct file_lock *blocker)
{
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);
__locks_delete_block(waiter);
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) {
if (!IS_POSIX(cfl))
continue;
if (posix_locks_conflict(cfl, fl))
if (posix_locks_conflict(fl, cfl))
break;
}
if (cfl)
@ -698,13 +700,12 @@ EXPORT_SYMBOL(posix_test_lock);
static int posix_locks_deadlock(struct file_lock *caller_fl,
struct file_lock *block_fl)
{
struct list_head *tmp;
struct file_lock *fl;
next_task:
if (posix_same_owner(caller_fl, block_fl))
return 1;
list_for_each(tmp, &blocked_list) {
struct file_lock *fl = list_entry(tmp, struct file_lock, fl_link);
list_for_each_entry(fl, &blocked_list, fl_link) {
if (posix_same_owner(fl, block_fl)) {
fl = fl->fl_next;
block_fl = fl;
@ -715,8 +716,7 @@ next_task:
}
/* 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
* flock_lock_file and posix_lock_file.
* after any leases, but before any posix locks.
*
* 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
@ -733,6 +733,15 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
lock_kernel();
if (request->fl_flags & FL_ACCESS)
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) {
struct file_lock *fl = *before;
if (IS_POSIX(fl))
@ -754,10 +763,6 @@ static int flock_lock_file(struct file *filp, struct file_lock *request)
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,
* 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();
if (request->fl_type != F_UNLCK) {
for_each_lock(inode, before) {
struct file_lock *fl = *before;
fl = *before;
if (!IS_POSIX(fl))
continue;
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
* changed the permissions behind our back.
*/
if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID)
if (__mandatory_lock(inode))
continue;
}
@ -1337,6 +1342,7 @@ int fcntl_getlease(struct file *filp)
int generic_setlease(struct file *filp, long arg, struct file_lock **flp)
{
struct file_lock *fl, **before, **my_before = NULL, *lease;
struct file_lock *new_fl = NULL;
struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
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)))
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
* 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)
goto out;
error = -ENOMEM;
fl = locks_alloc_lock();
if (fl == NULL)
goto out;
locks_copy_lock(new_fl, lease);
locks_insert_lock(before, new_fl);
locks_copy_lock(fl, lease);
*flp = new_fl;
return 0;
locks_insert_lock(before, fl);
*flp = fl;
error = 0;
out:
if (new_fl != NULL)
locks_free_lock(new_fl);
return error;
}
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
* and shared.
*/
if (IS_MANDLOCK(inode) &&
(inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
mapping_writably_mapped(filp->f_mapping)) {
if (mandatory_lock(inode) && mapping_writably_mapped(filp->f_mapping)) {
error = -EAGAIN;
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
* and shared.
*/
if (IS_MANDLOCK(inode) &&
(inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
mapping_writably_mapped(filp->f_mapping)) {
if (mandatory_lock(inode) && mapping_writably_mapped(filp->f_mapping)) {
error = -EAGAIN;
goto out;
}
@ -2062,138 +2066,114 @@ int vfs_cancel_lock(struct file *filp, struct file_lock *fl)
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;
if (fl->fl_file != NULL)
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)) {
out += sprintf(out, "%6s %s ",
seq_printf(f, "%6s %s ",
(fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ",
(inode == NULL) ? "*NOINODE*" :
(IS_MANDLOCK(inode) &&
(inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ?
"MANDATORY" : "ADVISORY ");
mandatory_lock(inode) ? "MANDATORY" : "ADVISORY ");
} else if (IS_FLOCK(fl)) {
if (fl->fl_type & LOCK_MAND) {
out += sprintf(out, "FLOCK MSNFS ");
seq_printf(f, "FLOCK MSNFS ");
} else {
out += sprintf(out, "FLOCK ADVISORY ");
seq_printf(f, "FLOCK ADVISORY ");
}
} else if (IS_LEASE(fl)) {
out += sprintf(out, "LEASE ");
seq_printf(f, "LEASE ");
if (fl->fl_type & F_INPROGRESS)
out += sprintf(out, "BREAKING ");
seq_printf(f, "BREAKING ");
else if (fl->fl_file)
out += sprintf(out, "ACTIVE ");
seq_printf(f, "ACTIVE ");
else
out += sprintf(out, "BREAKER ");
seq_printf(f, "BREAKER ");
} else {
out += sprintf(out, "UNKNOWN UNKNOWN ");
seq_printf(f, "UNKNOWN UNKNOWN ");
}
if (fl->fl_type & LOCK_MAND) {
out += sprintf(out, "%s ",
seq_printf(f, "%s ",
(fl->fl_type & LOCK_READ)
? (fl->fl_type & LOCK_WRITE) ? "RW " : "READ "
: (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE ");
} else {
out += sprintf(out, "%s ",
seq_printf(f, "%s ",
(fl->fl_type & F_INPROGRESS)
? (fl->fl_type & F_UNLCK) ? "UNLCK" : "READ "
: (fl->fl_type & F_WRLCK) ? "WRITE" : "READ ");
}
if (inode) {
#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);
#else
/* 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),
MINOR(inode->i_sb->s_dev), inode->i_ino);
#endif
} else {
out += sprintf(out, "%d <none>:0 ", fl->fl_pid);
seq_printf(f, "%d <none>:0 ", fl->fl_pid);
}
if (IS_POSIX(fl)) {
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
out += sprintf(out, "%Ld %Ld\n", fl->fl_start,
fl->fl_end);
seq_printf(f, "%Ld %Ld\n", fl->fl_start, fl->fl_end);
} 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;
len = strlen(*p);
if(*pos >= offset) {
/* the complete line is valid */
*p += len;
*pos += len;
return;
}
if(*pos+len > offset) {
/* use the second part of the line */
int i = offset-*pos;
memmove(*p,*p+i,len-i);
*p += len-i;
*pos += len;
return;
}
/* discard the complete line */
*pos += len;
struct file_lock *fl, *bfl;
fl = list_entry(v, struct file_lock, fl_link);
lock_get_status(f, fl, (long)f->private, "");
list_for_each_entry(bfl, &fl->fl_block, fl_block)
lock_get_status(f, bfl, (long)f->private, " ->");
f->private++;
return 0;
}
/**
* 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)
static void *locks_start(struct seq_file *f, loff_t *pos)
{
struct list_head *tmp;
char *q = buffer;
off_t pos = 0;
int i = 0;
lock_kernel();
list_for_each(tmp, &file_lock_list) {
struct list_head *btmp;
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;
f->private = (void *)1;
return seq_list_start(&file_lock_list, *pos);
}
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
* @inode: the inode that is being read

View File

@ -577,8 +577,7 @@ static int nfs_lock(struct file *filp, int cmd, struct file_lock *fl)
nfs_inc_stats(inode, NFSIOS_VFSLOCK);
/* No mandatory locks over NFS */
if ((inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID &&
fl->fl_type != F_UNLCK)
if (__mandatory_lock(inode) && fl->fl_type != F_UNLCK)
return -ENOLCK;
if (IS_GETLK(cmd))

View File

@ -2035,7 +2035,7 @@ static inline int
io_during_grace_disallowed(struct inode *inode, int flags)
{
return nfs4_in_grace() && (flags & (RD_STATE | WR_STATE))
&& MANDATORY_LOCK(inode);
&& mandatory_lock(inode);
}
/*

View File

@ -61,12 +61,6 @@
#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
* 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;
if (IS_APPEND(inode) && (access & MAY_WRITE))
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;
if (!inode->i_fop)

View File

@ -66,7 +66,6 @@ extern int get_stram_list(char *);
extern int get_filesystem_list(char *);
extern int get_exec_domain_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,
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);
}
static int locks_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
static int locks_open(struct inode *inode, struct file *filp)
{
int len = get_locks_status(page, start, off, count);
if (len < count)
*eof = 1;
return len;
return seq_open(filp, &locks_seq_operations);
}
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,
int count, int *eof, void *data)
{
@ -691,7 +692,6 @@ void __init proc_misc_init(void)
#endif
{"filesystems", filesystems_read_proc},
{"cmdline", cmdline_read_proc},
{"locks", locks_read_proc},
{"execdomains", execdomains_read_proc},
{NULL,}
};
@ -709,6 +709,7 @@ void __init proc_misc_init(void)
entry->proc_fops = &proc_kmsg_operations;
}
#endif
create_seq_entry("locks", 0, &proc_locks_operations);
create_seq_entry("devices", 0, &proc_devinfo_operations);
create_seq_entry("cpuinfo", 0, &proc_cpuinfo_operations);
#ifdef CONFIG_BLOCK

View File

@ -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))
goto Einval;
if (unlikely(inode->i_flock && MANDATORY_LOCK(inode))) {
if (unlikely(inode->i_flock && mandatory_lock(inode))) {
int retval = locks_mandatory_area(
read_write == READ ? FLOCK_VERIFY_READ : FLOCK_VERIFY_WRITE,
inode, file, pos, count);

View File

@ -883,6 +883,7 @@ extern int vfs_setlease(struct file *, long, struct file_lock **);
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);
extern struct seq_operations locks_seq_operations;
struct fasync_struct {
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
* 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)
{
if (MANDATORY_LOCK(inode))
if (mandatory_lock(inode))
return locks_mandatory_locked(inode);
return 0;
}
@ -1391,7 +1405,7 @@ static inline int locks_verify_truncate(struct inode *inode,
struct file *filp,
loff_t size)
{
if (inode->i_flock && MANDATORY_LOCK(inode))
if (inode->i_flock && mandatory_lock(inode))
return locks_mandatory_area(
FLOCK_VERIFY_WRITE, inode, filp,
size < inode->i_size ? size : inode->i_size,