📄 locks.c
字号:
/* * linux/fs/locks.c * * Provide support for fcntl()'s F_GETLK, F_SETLK, and F_SETLKW calls. * Doug Evans (dje@spiff.uucp), August 07, 1992 * * Deadlock detection added. * FIXME: one thing isn't handled yet: * - mandatory locks (requires lots of changes elsewhere) * Kelly Carmichael (kelly@[142.24.8.65]), September 17, 1994. * * Miscellaneous edits, and a total rewrite of posix_lock_file() code. * Kai Petzke (wpp@marie.physik.tu-berlin.de), 1994 * * Converted file_lock_table to a linked list from an array, which eliminates * the limits on how many active file locks are open. * Chad Page (pageone@netcom.com), November 27, 1994 * * Removed dependency on file descriptors. dup()'ed file descriptors now * get the same locks as the original file descriptors, and a close() on * any file descriptor removes ALL the locks on the file for the current * process. Since locks still depend on the process id, locks are inherited * after an exec() but not after a fork(). This agrees with POSIX, and both * BSD and SVR4 practice. * Andy Walker (andy@lysaker.kvaerner.no), February 14, 1995 * * Scrapped free list which is redundant now that we allocate locks * dynamically with kmalloc()/kfree(). * Andy Walker (andy@lysaker.kvaerner.no), February 21, 1995 * * Implemented two lock personalities - FL_FLOCK and FL_POSIX. * * FL_POSIX locks are created with calls to fcntl() and lockf() through the * fcntl() system call. They have the semantics described above. * * FL_FLOCK locks are created with calls to flock(), through the flock() * system call, which is new. Old C libraries implement flock() via fcntl() * and will continue to use the old, broken implementation. * * FL_FLOCK locks follow the 4.4 BSD flock() semantics. They are associated * with a file pointer (filp). As a result they can be shared by a parent * process and its children after a fork(). They are removed when the last * file descriptor referring to the file pointer is closed (unless explicitly * unlocked). * * FL_FLOCK locks never deadlock, an existing lock is always removed before * upgrading from shared to exclusive (or vice versa). When this happens * any processes blocked by the current lock are woken up and allowed to * run before the new lock is applied. * Andy Walker (andy@lysaker.kvaerner.no), June 09, 1995 * * Removed some race conditions in flock_lock_file(), marked other possible * races. Just grep for FIXME to see them. * Dmitry Gorodchanin (pgmdsg@ibi.com), February 09, 1996. * * Addressed Dmitry's concerns. Deadlock checking no longer recursive. * Lock allocation changed to GFP_ATOMIC as we can't afford to sleep * once we've checked for blocking and deadlocking. * Andy Walker (andy@lysaker.kvaerner.no), April 03, 1996. * * Initial implementation of mandatory locks. SunOS turned out to be * a rotten model, so I implemented the "obvious" semantics. * See 'linux/Documentation/mandatory.txt' for details. * Andy Walker (andy@lysaker.kvaerner.no), April 06, 1996. * * Don't allow mandatory locks on mmap()'ed files. Added simple functions to * check if a file has mandatory locks, used by mmap(), open() and creat() to * see if system call should be rejected. Ref. HP-UX/SunOS/Solaris Reference * Manual, Section 2. * Andy Walker (andy@lysaker.kvaerner.no), April 09, 1996. * * Tidied up block list handling. Added '/proc/locks' interface. * Andy Walker (andy@lysaker.kvaerner.no), April 24, 1996. * * Fixed deadlock condition for pathological code that mixes calls to * flock() and fcntl(). * Andy Walker (andy@lysaker.kvaerner.no), April 29, 1996. * * Allow only one type of locking scheme (FL_POSIX or FL_FLOCK) to be in use * for a given file at a time. Changed the CONFIG_LOCK_MANDATORY scheme to * guarantee sensible behaviour in the case where file system modules might * be compiled with different options than the kernel itself. * Andy Walker (andy@lysaker.kvaerner.no), May 15, 1996. * * Added a couple of missing wake_up() calls. Thanks to Thomas Meckel * (Thomas.Meckel@mni.fh-giessen.de) for spotting this. * Andy Walker (andy@lysaker.kvaerner.no), May 15, 1996. * * Changed FL_POSIX locks to use the block list in the same way as FL_FLOCK * locks. Changed process synchronisation to avoid dereferencing locks that * have already been freed. * Andy Walker (andy@lysaker.kvaerner.no), Sep 21, 1996. * * Made the block list a circular list to minimise searching in the list. * Andy Walker (andy@lysaker.kvaerner.no), Sep 25, 1996. * * Made mandatory locking a mount option. Default is not to allow mandatory * locking. * Andy Walker (andy@lysaker.kvaerner.no), Oct 04, 1996. * * Fixed /proc/locks interface so that we can't overrun the buffer we are handed. * Andy Walker (andy@lysaker.kvaerner.no), May 12, 1997. */#include <linux/malloc.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/stat.h>#include <linux/fcntl.h>#include <asm/segment.h>#define OFFSET_MAX ((off_t)0x7fffffff) /* FIXME: move elsewhere? */static int flock_make_lock(struct file *filp, struct file_lock *fl, unsigned int cmd);static int posix_make_lock(struct file *filp, struct file_lock *fl, struct flock *l);static int flock_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl);static int posix_locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl);static int locks_conflict(struct file_lock *caller_fl, struct file_lock *sys_fl);static int flock_lock_file(struct file *filp, struct file_lock *caller, unsigned int wait);static int posix_lock_file(struct file *filp, struct file_lock *caller, unsigned int wait);static int posix_locks_deadlock(struct task_struct *my_task, struct task_struct *blocked_task);static void posix_remove_locks(struct file_lock **before, struct task_struct *task);static void flock_remove_locks(struct file_lock **before, struct file *filp);static struct file_lock *locks_empty_lock(void);static struct file_lock *locks_init_lock(struct file_lock *, struct file_lock *);static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl);static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait);static char *lock_get_status(struct file_lock *fl, int id, char *pfx);static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter);static void locks_delete_block(struct file_lock *blocker, struct file_lock *waiter);static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait);static struct file_lock *file_lock_table = NULL;/* Allocate a new lock, and initialize its fields from fl. * The lock is not inserted into any lists until locks_insert_lock() or * locks_insert_block() are called. */static inline struct file_lock *locks_alloc_lock(struct file_lock *fl){ return locks_init_lock(locks_empty_lock(), fl);}/* Free lock not inserted in any queue. */static inline void locks_free_lock(struct file_lock *fl){ if (waitqueue_active(&fl->fl_wait)) panic("Aarggh: attempting to free lock with active wait queue - shoot Andy"); if (fl->fl_nextblock != NULL || fl->fl_prevblock != NULL) panic("Aarggh: attempting to free lock with active block list - shoot Andy"); kfree(fl); return;}/* Check if two locks overlap each other. */static inline int locks_overlap(struct file_lock *fl1, struct file_lock *fl2){ return ((fl1->fl_end >= fl2->fl_start) && (fl2->fl_end >= fl1->fl_start));}/* Insert waiter into blocker's block list. * We use a circular list so that processes can be easily woken up in * the order they blocked. The documentation doesn't require this but * it seems seems like the reasonable thing to do. */static void locks_insert_block(struct file_lock *blocker, struct file_lock *waiter){ struct file_lock *prevblock; if (blocker->fl_prevblock == NULL) /* No previous waiters - list is empty */ prevblock = blocker; else /* Previous waiters exist - add to end of list */ prevblock = blocker->fl_prevblock; prevblock->fl_nextblock = waiter; blocker->fl_prevblock = waiter; waiter->fl_nextblock = blocker; waiter->fl_prevblock = prevblock; return;}/* Remove waiter from blocker's block list. * When blocker ends up pointing to itself then the list is empty. */static void locks_delete_block(struct file_lock *blocker, struct file_lock *waiter){ struct file_lock *nextblock; struct file_lock *prevblock; nextblock = waiter->fl_nextblock; prevblock = waiter->fl_prevblock; if (nextblock == NULL) return; nextblock->fl_prevblock = prevblock; prevblock->fl_nextblock = nextblock; waiter->fl_prevblock = waiter->fl_nextblock = NULL; if (blocker->fl_nextblock == blocker) /* No more locks on blocker's blocked list */ blocker->fl_prevblock = blocker->fl_nextblock = NULL; return;}/* Wake up processes blocked waiting for blocker. * If told to wait then schedule the processes until the block list * is empty, otherwise empty the block list ourselves. */static void locks_wake_up_blocks(struct file_lock *blocker, unsigned int wait){ struct file_lock *waiter; while ((waiter = blocker->fl_nextblock) != NULL) { wake_up(&waiter->fl_wait); if (wait) /* Let the blocked process remove waiter from the * block list when it gets scheduled. */ schedule(); else /* Remove waiter from the block list, because by the * time it wakes up blocker won't exist any more. */ locks_delete_block(blocker, waiter); } return;}/* flock() system call entry point. Apply a FL_FLOCK style lock to * an open file descriptor. */asmlinkage int sys_flock(unsigned int fd, unsigned int cmd){ struct file_lock file_lock; struct file *filp; if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd])) return (-EBADF); if (!flock_make_lock(filp, &file_lock, cmd)) return (-EINVAL); if ((file_lock.fl_type != F_UNLCK) && !(filp->f_mode & 3)) return (-EBADF); return (flock_lock_file(filp, &file_lock, (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1));}/* Report the first existing lock that would conflict with l. * This implements the F_GETLK command of fcntl(). */int fcntl_getlk(unsigned int fd, struct flock *l){ int error; struct flock flock; struct file *filp; struct file_lock *fl,file_lock; if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd])) return (-EBADF); error = verify_area(VERIFY_WRITE, l, sizeof(*l)); if (error) return (error); memcpy_fromfs(&flock, l, sizeof(flock)); if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK)) return (-EINVAL); if (!filp->f_inode || !posix_make_lock(filp, &file_lock, &flock)) return (-EINVAL); flock.l_type = F_UNLCK; for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) { if (!(fl->fl_flags & FL_POSIX)) break; if (posix_locks_conflict(&file_lock, fl)) { flock.l_pid = fl->fl_owner->pid; flock.l_start = fl->fl_start; flock.l_len = fl->fl_end == OFFSET_MAX ? 0 : fl->fl_end - fl->fl_start + 1; flock.l_whence = 0; flock.l_type = fl->fl_type; break; } } memcpy_tofs(l, &flock, sizeof(flock)); return (0);}/* Apply the lock described by l to an open file descriptor. * This implements both the F_SETLK and F_SETLKW commands of fcntl(). * It also emulates flock() in a pretty broken way for older C * libraries. */int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l){ int error; struct file *filp; struct file_lock file_lock; struct flock flock; struct inode *inode; /* Get arguments and validate them ... */ if ((fd >= NR_OPEN) || !(filp = current->files->fd[fd])) return (-EBADF); error = verify_area(VERIFY_READ, l, sizeof(*l)); if (error) return (error); if (!(inode = filp->f_inode)) return (-EINVAL); /* * This might block, so we do it before checking the inode. */ memcpy_fromfs(&flock, l, sizeof(flock)); /* Don't allow mandatory locks on files that may be memory mapped * and shared. */#ifndef NO_MM if (IS_MANDLOCK(inode) && (inode->i_mode & (S_ISGID | S_IXGRP)) == S_ISGID && inode->i_mmap) { struct vm_area_struct *vma = inode->i_mmap; do { if (vma->vm_flags & VM_MAYSHARE) return (-EAGAIN); vma = vma->vm_next_share; } while (vma != inode->i_mmap); }#endif if (!posix_make_lock(filp, &file_lock, &flock)) return (-EINVAL); switch (flock.l_type) { case F_RDLCK: if (!(filp->f_mode & 1)) return (-EBADF); break; case F_WRLCK: if (!(filp->f_mode & 2)) return (-EBADF); break; case F_UNLCK: break; case F_SHLCK: case F_EXLCK:#if 1/* warn a bit for now, but don't overdo it */{ static int count = 0; if (!count) { count=1; printk(KERN_WARNING "fcntl_setlk() called by process %d (%s) with broken flock() emulation\n", current->pid, current->comm); }}#endif if (!(filp->f_mode & 3)) return (-EBADF); break; default:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -