#include "sys/param.h"#include "sys/types.h"#include "sys/sysmacros.h"#include "sys/systm.h"#include "sys/sysinfo.h"#include "sys/mount.h"#include "sys/dir.h"#include "sys/signal.h"#include "sys/user.h"#include "sys/errno.h"#include "sys/inode.h"#include "sys/file.h"#include "sys/ino.h"#include "sys/filsys.h"#include "sys/buf.h"#include "sys/var.h"#include "sys/proc.h"#include "sys/locking.h"/* * file lock routines * John Bass, PO Box 1223, San Luis Obispo, CA 93401 * Original design spring 1976, CalPoly, San Luis Obispo * Deadlock idea from Ed Grudzien at Basys April 1980 * Extensions Fall 1980, Onyx Systems Inc., San Jose * Linted and ported to System V by UniSoft Systems, Berkeley, CA. */#define MAXSIZE (long)(1L<<30)	/* number larger than any request */struct locklist *deadlock();/* * locking -- handles syscall requests */locking() {	struct file *fp;	struct inode *ip;	/*	 * define order and type of syscall args	 */	register struct a {		int fdes;		int flag;		off_t size;	} *uap = (struct a *)u.u_ap;	register struct locklist *cl, *nl;	off_t LB, UB;	/*	 * check for valid open file	 */	fp = getf(uap->fdes);	if(fp == NULL) return;	ip = fp->f_inode;	if ((ip->i_mode&IFMT) == IFDIR) {		u.u_error = EACCES;		return;	}	/*	 * validate ranges	 * kludge for zero length	 */	LB = fp->f_offset;	if( uap->size ) {		UB = LB + uap->size;		if(UB <= 0) UB = MAXSIZE;	}	else UB = MAXSIZE;	/*	 * test for unlock request	 */	if(uap->flag == 0) {		/*		 * starting at list head scan		 * for locks in the range by		 * this process		 */		cl = (struct locklist *)&ip->i_locklist;/* addr is pointer */		while(nl = cl->ll_link) {			/*			 * if not by this process skip to next lock			 */			if(nl->ll_proc != u.u_procp) {				cl = nl;				continue;			}			/*			 * check for locks in proper range			 */			if( UB <= nl->ll_start )				break;			if( nl->ll_end <= LB ) {				cl = nl;				continue;			}			/*			 * for locks fully contained within			 * requested range, just delete the item			 */			if( LB <= nl->ll_start && nl->ll_end <= UB) {				cl->ll_link = nl->ll_link;				lockfree(nl);				continue;			}			/*			 * if some one is sleeping on this lock			 * do a wakeup, we may free the region			 * being slept on			 */			if(nl->ll_flags & IWANT) {				nl->ll_flags &= ~IWANT;				wakeup((caddr_t)nl);			}			/*			 * middle section is being removed			 * add new lock for last section			 * modify existing lock for first section.			 * if no locks, return in error			 */			if( nl->ll_start < LB && UB < nl->ll_end) {				if( lockadd(nl,UB,nl->ll_end) ) return;				nl->ll_end = LB;				break;			}			/*			 * first section is being deleted			 * just move starting point up			 */			if( LB <= nl->ll_start && UB < nl->ll_end) {				nl->ll_start = UB;				break;			}			/*			 * must be deleting last part of this section			 * move ending point down			 * continue looking for locks covered by upper			 * limit of unlock range			 */			nl->ll_end = LB;			cl = nl;		}		/*		 * end of scan for unlock request		 */		return;	}	/*	 * request must be a lock of some kind	 * check to see if the region is lockable by this	 * process	 */	if(locked(uap->flag, ip, LB, UB))return;	cl = (struct locklist *)&ip->i_locklist;/* note addr is pointer */	/*	 * simple case, no existing locks, simply add new lock	 */	if( (nl=cl->ll_link) == NULL ) {		(void) lockadd(cl, LB, UB);		return;	}	/*	 * simple case, lock is before existing locks,	 * simply insert at head of list	 */	if( UB < nl->ll_start ) {		(void) lockadd(cl,LB,UB);		return;	}	/*	 * ending range of lock is same as start of lock by	 * another process, simply insert at head of list	 */	if( UB <= nl->ll_start && u.u_procp != nl->ll_proc ) {		(void) lockadd(cl, LB, UB);		return;	}	/*	 * request range overlaps with begining of first request	 * modify starting point in lock to include requested region	 */	if( UB >= nl->ll_start && LB < nl->ll_start ) {		nl->ll_start = LB;	}	/*	 * scan thru remaining locklist	 */	cl = nl;	for (;;) {		/*		 * actions for requests at end of list		 */		if( ( nl = cl->ll_link ) == NULL ) {			/*			 * request overlaps tail of last entry			 * extend end point			 */			if( LB <= cl->ll_end && u.u_procp == cl->ll_proc ) {				if( UB > cl->ll_end ) cl->ll_end = UB;				return;			}			/*			 * otherwise add new entry			 */			(void) lockadd(cl, LB, UB);			return;		}		/*		 * if more locks in range skip to next		 * otherwise stop scan		 */		if( nl->ll_start < LB ) {			cl = nl;		}		else {			break;		}	}	/*	 * if upper bound is fully resolved were done	 * otherwise fix end of last entry or add new entry	 */	if(UB <= cl->ll_end) return;	if(LB <= cl->ll_end && u.u_procp == cl->ll_proc) cl->ll_end = UB;	else {		if( lockadd(cl, LB, UB) ) return;		cl = cl->ll_link;	}	/*	 * end point set above may overlap later entries	 * if so delete or modify them to perform the compaction	 */	while( (nl=cl->ll_link) != NULL) {		/*		 * if we found lock by another process we must		 * be done since we validated the range above		 */		if(u.u_procp != nl->ll_proc) return;		/*		 * if the new endpoint no longer overlaps were done		 */		if(cl->ll_end < nl->ll_start) return;		/*		 * if the new range overlaps the first part of the		 * next lock, take its end point		 * and delete the next lock		 * we should be done		 */		if(cl->ll_end <= nl->ll_end) {			cl->ll_end = nl->ll_end;			cl->ll_link = nl->ll_link;			lockfree(nl);			return;		}		/*		 * the next lock is fully included in the new range		 * so it may be deleted		 */		cl->ll_link = nl->ll_link;		lockfree(nl);	}}/* * locked -- routine to scan locks and check for a locked condition */locked(flag, ip, LB, UB)register struct inode *ip;off_t LB, UB;{	register struct locklist *nl = ip->i_locklist;	/*	 * scan list while locks are in requested range	 */	while(nl != NULL && nl->ll_start < UB) {		/*		 * skip locks for this process		 * and those out of range		 */		if( nl->ll_proc == u.u_procp || nl->ll_end <= LB) {			nl = nl->ll_link;			if(nl == NULL) return(NULL);			continue;		}		/*		 * must have found lock by another process		 * if request is to test only, then exit with		 * error code		 */		if(flag>1) {			u.u_error = EACCES;			return(1);		}		/*		 * will need to sleep on lock, check for deadlock first		 * abort on error		 */		if(deadlock(nl) != NULL) return(1);		/*		 * post want flag to get awoken		 * then sleep till lock is released		 */		nl->ll_flags |= IWANT;		(void) sleep( (caddr_t)nl, PSLEP);		/*		 * set scan back to begining to catch		 * any new areas locked		 * or a partial delete		 */		nl = ip->i_locklist;		/*		 * abort if any errors		 */		if(u.u_error) return(1);	}	return(NULL);}/* * deadlock -- routine to follow chain of locks and proc table entries *	to find deadlocks on file locks. */struct locklist *deadlock(lp)register struct locklist *lp;{	register struct locklist *nl;		/*	 * scan while the process owning the lock is sleeping	 */	while(lp->ll_proc->p_stat == SSLEEP) {		/*		 * if the object the process is sleeping on is		 * NOT in the locktable every thing is ok		 * fall out of loop and return NULL		 */		nl = lp->ll_proc->p_wlock;		if( nl < &locklist[0] || nl >= &locklist[(short)v.v_flock] )			break;		/*		 * the object was a locklist entry		 * if the owner of that entry is this		 * process then a deadlock would occur		 * set error exit and return		 */		if(nl->ll_proc == u.u_procp) {			u.u_error = EDEADLOCK;			return(nl);		}		/*		 * the object was a locklist entry		 * owned by some other process		 * continue the scan with that process		 */		lp = nl;	}	return(NULL);}/* * unlock -- called by close to release all locks for this process */unlock(ip)struct inode *ip;{	register struct locklist *nl;	register struct locklist *cl;	cl = (struct locklist *)&ip->i_locklist;	while( (nl = cl->ll_link) != NULL) {		if(nl->ll_proc == u.u_procp) {			cl->ll_link = nl->ll_link;			lockfree(nl);		}		else cl = nl;	}}/* * lockalloc -- allocates free list, returns free lock items */struct locklist *lockalloc(){	register struct locklist *fl = &locklist[0];	register struct locklist *nl;	/*	 * if first entry has never been used	 * link the locklist table into the freelist	 */	if(fl->ll_proc == NULL) {		fl->ll_proc = &proc[0];		for(nl= &locklist[1]; nl < &locklist[(short)v.v_flock]; nl++) {			lockfree(nl);		}	}	/*	 * if all the locks are used error exit	 */	if( (nl=fl->ll_link) == NULL) {		u.u_error = EDEADLOCK;		return(NULL);	}	/*	 * return the next lock on the list	 */	fl->ll_link = nl->ll_link;	nl->ll_link = NULL;	return(nl);}/* * lockfree -- returns a lock item to the free list */lockfree(lp)register struct locklist *lp;{	register struct locklist *fl = &locklist[0];	/*	 * if some process is sleeping on this lock	 * wake them up	 */	if(lp->ll_flags & IWANT) {		lp->ll_flags &= ~IWANT;		wakeup((caddr_t)lp);	}	/*	 * add the lock into the free list	 */	lp->ll_link = fl->ll_link;	fl->ll_link = lp;}/* * lockadd -- routine to add item to list */lockadd(cl,LB,UB)register struct locklist *cl;off_t LB,UB;{	register struct locklist *nl;	/*	 * get a lock, return if none available	 */	nl = lockalloc();	if(nl == NULL) {		return(1);	}	/*	 * link the new entry into list at current spot	 * fill in the data from the args	 */	nl->ll_link = cl->ll_link;	cl->ll_link = nl;	nl->ll_proc = u.u_procp;	nl->ll_start = LB;	nl->ll_end = UB;	return(0);}