xref: /minix3/minix/servers/vfs/lock.c (revision a0814afb2e128f52ed10c9a5d4c91bf6abc22290)
1433d6423SLionel Sambuc /* This file handles advisory file locking as required by POSIX.
2433d6423SLionel Sambuc  *
3433d6423SLionel Sambuc  * The entry points into this file are
4433d6423SLionel Sambuc  *   lock_op:	perform locking operations for FCNTL system call
5433d6423SLionel Sambuc  *   lock_revive: revive processes when a lock is released
6433d6423SLionel Sambuc  */
7433d6423SLionel Sambuc 
8433d6423SLionel Sambuc #include "fs.h"
9433d6423SLionel Sambuc #include <minix/com.h>
10433d6423SLionel Sambuc #include <minix/u64.h>
11433d6423SLionel Sambuc #include <fcntl.h>
12433d6423SLionel Sambuc #include <unistd.h>
13232819ddSDavid van Moolenbroek #include <assert.h>
14433d6423SLionel Sambuc #include "file.h"
15433d6423SLionel Sambuc #include "lock.h"
16433d6423SLionel Sambuc #include "vnode.h"
17433d6423SLionel Sambuc 
18433d6423SLionel Sambuc /*===========================================================================*
19433d6423SLionel Sambuc  *				lock_op					     *
20433d6423SLionel Sambuc  *===========================================================================*/
lock_op(int fd,int req,vir_bytes arg)21232819ddSDavid van Moolenbroek int lock_op(int fd, int req, vir_bytes arg)
22433d6423SLionel Sambuc {
23433d6423SLionel Sambuc /* Perform the advisory locking required by POSIX. */
24433d6423SLionel Sambuc   int r, ltype, i, conflict = 0, unlocking = 0;
25433d6423SLionel Sambuc   mode_t mo;
26433d6423SLionel Sambuc   off_t first, last;
27232819ddSDavid van Moolenbroek   struct filp *f;
28433d6423SLionel Sambuc   struct flock flock;
29433d6423SLionel Sambuc   struct file_lock *flp, *flp2, *empty;
30433d6423SLionel Sambuc 
31232819ddSDavid van Moolenbroek   assert(req == F_GETLK || req == F_SETLK || req == F_SETLKW);
32232819ddSDavid van Moolenbroek 
33232819ddSDavid van Moolenbroek   f = fp->fp_filp[fd];
34232819ddSDavid van Moolenbroek   assert(f != NULL);
35232819ddSDavid van Moolenbroek 
36433d6423SLionel Sambuc   /* Fetch the flock structure from user space. */
37232819ddSDavid van Moolenbroek   r = sys_datacopy_wrapper(who_e, arg, VFS_PROC_NR, (vir_bytes)&flock,
38232819ddSDavid van Moolenbroek       sizeof(flock));
39433d6423SLionel Sambuc   if (r != OK) return(EINVAL);
40433d6423SLionel Sambuc 
41433d6423SLionel Sambuc   /* Make some error checks. */
42433d6423SLionel Sambuc   ltype = flock.l_type;
43433d6423SLionel Sambuc   mo = f->filp_mode;
44433d6423SLionel Sambuc   if (ltype != F_UNLCK && ltype != F_RDLCK && ltype != F_WRLCK) return(EINVAL);
45433d6423SLionel Sambuc   if (req == F_GETLK && ltype == F_UNLCK) return(EINVAL);
46433d6423SLionel Sambuc   if (!S_ISREG(f->filp_vno->v_mode) && !S_ISBLK(f->filp_vno->v_mode))
47433d6423SLionel Sambuc 	return(EINVAL);
48433d6423SLionel Sambuc   if (req != F_GETLK && ltype == F_RDLCK && (mo & R_BIT) == 0) return(EBADF);
49433d6423SLionel Sambuc   if (req != F_GETLK && ltype == F_WRLCK && (mo & W_BIT) == 0) return(EBADF);
50433d6423SLionel Sambuc 
51433d6423SLionel Sambuc   /* Compute the first and last bytes in the lock region. */
52433d6423SLionel Sambuc   switch (flock.l_whence) {
53433d6423SLionel Sambuc     case SEEK_SET:	first = 0; break;
54433d6423SLionel Sambuc     case SEEK_CUR:	first = f->filp_pos; break;
55433d6423SLionel Sambuc     case SEEK_END:	first = f->filp_vno->v_size; break;
56433d6423SLionel Sambuc     default:	return(EINVAL);
57433d6423SLionel Sambuc   }
58433d6423SLionel Sambuc 
59433d6423SLionel Sambuc   /* Check for overflow. */
60433d6423SLionel Sambuc   if (((long) flock.l_start > 0) && ((first + flock.l_start) < first))
61433d6423SLionel Sambuc 	return(EINVAL);
62433d6423SLionel Sambuc   if (((long) flock.l_start < 0) && ((first + flock.l_start) > first))
63433d6423SLionel Sambuc 	return(EINVAL);
64433d6423SLionel Sambuc   first = first + flock.l_start;
65433d6423SLionel Sambuc   last = first + flock.l_len - 1;
66433d6423SLionel Sambuc   if (flock.l_len == 0) last = MAX_FILE_POS;
67433d6423SLionel Sambuc   if (last < first) return(EINVAL);
68433d6423SLionel Sambuc 
69433d6423SLionel Sambuc   /* Check if this region conflicts with any existing lock. */
70433d6423SLionel Sambuc   empty = NULL;
71433d6423SLionel Sambuc   for (flp = &file_lock[0]; flp < &file_lock[NR_LOCKS]; flp++) {
72433d6423SLionel Sambuc 	if (flp->lock_type == 0) {
73433d6423SLionel Sambuc 		if (empty == NULL) empty = flp;
74433d6423SLionel Sambuc 		continue;	/* 0 means unused slot */
75433d6423SLionel Sambuc 	}
76433d6423SLionel Sambuc 	if (flp->lock_vnode != f->filp_vno) continue;	/* different file */
77433d6423SLionel Sambuc 	if (last < flp->lock_first) continue;	/* new one is in front */
78433d6423SLionel Sambuc 	if (first > flp->lock_last) continue;	/* new one is afterwards */
79433d6423SLionel Sambuc 	if (ltype == F_RDLCK && flp->lock_type == F_RDLCK) continue;
80433d6423SLionel Sambuc 	if (ltype != F_UNLCK && flp->lock_pid == fp->fp_pid) continue;
81433d6423SLionel Sambuc 
82433d6423SLionel Sambuc 	/* There might be a conflict.  Process it. */
83433d6423SLionel Sambuc 	conflict = 1;
84433d6423SLionel Sambuc 	if (req == F_GETLK) break;
85433d6423SLionel Sambuc 
86433d6423SLionel Sambuc 	/* If we are trying to set a lock, it just failed. */
87433d6423SLionel Sambuc 	if (ltype == F_RDLCK || ltype == F_WRLCK) {
88433d6423SLionel Sambuc 		if (req == F_SETLK) {
89433d6423SLionel Sambuc 			/* For F_SETLK, just report back failure. */
90433d6423SLionel Sambuc 			return(EAGAIN);
91433d6423SLionel Sambuc 		} else {
92433d6423SLionel Sambuc 			/* For F_SETLKW, suspend the process. */
93232819ddSDavid van Moolenbroek 			fp->fp_flock.fd = fd;
94232819ddSDavid van Moolenbroek 			fp->fp_flock.cmd = req;
95232819ddSDavid van Moolenbroek 			fp->fp_flock.arg = arg;
96232819ddSDavid van Moolenbroek 			suspend(FP_BLOCKED_ON_FLOCK);
97433d6423SLionel Sambuc 			return(SUSPEND);
98433d6423SLionel Sambuc 		}
99433d6423SLionel Sambuc 	}
100433d6423SLionel Sambuc 
101433d6423SLionel Sambuc 	/* We are clearing a lock and we found something that overlaps. */
102433d6423SLionel Sambuc 	unlocking = 1;
103433d6423SLionel Sambuc 	if (first <= flp->lock_first && last >= flp->lock_last) {
104433d6423SLionel Sambuc 		flp->lock_type = 0;	/* mark slot as unused */
105433d6423SLionel Sambuc 		nr_locks--;		/* number of locks is now 1 less */
106433d6423SLionel Sambuc 		continue;
107433d6423SLionel Sambuc 	}
108433d6423SLionel Sambuc 
109433d6423SLionel Sambuc 	/* Part of a locked region has been unlocked. */
110433d6423SLionel Sambuc 	if (first <= flp->lock_first) {
111433d6423SLionel Sambuc 		flp->lock_first = last + 1;
112433d6423SLionel Sambuc 		continue;
113433d6423SLionel Sambuc 	}
114433d6423SLionel Sambuc 
115433d6423SLionel Sambuc 	if (last >= flp->lock_last) {
116433d6423SLionel Sambuc 		flp->lock_last = first - 1;
117433d6423SLionel Sambuc 		continue;
118433d6423SLionel Sambuc 	}
119433d6423SLionel Sambuc 
120433d6423SLionel Sambuc 	/* Bad luck. A lock has been split in two by unlocking the middle. */
121433d6423SLionel Sambuc 	if (nr_locks == NR_LOCKS) return(ENOLCK);
122433d6423SLionel Sambuc 	for (i = 0; i < NR_LOCKS; i++)
123433d6423SLionel Sambuc 		if (file_lock[i].lock_type == 0) break;
124433d6423SLionel Sambuc 	flp2 = &file_lock[i];
125433d6423SLionel Sambuc 	flp2->lock_type = flp->lock_type;
126433d6423SLionel Sambuc 	flp2->lock_pid = flp->lock_pid;
127433d6423SLionel Sambuc 	flp2->lock_vnode = flp->lock_vnode;
128433d6423SLionel Sambuc 	flp2->lock_first = last + 1;
129433d6423SLionel Sambuc 	flp2->lock_last = flp->lock_last;
130433d6423SLionel Sambuc 	flp->lock_last = first - 1;
131433d6423SLionel Sambuc 	nr_locks++;
132433d6423SLionel Sambuc   }
133433d6423SLionel Sambuc   if (unlocking) lock_revive();
134433d6423SLionel Sambuc 
135433d6423SLionel Sambuc   if (req == F_GETLK) {
136433d6423SLionel Sambuc 	if (conflict) {
137433d6423SLionel Sambuc 		/* GETLK and conflict. Report on the conflicting lock. */
138433d6423SLionel Sambuc 		flock.l_type = flp->lock_type;
139433d6423SLionel Sambuc 		flock.l_whence = SEEK_SET;
140433d6423SLionel Sambuc 		flock.l_start = flp->lock_first;
141433d6423SLionel Sambuc 		flock.l_len = flp->lock_last - flp->lock_first + 1;
142433d6423SLionel Sambuc 		flock.l_pid = flp->lock_pid;
143433d6423SLionel Sambuc 
144433d6423SLionel Sambuc 	} else {
145433d6423SLionel Sambuc 		/* It is GETLK and there is no conflict. */
146433d6423SLionel Sambuc 		flock.l_type = F_UNLCK;
147433d6423SLionel Sambuc 	}
148433d6423SLionel Sambuc 
149433d6423SLionel Sambuc 	/* Copy the flock structure back to the caller. */
150232819ddSDavid van Moolenbroek 	r = sys_datacopy_wrapper(VFS_PROC_NR, (vir_bytes)&flock, who_e, arg,
151232819ddSDavid van Moolenbroek 	    sizeof(flock));
152433d6423SLionel Sambuc 	return(r);
153433d6423SLionel Sambuc   }
154433d6423SLionel Sambuc 
155433d6423SLionel Sambuc   if (ltype == F_UNLCK) return(OK);	/* unlocked a region with no locks */
156433d6423SLionel Sambuc 
157433d6423SLionel Sambuc   /* There is no conflict.  If space exists, store new lock in the table. */
158433d6423SLionel Sambuc   if (empty == NULL) return(ENOLCK);	/* table full */
159433d6423SLionel Sambuc   empty->lock_type = ltype;
160433d6423SLionel Sambuc   empty->lock_pid = fp->fp_pid;
161433d6423SLionel Sambuc   empty->lock_vnode = f->filp_vno;
162433d6423SLionel Sambuc   empty->lock_first = first;
163433d6423SLionel Sambuc   empty->lock_last = last;
164433d6423SLionel Sambuc   nr_locks++;
165433d6423SLionel Sambuc   return(OK);
166433d6423SLionel Sambuc }
167433d6423SLionel Sambuc 
168433d6423SLionel Sambuc 
169433d6423SLionel Sambuc /*===========================================================================*
170433d6423SLionel Sambuc  *				lock_revive				     *
171433d6423SLionel Sambuc  *===========================================================================*/
172*a0814afbSRichard Sailer void
lock_revive(void)173*a0814afbSRichard Sailer lock_revive(void)
174433d6423SLionel Sambuc {
175433d6423SLionel Sambuc /* Go find all the processes that are waiting for any kind of lock and
176433d6423SLionel Sambuc  * revive them all.  The ones that are still blocked will block again when
177433d6423SLionel Sambuc  * they run.  The others will complete.  This strategy is a space-time
178433d6423SLionel Sambuc  * tradeoff.  Figuring out exactly which ones to unblock now would take
179433d6423SLionel Sambuc  * extra code, and the only thing it would win would be some performance in
180433d6423SLionel Sambuc  * extremely rare circumstances (namely, that somebody actually used
181433d6423SLionel Sambuc  * locking).
182433d6423SLionel Sambuc  */
183433d6423SLionel Sambuc 
184433d6423SLionel Sambuc   struct fproc *fptr;
185433d6423SLionel Sambuc 
186433d6423SLionel Sambuc   for (fptr = &fproc[0]; fptr < &fproc[NR_PROCS]; fptr++){
187433d6423SLionel Sambuc 	if (fptr->fp_pid == PID_FREE) continue;
188232819ddSDavid van Moolenbroek 	if (fptr->fp_blocked_on == FP_BLOCKED_ON_FLOCK) {
189433d6423SLionel Sambuc 		revive(fptr->fp_endpoint, 0);
190433d6423SLionel Sambuc 	}
191433d6423SLionel Sambuc   }
192433d6423SLionel Sambuc }
193