source: trunk/minix/servers/fs/lock.c@ 21

Last change on this file since 21 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 5.9 KB
Line 
1/* This file handles advisory file locking as required by POSIX.
2 *
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 */
7
8#include "fs.h"
9#include <minix/com.h>
10#include <fcntl.h>
11#include <unistd.h>
12#include "file.h"
13#include "fproc.h"
14#include "inode.h"
15#include "lock.h"
16#include "param.h"
17
18/*===========================================================================*
19 * lock_op *
20 *===========================================================================*/
21PUBLIC int lock_op(f, req)
22struct filp *f;
23int req; /* either F_SETLK or F_SETLKW */
24{
25/* Perform the advisory locking required by POSIX. */
26
27 int r, ltype, i, conflict = 0, unlocking = 0;
28 mode_t mo;
29 off_t first, last;
30 struct flock flock;
31 vir_bytes user_flock;
32 struct file_lock *flp, *flp2, *empty;
33
34 /* Fetch the flock structure from user space. */
35 user_flock = (vir_bytes) m_in.name1;
36 r = sys_datacopy(who_e, (vir_bytes) user_flock,
37 FS_PROC_NR, (vir_bytes) &flock, (phys_bytes) sizeof(flock));
38 if (r != OK) return(EINVAL);
39
40 /* Make some error checks. */
41 ltype = flock.l_type;
42 mo = f->filp_mode;
43 if (ltype != F_UNLCK && ltype != F_RDLCK && ltype != F_WRLCK) return(EINVAL);
44 if (req == F_GETLK && ltype == F_UNLCK) return(EINVAL);
45 if ( (f->filp_ino->i_mode & I_TYPE) != I_REGULAR) return(EINVAL);
46 if (req != F_GETLK && ltype == F_RDLCK && (mo & R_BIT) == 0) return(EBADF);
47 if (req != F_GETLK && ltype == F_WRLCK && (mo & W_BIT) == 0) return(EBADF);
48
49 /* Compute the first and last bytes in the lock region. */
50 switch (flock.l_whence) {
51 case SEEK_SET: first = 0; break;
52 case SEEK_CUR: first = f->filp_pos; break;
53 case SEEK_END: first = f->filp_ino->i_size; break;
54 default: return(EINVAL);
55 }
56 /* Check for overflow. */
57 if (((long)flock.l_start > 0) && ((first + flock.l_start) < first))
58 return(EINVAL);
59 if (((long)flock.l_start < 0) && ((first + flock.l_start) > first))
60 return(EINVAL);
61 first = first + flock.l_start;
62 last = first + flock.l_len - 1;
63 if (flock.l_len == 0) last = MAX_FILE_POS;
64 if (last < first) return(EINVAL);
65
66 /* Check if this region conflicts with any existing lock. */
67 empty = (struct file_lock *) 0;
68 for (flp = &file_lock[0]; flp < & file_lock[NR_LOCKS]; flp++) {
69 if (flp->lock_type == 0) {
70 if (empty == (struct file_lock *) 0) empty = flp;
71 continue; /* 0 means unused slot */
72 }
73 if (flp->lock_inode != f->filp_ino) continue; /* different file */
74 if (last < flp->lock_first) continue; /* new one is in front */
75 if (first > flp->lock_last) continue; /* new one is afterwards */
76 if (ltype == F_RDLCK && flp->lock_type == F_RDLCK) continue;
77 if (ltype != F_UNLCK && flp->lock_pid == fp->fp_pid) continue;
78
79 /* There might be a conflict. Process it. */
80 conflict = 1;
81 if (req == F_GETLK) break;
82
83 /* If we are trying to set a lock, it just failed. */
84 if (ltype == F_RDLCK || ltype == F_WRLCK) {
85 if (req == F_SETLK) {
86 /* For F_SETLK, just report back failure. */
87 return(EAGAIN);
88 } else {
89 /* For F_SETLKW, suspend the process. */
90 suspend(XLOCK);
91 return(SUSPEND);
92 }
93 }
94
95 /* We are clearing a lock and we found something that overlaps. */
96 unlocking = 1;
97 if (first <= flp->lock_first && last >= flp->lock_last) {
98 flp->lock_type = 0; /* mark slot as unused */
99 nr_locks--; /* number of locks is now 1 less */
100 continue;
101 }
102
103 /* Part of a locked region has been unlocked. */
104 if (first <= flp->lock_first) {
105 flp->lock_first = last + 1;
106 continue;
107 }
108
109 if (last >= flp->lock_last) {
110 flp->lock_last = first - 1;
111 continue;
112 }
113
114 /* Bad luck. A lock has been split in two by unlocking the middle. */
115 if (nr_locks == NR_LOCKS) return(ENOLCK);
116 for (i = 0; i < NR_LOCKS; i++)
117 if (file_lock[i].lock_type == 0) break;
118 flp2 = &file_lock[i];
119 flp2->lock_type = flp->lock_type;
120 flp2->lock_pid = flp->lock_pid;
121 flp2->lock_inode = flp->lock_inode;
122 flp2->lock_first = last + 1;
123 flp2->lock_last = flp->lock_last;
124 flp->lock_last = first - 1;
125 nr_locks++;
126 }
127 if (unlocking) lock_revive();
128
129 if (req == F_GETLK) {
130 if (conflict) {
131 /* GETLK and conflict. Report on the conflicting lock. */
132 flock.l_type = flp->lock_type;
133 flock.l_whence = SEEK_SET;
134 flock.l_start = flp->lock_first;
135 flock.l_len = flp->lock_last - flp->lock_first + 1;
136 flock.l_pid = flp->lock_pid;
137
138 } else {
139 /* It is GETLK and there is no conflict. */
140 flock.l_type = F_UNLCK;
141 }
142
143 /* Copy the flock structure back to the caller. */
144 r = sys_datacopy(FS_PROC_NR, (vir_bytes) &flock,
145 who_e, (vir_bytes) user_flock, (phys_bytes) sizeof(flock));
146 return(r);
147 }
148
149 if (ltype == F_UNLCK) return(OK); /* unlocked a region with no locks */
150
151 /* There is no conflict. If space exists, store new lock in the table. */
152 if (empty == (struct file_lock *) 0) return(ENOLCK); /* table full */
153 empty->lock_type = ltype;
154 empty->lock_pid = fp->fp_pid;
155 empty->lock_inode = f->filp_ino;
156 empty->lock_first = first;
157 empty->lock_last = last;
158 nr_locks++;
159 return(OK);
160}
161
162/*===========================================================================*
163 * lock_revive *
164 *===========================================================================*/
165PUBLIC void lock_revive()
166{
167/* Go find all the processes that are waiting for any kind of lock and
168 * revive them all. The ones that are still blocked will block again when
169 * they run. The others will complete. This strategy is a space-time
170 * tradeoff. Figuring out exactly which ones to unblock now would take
171 * extra code, and the only thing it would win would be some performance in
172 * extremely rare circumstances (namely, that somebody actually used
173 * locking).
174 */
175
176 int task;
177 struct fproc *fptr;
178
179 for (fptr = &fproc[INIT_PROC_NR + 1]; fptr < &fproc[NR_PROCS]; fptr++){
180 if(fptr->fp_pid == PID_FREE) continue;
181 task = -fptr->fp_task;
182 if (fptr->fp_suspended == SUSPENDED && task == XLOCK) {
183 revive(fptr->fp_endpoint, 0);
184 }
185 }
186}
Note: See TracBrowser for help on using the repository browser.