📄 locks.c
字号:
} } error = flock_to_posix_lock(filp, file_lock, &flock); if (error) goto out_putf; error = -EBADF; switch (flock.l_type) { case F_RDLCK: if (!(filp->f_mode & FMODE_READ)) goto out_putf; break; case F_WRLCK: if (!(filp->f_mode & FMODE_WRITE)) goto out_putf; break; case F_UNLCK: break; case F_SHLCK: case F_EXLCK:#ifdef __sparc__/* 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); }} if (!(filp->f_mode & 3)) goto out_putf; break;#endif default: error = -EINVAL; goto out_putf; } if (filp->f_op && filp->f_op->lock != NULL) { error = filp->f_op->lock(filp, cmd, file_lock); if (error < 0) goto out_putf; } error = posix_lock_file(filp, file_lock, cmd == F_SETLKW);out_putf: fput(filp);out: locks_free_lock(file_lock); return error;}#if BITS_PER_LONG == 32/* Report the first existing lock that would conflict with l. * This implements the F_GETLK command of fcntl(). */int fcntl_getlk64(unsigned int fd, struct flock64 *l){ struct file *filp; struct file_lock *fl, file_lock; struct flock64 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 = flock64_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; 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_setlk64(unsigned int fd, unsigned int cmd, struct flock64 *l){ struct file *filp; struct file_lock *file_lock = locks_alloc_lock(0); struct flock64 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; } } error = flock64_to_posix_lock(filp, file_lock, &flock); if (error) goto out_putf; error = -EBADF; switch (flock.l_type) { case F_RDLCK: if (!(filp->f_mode & FMODE_READ)) goto out_putf; break; case F_WRLCK: if (!(filp->f_mode & FMODE_WRITE)) goto out_putf; break; case F_UNLCK: break; case F_SHLCK: case F_EXLCK: default: error = -EINVAL; goto out_putf; } if (filp->f_op && filp->f_op->lock != NULL) { error = filp->f_op->lock(filp, cmd, file_lock); if (error < 0) goto out_putf; } error = posix_lock_file(filp, file_lock, cmd == F_SETLKW64);out_putf: fput(filp);out: locks_free_lock(file_lock); return error;}#endif /* BITS_PER_LONG == 32 *//* * This function is called when the file is being removed * from the task's fd array. */void locks_remove_posix(struct file *filp, fl_owner_t owner){ struct inode * inode = filp->f_dentry->d_inode; struct file_lock *fl; struct file_lock **before; /* * For POSIX locks we free all locks on this file for the given task. */ if (!inode->i_flock) { /* * Notice that something might be grabbing a lock right now. * Consider it as a race won by us - event is async, so even if * we miss the lock added we can trivially consider it as added * after we went through this call. */ return; } lock_kernel(); before = &inode->i_flock; while ((fl = *before) != NULL) { if ((fl->fl_flags & FL_POSIX) && fl->fl_owner == owner) { locks_unlock_delete(before); before = &inode->i_flock; continue; } before = &fl->fl_next; } unlock_kernel();}/* * This function is called on the last close of an open file. */void locks_remove_flock(struct file *filp){ struct inode * inode = filp->f_dentry->d_inode; struct file_lock *fl; struct file_lock **before; if (!inode->i_flock) return; lock_kernel(); before = &inode->i_flock; while ((fl = *before) != NULL) { if ((fl->fl_flags & (FL_FLOCK|FL_LEASE)) && (fl->fl_file == filp)) { locks_delete_lock(before, 0); continue; } before = &fl->fl_next; } unlock_kernel();}/** * posix_block_lock - blocks waiting for a file lock * @blocker: the lock which is blocking * @waiter: the lock which conflicts and has to wait * * lockd needs to block waiting for locks. */voidposix_block_lock(struct file_lock *blocker, struct file_lock *waiter){ locks_insert_block(blocker, waiter);}/** * posix_unblock_lock - stop waiting for a file lock * @waiter: the lock which was waiting * * lockd needs to block waiting for locks. */voidposix_unblock_lock(struct file_lock *waiter){ if (!list_empty(&waiter->fl_block)) locks_delete_block(waiter);}static void lock_get_status(char* out, struct file_lock *fl, int id, char *pfx){ struct inode *inode = NULL; if (fl->fl_file != NULL) inode = fl->fl_file->f_dentry->d_inode; out += sprintf(out, "%d:%s ", id, pfx); if (fl->fl_flags & FL_POSIX) { out += sprintf(out, "%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 "); } else if (fl->fl_flags & FL_FLOCK) {#ifdef MSNFS if (fl->fl_type & LOCK_MAND) { out += sprintf(out, "FLOCK MSNFS "); } else#endif out += sprintf(out, "FLOCK ADVISORY "); } else if (fl->fl_flags & FL_LEASE) { out += sprintf(out, "LEASE MANDATORY "); } else { out += sprintf(out, "UNKNOWN UNKNOWN "); }#ifdef MSNFS if (fl->fl_type & LOCK_MAND) { out += sprintf(out, "%s ", (fl->fl_type & LOCK_READ) ? (fl->fl_type & LOCK_WRITE) ? "RW " : "READ " : (fl->fl_type & LOCK_WRITE) ? "WRITE" : "NONE "); } else#endif out += sprintf(out, "%s ", (fl->fl_type & F_WRLCK) ? "WRITE" : "READ "); out += sprintf(out, "%d %s:%ld ", fl->fl_pid, inode ? kdevname(inode->i_dev) : "<none>", inode ? inode->i_ino : 0); out += sprintf(out, "%Ld ", fl->fl_start); if (fl->fl_end == OFFSET_MAX) out += sprintf(out, "EOF "); else out += sprintf(out, "%Ld ", fl->fl_end); sprintf(out, "%08lx %08lx %08lx %08lx %08lx\n", (long)fl, (long)fl->fl_link.prev, (long)fl->fl_link.next, (long)fl->fl_next, (long)fl->fl_block.next);}static void move_lock_status(char **p, off_t* pos, off_t offset){ 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;}/** * 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(); 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;}#ifdef MSNFS/** * lock_may_read - checks that the region is free of locks * @inode: the inode that is being read * @start: the first byte to read * @len: the number of bytes to read * * Emulates Windows locking requirements. Whole-file * mandatory locks (share modes) can prohibit a read and * byte-range POSIX locks can prohibit a read if they overlap. * * N.B. this function is only ever called * from knfsd and ownership of locks is never checked. */int lock_may_read(struct inode *inode, loff_t start, unsigned long len){ struct file_lock *fl; int result = 1; lock_kernel(); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (fl->fl_flags == FL_POSIX) { if (fl->fl_type == F_RDLCK) continue; if ((fl->fl_end < start) || (fl->fl_start > (start + len))) continue; } else if (fl->fl_flags == FL_FLOCK) { if (!(fl->fl_type & LOCK_MAND)) continue; if (fl->fl_type & LOCK_READ) continue; } else continue; result = 0; break; } unlock_kernel(); return result;}/** * lock_may_write - checks that the region is free of locks * @inode: the inode that is being written * @start: the first byte to write * @len: the number of bytes to write * * Emulates Windows locking requirements. Whole-file * mandatory locks (share modes) can prohibit a write and * byte-range POSIX locks can prohibit a write if they overlap. * * N.B. this function is only ever called * from knfsd and ownership of locks is never checked. */int lock_may_write(struct inode *inode, loff_t start, unsigned long len){ struct file_lock *fl; int result = 1; lock_kernel(); for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { if (fl->fl_flags == FL_POSIX) { if ((fl->fl_end < start) || (fl->fl_start > (start + len))) continue; } else if (fl->fl_flags == FL_FLOCK) { if (!(fl->fl_type & LOCK_MAND)) continue; if (fl->fl_type & LOCK_WRITE) continue; } else continue; result = 0; break; } unlock_kernel(); return result;}#endifstatic int __init filelock_init(void){ filelock_cache = kmem_cache_create("file lock cache", sizeof(struct file_lock), 0, 0, init_once, NULL); if (!filelock_cache) panic("cannot create file lock slab cache"); return 0;}module_init(filelock_init)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -