correct references to manpage section 9 to 1x.
[minix3.git] / servers / vfs / lock.c
blob6a3233a1c28415f65fd80f11343ec66c49257842
1 /* This file handles advisory file locking as required by POSIX.
3 * The entry points into this file are
4 * lock_op: perform locking operations for FCNTL system call
5 * lock_revive: revive processes when a lock is released
6 */
8 #include "fs.h"
9 #include <minix/com.h>
10 #include <minix/u64.h>
11 #include <fcntl.h>
12 #include <unistd.h>
13 #include "file.h"
14 #include "fproc.h"
15 #include "lock.h"
16 #include "param.h"
18 #include "vnode.h"
20 /*===========================================================================*
21 * lock_op *
22 *===========================================================================*/
23 PUBLIC int lock_op(f, req)
24 struct filp *f;
25 int req; /* either F_SETLK or F_SETLKW */
27 /* Perform the advisory locking required by POSIX. */
29 int r, ltype, i, conflict = 0, unlocking = 0;
30 mode_t mo;
31 off_t first, last;
32 struct flock flock;
33 vir_bytes user_flock;
34 struct file_lock *flp, *flp2, *empty;
36 /* Fetch the flock structure from user space. */
37 user_flock = (vir_bytes) m_in.name1;
38 r = sys_datacopy(who_e, (vir_bytes) user_flock,
39 FS_PROC_NR, (vir_bytes) &flock, (phys_bytes) sizeof(flock));
40 if (r != OK) return(EINVAL);
42 /* Make some error checks. */
43 ltype = flock.l_type;
44 mo = f->filp_mode;
45 if (ltype != F_UNLCK && ltype != F_RDLCK && ltype != F_WRLCK) return(EINVAL);
46 if (req == F_GETLK && ltype == F_UNLCK) return(EINVAL);
47 if ( (f->filp_vno->v_mode & I_TYPE) != I_REGULAR) return(EINVAL);
48 if (req != F_GETLK && ltype == F_RDLCK && (mo & R_BIT) == 0) return(EBADF);
49 if (req != F_GETLK && ltype == F_WRLCK && (mo & W_BIT) == 0) return(EBADF);
51 /* Compute the first and last bytes in the lock region. */
52 switch (flock.l_whence) {
53 case SEEK_SET: first = 0; break;
54 case SEEK_CUR:
55 if (ex64hi(f->filp_pos) != 0)
57 panic(__FILE__, "lock_op: position in file too high",
58 NO_NUM);
60 first = ex64lo(f->filp_pos); break;
61 case SEEK_END: first = f->filp_vno->v_size; break;
62 default: return(EINVAL);
64 /* Check for overflow. */
65 if (((long)flock.l_start > 0) && ((first + flock.l_start) < first))
66 return(EINVAL);
67 if (((long)flock.l_start < 0) && ((first + flock.l_start) > first))
68 return(EINVAL);
69 first = first + flock.l_start;
70 last = first + flock.l_len - 1;
71 if (flock.l_len == 0) last = MAX_FILE_POS;
72 if (last < first) return(EINVAL);
74 /* Check if this region conflicts with any existing lock. */
75 empty = (struct file_lock *) 0;
76 for (flp = &file_lock[0]; flp < & file_lock[NR_LOCKS]; flp++) {
77 if (flp->lock_type == 0) {
78 if (empty == (struct file_lock *) 0) empty = flp;
79 continue; /* 0 means unused slot */
81 if (flp->lock_vnode != f->filp_vno) continue; /* different file */
82 if (last < flp->lock_first) continue; /* new one is in front */
83 if (first > flp->lock_last) continue; /* new one is afterwards */
84 if (ltype == F_RDLCK && flp->lock_type == F_RDLCK) continue;
85 if (ltype != F_UNLCK && flp->lock_pid == fp->fp_pid) continue;
87 /* There might be a conflict. Process it. */
88 conflict = 1;
89 if (req == F_GETLK) break;
91 /* If we are trying to set a lock, it just failed. */
92 if (ltype == F_RDLCK || ltype == F_WRLCK) {
93 if (req == F_SETLK) {
94 /* For F_SETLK, just report back failure. */
95 return(EAGAIN);
96 } else {
97 /* For F_SETLKW, suspend the process. */
98 suspend(XLOCK);
99 return(SUSPEND);
103 /* We are clearing a lock and we found something that overlaps. */
104 unlocking = 1;
105 if (first <= flp->lock_first && last >= flp->lock_last) {
106 flp->lock_type = 0; /* mark slot as unused */
107 nr_locks--; /* number of locks is now 1 less */
108 continue;
111 /* Part of a locked region has been unlocked. */
112 if (first <= flp->lock_first) {
113 flp->lock_first = last + 1;
114 continue;
117 if (last >= flp->lock_last) {
118 flp->lock_last = first - 1;
119 continue;
122 /* Bad luck. A lock has been split in two by unlocking the middle. */
123 if (nr_locks == NR_LOCKS) return(ENOLCK);
124 for (i = 0; i < NR_LOCKS; i++)
125 if (file_lock[i].lock_type == 0) break;
126 flp2 = &file_lock[i];
127 flp2->lock_type = flp->lock_type;
128 flp2->lock_pid = flp->lock_pid;
129 flp2->lock_vnode = flp->lock_vnode;
130 flp2->lock_first = last + 1;
131 flp2->lock_last = flp->lock_last;
132 flp->lock_last = first - 1;
133 nr_locks++;
135 if (unlocking) lock_revive();
137 if (req == F_GETLK) {
138 if (conflict) {
139 /* GETLK and conflict. Report on the conflicting lock. */
140 flock.l_type = flp->lock_type;
141 flock.l_whence = SEEK_SET;
142 flock.l_start = flp->lock_first;
143 flock.l_len = flp->lock_last - flp->lock_first + 1;
144 flock.l_pid = flp->lock_pid;
146 } else {
147 /* It is GETLK and there is no conflict. */
148 flock.l_type = F_UNLCK;
151 /* Copy the flock structure back to the caller. */
152 r = sys_datacopy(FS_PROC_NR, (vir_bytes) &flock,
153 who_e, (vir_bytes) user_flock, (phys_bytes) sizeof(flock));
154 return(r);
157 if (ltype == F_UNLCK) return(OK); /* unlocked a region with no locks */
159 /* There is no conflict. If space exists, store new lock in the table. */
160 if (empty == (struct file_lock *) 0) return(ENOLCK); /* table full */
161 empty->lock_type = ltype;
162 empty->lock_pid = fp->fp_pid;
163 empty->lock_vnode = f->filp_vno;
164 empty->lock_first = first;
165 empty->lock_last = last;
166 nr_locks++;
167 return(OK);
170 /*===========================================================================*
171 * lock_revive *
172 *===========================================================================*/
173 PUBLIC void lock_revive()
175 /* Go find all the processes that are waiting for any kind of lock and
176 * revive them all. The ones that are still blocked will block again when
177 * they run. The others will complete. This strategy is a space-time
178 * tradeoff. Figuring out exactly which ones to unblock now would take
179 * extra code, and the only thing it would win would be some performance in
180 * extremely rare circumstances (namely, that somebody actually used
181 * locking).
184 int task;
185 struct fproc *fptr;
187 for (fptr = &fproc[INIT_PROC_NR + 1]; fptr < &fproc[NR_PROCS]; fptr++){
188 if(fptr->fp_pid == PID_FREE) continue;
189 task = -fptr->fp_task;
190 if (fptr->fp_suspended == SUSPENDED && task == XLOCK) {
191 revive(fptr->fp_endpoint, 0);