/* 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 the next * two functions. */ static int flock_lock_file(struct file *filp, struct file_lock *caller, unsigned int wait) { struct file_lock *fl; struct file_lock *new_fl = NULL; struct file_lock **before; int error; int change; int unlock = (caller->fl_type == F_UNLCK); /* * If we need a new lock, get it in advance to avoid races. */ if (!unlock) { error = -ENOLCK; new_fl = locks_alloc_lock(caller); if (!new_fl) goto out; } error = 0; search: change = 0; before = &filp->f_inode->i_flock; if ((fl = *before) && (fl->fl_flags & FL_POSIX)) { error = -EBUSY; goto out; } while ((fl = *before) != NULL) { if (caller->fl_file == fl->fl_file) { if (caller->fl_type == fl->fl_type) goto out; change = 1; break; } before = &fl->fl_next; } /* change means that we are changing the type of an existing lock, or * or else unlocking it. */ if (change) { /* N.B. What if the wait argument is false? */ locks_delete_lock(before, !unlock); /* * If we waited, another lock may have been added ... */ if (!unlock) goto search; } if (unlock) goto out; repeat: /* Check signals each time we start */ error = -ERESTARTSYS; if (current->signal & ~current->blocked) goto out; 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); }
/* Try to create a FLOCK lock on filp. We always insert new locks at * the head of the list. */ static int flock_lock_file(struct file *filp, struct file_lock *caller, unsigned int wait) { struct file_lock *fl; struct file_lock *new_fl; struct file_lock **before; int change = 0; before = &filp->f_inode->i_flock; if ((fl = *before) && (fl->fl_flags & F_POSIX)) return (-EBUSY); while ((fl = *before) != NULL) { if (caller->fl_file == fl->fl_file) { if (caller->fl_type == fl->fl_type) return (0); change = 1; break; } before = &fl->fl_next; } /* change means that we are changing the type of an existing lock, or * or else unlocking it. */ if (change) locks_delete_lock(before, caller->fl_type != F_UNLCK); if (caller->fl_type == F_UNLCK) return (0); if ((new_fl = locks_alloc_lock(caller)) == NULL) return (-ENOLCK); repeat: if ((fl = filp->f_inode->i_flock) && (fl->fl_flags & F_POSIX)) { locks_free_lock(new_fl); return (-EBUSY); } while (fl != NULL) { if (flock_locks_conflict(new_fl, fl)) { if (!wait) { locks_free_lock(new_fl); return (-EAGAIN); } if (current->signal & ~current->blocked) { /* Note: new_fl is not in any queue at this * point, so we must use locks_free_lock() * instead of locks_delete_lock() * Dmitry Gorodchanin 09/02/96. */ locks_free_lock(new_fl); return (-ERESTARTSYS); } locks_insert_block(fl, new_fl); interruptible_sleep_on(&new_fl->fl_wait); wake_up(&new_fl->fl_wait); if (current->signal & ~current->blocked) { /* If we are here, than we were awakened * by a signal, so new_fl is still in the * block queue of fl. We need to remove * new_fl and then free it. * Dmitry Gorodchanin 09/02/96. */ locks_delete_block(fl, new_fl); locks_free_lock(new_fl); return (-ERESTARTSYS); } goto repeat; } fl = fl->fl_next; } locks_insert_lock(&filp->f_inode->i_flock, new_fl); return (0); }