📄 locks.c
字号:
locks_delete_lock(before, 0); continue; } /* Replace the old lock with the new one. * Wake up anybody waiting for the old one, * as the change in lock type might satisfy * their needs. */ locks_wake_up_blocks(fl, 0); /* This cannot schedule()! */ fl->fl_start = caller->fl_start; fl->fl_end = caller->fl_end; fl->fl_type = caller->fl_type; fl->fl_u = caller->fl_u; caller = fl; added = 1; } } /* Go on to next lock. */ next_lock: before = &fl->fl_next; } error = 0; if (!added) { if (caller->fl_type == F_UNLCK) goto out; locks_copy_lock(new_fl, caller); locks_insert_lock(before, new_fl); new_fl = NULL; } if (right) { if (left == right) { /* The new lock breaks the old one in two pieces, * so we have to use the second new lock. */ left = new_fl2; new_fl2 = NULL; locks_copy_lock(left, right); locks_insert_lock(before, left); } right->fl_start = caller->fl_end + 1; locks_wake_up_blocks(right, 0); } if (left) { left->fl_end = caller->fl_start - 1; locks_wake_up_blocks(left, 0); }out: unlock_kernel();out_nolock: /* * Free any unused locks. */ if (new_fl) locks_free_lock(new_fl); if (new_fl2) locks_free_lock(new_fl2); return error;}static inline int flock_translate_cmd(int cmd) {#ifdef MSNFS if (cmd & LOCK_MAND) return cmd & (LOCK_MAND | LOCK_RW);#endif switch (cmd &~ LOCK_NB) { case LOCK_SH: return F_RDLCK; case LOCK_EX: return F_WRLCK; case LOCK_UN: return F_UNLCK; } return -EINVAL;}/** * __get_lease - revoke all outstanding leases on file * @inode: the inode of the file to return * @mode: the open mode (read or write) * * get_lease (inlined for speed) has checked there already * is a lease on this file. Leases are broken on a call to open() * or truncate(). This function can sleep unless you * specified %O_NONBLOCK to your open(). */int __get_lease(struct inode *inode, unsigned int mode){ int error = 0, future; struct file_lock *new_fl, *flock; struct file_lock *fl; int alloc_err; alloc_err = lease_alloc(NULL, 0, &new_fl); lock_kernel(); flock = inode->i_flock; if (flock->fl_type & F_INPROGRESS) { if ((mode & O_NONBLOCK) || (flock->fl_owner == current->files)) { error = -EWOULDBLOCK; goto out; } if (alloc_err != 0) { error = alloc_err; goto out; } do { error = locks_block_on(flock, new_fl); if (error != 0) goto out; flock = inode->i_flock; if (!(flock && (flock->fl_flags & FL_LEASE))) goto out; } while (flock->fl_type & F_INPROGRESS); } if (mode & FMODE_WRITE) { /* If we want write access, we have to revoke any lease. */ future = F_UNLCK | F_INPROGRESS; } else if (flock->fl_type & F_WRLCK) { /* Downgrade the exclusive lease to a read-only lease. */ future = F_RDLCK | F_INPROGRESS; } else { /* the existing lease was read-only, so we can read too. */ goto out; } if (alloc_err && (flock->fl_owner != current->files)) { error = alloc_err; goto out; } fl = flock; do { fl->fl_type = future; fl = fl->fl_next; } while (fl != NULL && (fl->fl_flags & FL_LEASE)); kill_fasync(&flock->fl_fasync, SIGIO, POLL_MSG); if ((mode & O_NONBLOCK) || (flock->fl_owner == current->files)) { error = -EWOULDBLOCK; goto out; } if (lease_break_time > 0) error = lease_break_time * HZ; else error = 0;restart: error = locks_block_on_timeout(flock, new_fl, error); if (error == 0) { /* We timed out. Unilaterally break the lease. */ locks_delete_lock(&inode->i_flock, 0); printk(KERN_WARNING "lease timed out\n"); } else if (error > 0) { flock = inode->i_flock; if (flock && (flock->fl_flags & FL_LEASE)) goto restart; error = 0; }out: unlock_kernel(); if (!alloc_err) locks_free_lock(new_fl); return error;}/** * lease_get_mtime * @inode: the inode * * This is to force NFS clients to flush their caches for files with * exclusive leases. The justification is that if someone has an * exclusive lease, then they could be modifiying it. */time_t lease_get_mtime(struct inode *inode){ struct file_lock *flock = inode->i_flock; if (flock && (flock->fl_flags & FL_LEASE) && (flock->fl_type & F_WRLCK)) return CURRENT_TIME; return inode->i_mtime;}/** * fcntl_getlease - Enquire what lease is currently active * @filp: the file * * The value returned by this function will be one of * * %F_RDLCK to indicate a read-only (type II) lease is held. * * %F_WRLCK to indicate an exclusive lease is held. * * XXX: sfr & i disagree over whether F_INPROGRESS * should be returned to userspace. */int fcntl_getlease(struct file *filp){ struct file_lock *fl; fl = filp->f_dentry->d_inode->i_flock; if ((fl == NULL) || ((fl->fl_flags & FL_LEASE) == 0)) return F_UNLCK; return fl->fl_type & ~F_INPROGRESS;}/* We already had a lease on this file; just change its type */static int lease_modify(struct file_lock **before, int arg, int fd, struct file *filp){ struct file_lock *fl = *before; int error = assign_type(fl, arg); if (error < 0) goto out; locks_wake_up_blocks(fl, 0); if (arg == F_UNLCK) { filp->f_owner.pid = 0; filp->f_owner.uid = 0; filp->f_owner.euid = 0; filp->f_owner.signum = 0; locks_delete_lock(before, 0); fasync_helper(fd, filp, 0, &fl->fl_fasync); }out: return error;}/** * fcntl_setlease - sets a lease on an open file * @fd: open file descriptor * @filp: file pointer * @arg: type of lease to obtain * * Call this fcntl to establish a lease on the file. * Note that you also need to call %F_SETSIG to * receive a signal when the lease is broken. */int fcntl_setlease(unsigned int fd, struct file *filp, long arg){ struct file_lock *fl, **before, **my_before = NULL; struct dentry *dentry; struct inode *inode; int error, rdlease_count = 0, wrlease_count = 0; dentry = filp->f_dentry; inode = dentry->d_inode; if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE)) return -EACCES; if (!S_ISREG(inode->i_mode)) return -EINVAL; /* * FIXME: What about F_RDLCK and files open for writing? */ if ((arg == F_WRLCK) && ((atomic_read(&dentry->d_count) > 1) || (atomic_read(&inode->i_count) > 1))) return -EAGAIN; before = &inode->i_flock; lock_kernel(); while ((fl = *before) != NULL) { if (fl->fl_flags != FL_LEASE) break; if (fl->fl_file == filp) my_before = before; else if (fl->fl_type & F_WRLCK) wrlease_count++; else rdlease_count++; before = &fl->fl_next; } if ((arg == F_RDLCK && (wrlease_count > 0)) || (arg == F_WRLCK && ((rdlease_count + wrlease_count) > 0))) { error = -EAGAIN; goto out_unlock; } if (my_before != NULL) { error = lease_modify(my_before, arg, fd, filp); goto out_unlock; } if (arg == F_UNLCK) { error = 0; goto out_unlock; } if (!leases_enable) { error = -EINVAL; goto out_unlock; } error = lease_alloc(filp, arg, &fl); if (error) goto out_unlock; error = fasync_helper(fd, filp, 1, &fl->fl_fasync); if (error < 0) { locks_free_lock(fl); goto out_unlock; } fl->fl_next = *before; *before = fl; list_add(&fl->fl_link, &file_lock_list); filp->f_owner.pid = current->pid; filp->f_owner.uid = current->uid; filp->f_owner.euid = current->euid;out_unlock: unlock_kernel(); return error;}/** * sys_flock: - flock() system call. * @fd: the file descriptor to lock. * @cmd: the type of lock to apply. * * Apply a %FL_FLOCK style lock to an open file descriptor. * The @cmd can be one of * * %LOCK_SH -- a shared lock. * * %LOCK_EX -- an exclusive lock. * * %LOCK_UN -- remove an existing lock. * * %LOCK_MAND -- a `mandatory' flock. This exists to emulate Windows Share Modes. * * %LOCK_MAND can be combined with %LOCK_READ or %LOCK_WRITE to allow other * processes read and write access respectively. */asmlinkage long sys_flock(unsigned int fd, unsigned int cmd){ struct file *filp; int error, type; error = -EBADF; filp = fget(fd); if (!filp) goto out; error = flock_translate_cmd(cmd); if (error < 0) goto out_putf; type = error; error = -EBADF; if ((type != F_UNLCK)#ifdef MSNFS && !(type & LOCK_MAND)#endif && !(filp->f_mode & 3)) goto out_putf; lock_kernel(); error = flock_lock_file(filp, type, (cmd & (LOCK_UN | LOCK_NB)) ? 0 : 1); unlock_kernel();out_putf: fput(filp);out: return error;}/* 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){ struct file *filp; struct file_lock *fl, file_lock; struct flock flock; int error; error = -EFAULT; if (copy_from_user(&flock, l, sizeof(flock))) goto out; error = -EINVAL; if ((flock.l_type != F_RDLCK) && (flock.l_type != F_WRLCK)) goto out; error = -EBADF; filp = fget(fd); if (!filp) goto out; error = flock_to_posix_lock(filp, &file_lock, &flock); if (error) goto out_putf; if (filp->f_op && filp->f_op->lock) { error = filp->f_op->lock(filp, F_GETLK, &file_lock); if (error < 0) goto out_putf; else if (error == LOCK_USE_CLNT) /* Bypass for NFS with no locking - 2.0.36 compat */ fl = posix_test_lock(filp, &file_lock); else fl = (file_lock.fl_type == F_UNLCK ? NULL : &file_lock); } else { fl = posix_test_lock(filp, &file_lock); } flock.l_type = F_UNLCK; if (fl != NULL) { flock.l_pid = fl->fl_pid;#if BITS_PER_LONG == 32 /* * Make sure we can represent the posix lock via * legacy 32bit flock. */ error = -EOVERFLOW; if (fl->fl_start > OFFT_OFFSET_MAX) goto out_putf; if ((fl->fl_end != OFFSET_MAX) && (fl->fl_end > OFFT_OFFSET_MAX)) goto out_putf;#endif 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; } error = -EFAULT; if (!copy_to_user(l, &flock, sizeof(flock))) error = 0; out_putf: fput(filp);out: return error;}/* Apply the lock described by l to an open file descriptor. * This implements both the F_SETLK and F_SETLKW commands of fcntl(). */int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l){ struct file *filp; struct file_lock *file_lock = locks_alloc_lock(0); struct flock flock; struct inode *inode; int error; if (file_lock == NULL) return -ENOLCK; /* * This might block, so we do it before checking the inode. */ error = -EFAULT; if (copy_from_user(&flock, l, sizeof(flock))) goto out; /* Get arguments and validate them ... */ error = -EBADF; filp = fget(fd); if (!filp) goto out; error = -EINVAL; inode = filp->f_dentry->d_inode; /* 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) { struct address_space *mapping = inode->i_mapping; if (mapping->i_mmap_shared != NULL) { error = -EAGAIN; goto out_putf;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -