📄 locks.c
字号:
error = -EBUSY; if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & FL_POSIX)) goto out; while (fl != NULL) { if (!flock_locks_conflict(new_fl, fl)) { fl = fl->fl_next; continue; } error = -EAGAIN; if (!wait) goto out; locks_insert_block(fl, new_fl); interruptible_sleep_on(&new_fl->fl_wait); locks_delete_block(fl, new_fl); goto repeat; } locks_insert_lock(&filp->f_inode->i_flock, new_fl); new_fl = NULL; error = 0;out: if (new_fl) locks_free_lock(new_fl); return (error);}/* Add a POSIX style lock to a file. * We merge adjacent locks whenever possible. POSIX locks are sorted by owner * task, then by starting address * * Kai Petzke writes: * To make freeing a lock much faster, we keep a pointer to the lock before the * actual one. But the real gain of the new coding was, that lock_it() and * unlock_it() became one function. * * To all purists: Yes, I use a few goto's. Just pass on to the next function. */static int posix_lock_file(struct file *filp, struct file_lock *caller, unsigned int wait){ struct file_lock *fl; struct file_lock *new_fl, *new_fl2; struct file_lock *left = NULL; struct file_lock *right = NULL; struct file_lock **before; int error; int added = 0; /* * We may need two file_lock structures for this operation, * so we get them in advance to avoid races. */ new_fl = locks_empty_lock(); new_fl2 = locks_empty_lock(); error = -ENOLCK; /* "no luck" */ if (!(new_fl && new_fl2)) goto out; if (caller->fl_type != F_UNLCK) { repeat: error = -EBUSY; if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & FL_FLOCK)) goto out; while (fl != NULL) { if (!posix_locks_conflict(caller, fl)) { fl = fl->fl_next; continue; } error = -EAGAIN; if (!wait) goto out; error = -EDEADLK; if (posix_locks_deadlock(caller->fl_owner, fl->fl_owner)) goto out; error = -ERESTARTSYS; if (current->signal & ~current->blocked) goto out; locks_insert_block(fl, caller); interruptible_sleep_on(&caller->fl_wait); locks_delete_block(fl, caller); goto repeat; } } /* * We've allocated the new locks in advance, so there are no * errors possible (and no blocking operations) from here on. * * Find the first old lock with the same owner as the new lock. */ before = &filp->f_inode->i_flock; error = -EBUSY; if ((*before != NULL) && ((*before)->fl_flags & FL_FLOCK)) goto out; /* First skip locks owned by other processes. */ while ((fl = *before) && (caller->fl_owner != fl->fl_owner)) { before = &fl->fl_next; } /* Process locks with this owner. */ while ((fl = *before) && (caller->fl_owner == fl->fl_owner)) { /* Detect adjacent or overlapping regions (if same lock type) */ if (caller->fl_type == fl->fl_type) { if (fl->fl_end < caller->fl_start - 1) goto next_lock; /* If the next lock in the list has entirely bigger * addresses than the new one, insert the lock here. */ if (fl->fl_start > caller->fl_end + 1) break; /* If we come here, the new and old lock are of the * same type and adjacent or overlapping. Make one * lock yielding from the lower start address of both * locks to the higher end address. */ if (fl->fl_start > caller->fl_start) fl->fl_start = caller->fl_start; else caller->fl_start = fl->fl_start; if (fl->fl_end < caller->fl_end) fl->fl_end = caller->fl_end; else caller->fl_end = fl->fl_end; if (added) { locks_delete_lock(before, 0); continue; } caller = fl; added = 1; } else { /* Processing for different lock types is a bit * more complex. */ if (fl->fl_end < caller->fl_start) goto next_lock; if (fl->fl_start > caller->fl_end) break; if (caller->fl_type == F_UNLCK) added = 1; if (fl->fl_start < caller->fl_start) left = fl; /* If the next lock in the list has a higher end * address than the new one, insert the new one here. */ if (fl->fl_end > caller->fl_end) { right = fl; break; } if (fl->fl_start >= caller->fl_start) { /* The new lock completely replaces an old * one (This may happen several times). */ if (added) { 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); fl->fl_start = caller->fl_start; fl->fl_end = caller->fl_end; fl->fl_type = caller->fl_type; 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_init_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 (in this * case, even F_UNLCK may fail!). */ left = locks_init_lock(new_fl2, right); locks_insert_lock(before, left); new_fl2 = NULL; } 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: /* * Free any unused locks. (They haven't * ever been used, so we use kfree().) */ if (new_fl) kfree(new_fl); if (new_fl2) kfree(new_fl2); return error;}/* * Allocate an empty lock structure. We can use GFP_KERNEL now that * all allocations are done in advance. */static struct file_lock *locks_empty_lock(void){ return ((struct file_lock *) kmalloc(sizeof(struct file_lock), GFP_KERNEL));}/* * Initialize a new lock from an existing file_lock structure. */static struct file_lock *locks_init_lock(struct file_lock *new, struct file_lock *fl){ if (new) { memset(new, 0, sizeof(*new)); new->fl_owner = fl->fl_owner; new->fl_file = fl->fl_file; new->fl_flags = fl->fl_flags; new->fl_type = fl->fl_type; new->fl_start = fl->fl_start; new->fl_end = fl->fl_end; } return new;}/* Insert file lock fl into an inode's lock list at the position indicated * by pos. At the same time add the lock to the global file lock list. */static void locks_insert_lock(struct file_lock **pos, struct file_lock *fl){ fl->fl_nextlink = file_lock_table; fl->fl_prevlink = NULL; if (file_lock_table != NULL) file_lock_table->fl_prevlink = fl; file_lock_table = fl; fl->fl_next = *pos; /* insert into file's list */ *pos = fl; return;}/* Delete a lock and free it. * First remove our lock from the active lock lists. Then call * locks_wake_up_blocks() to wake up processes that are blocked * waiting for this lock. Finally free the lock structure. */static void locks_delete_lock(struct file_lock **thisfl_p, unsigned int wait){ struct file_lock *thisfl; struct file_lock *prevfl; struct file_lock *nextfl; thisfl = *thisfl_p; *thisfl_p = thisfl->fl_next; prevfl = thisfl->fl_prevlink; nextfl = thisfl->fl_nextlink; if (nextfl != NULL) nextfl->fl_prevlink = prevfl; if (prevfl != NULL) prevfl->fl_nextlink = nextfl; else file_lock_table = nextfl; locks_wake_up_blocks(thisfl, wait); locks_free_lock(thisfl); return;}static char *lock_get_status(struct file_lock *fl, int id, char *pfx){ static char temp[129]; char *p = temp; struct inode *inode; inode = fl->fl_file->f_inode; p += sprintf(p, "%d:%s ", id, pfx); if (fl->fl_flags & FL_POSIX) { p += sprintf(p, "%6s %s ", (fl->fl_flags & FL_BROKEN) ? "BROKEN" : (fl->fl_flags & FL_ACCESS) ? "ACCESS" : "POSIX ", (IS_MANDLOCK(inode) && (inode->i_mode & (S_IXGRP | S_ISGID)) == S_ISGID) ? "MANDATORY" : "ADVISORY "); } else { p += sprintf(p, "FLOCK ADVISORY "); } p += sprintf(p, "%s ", (fl->fl_type == F_RDLCK) ? "READ " : "WRITE"); p += sprintf(p, "%d %s:%ld %ld %ld ", fl->fl_owner ? fl->fl_owner->pid : 0, kdevname(inode->i_dev), inode->i_ino, fl->fl_start, fl->fl_end); sprintf(p, "%08lx %08lx %08lx %08lx %08lx\n", (long)fl, (long)fl->fl_prevlink, (long)fl->fl_nextlink, (long)fl->fl_next, (long)fl->fl_nextblock); return (temp);}static inline int copy_lock_status(char *p, char **q, off_t pos, int len, off_t offset, int length){ int i; i = pos - offset; if (i > 0) { if (i >= length) { i = len + length - i; memcpy(*q, p, i); *q += i; return (0); } if (i < len) { p += len - i; } else i = len; memcpy(*q, p, i); *q += i; } return (1);}int get_locks_status(char *buffer, char **start, off_t offset, int length){ struct file_lock *fl; struct file_lock *bfl; char *p; char *q = buffer; int i; int len; off_t pos = 0; for (fl = file_lock_table, i = 1; fl != NULL; fl = fl->fl_nextlink, i++) { p = lock_get_status(fl, i, ""); len = strlen(p); pos += len; if (!copy_lock_status(p, &q, pos, len, offset, length)) goto done; if ((bfl = fl->fl_nextblock) == NULL) continue; do { p = lock_get_status(bfl, i, " ->"); len = strlen(p); pos += len; if (!copy_lock_status(p, &q, pos, len, offset, length)) goto done; } while ((bfl = bfl->fl_nextblock) != fl); }done: if (q != buffer) *start = buffer; return (q - buffer);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -