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
10 #include <minix/u64.h>
14 #include "scratchpad.h"
18 /*===========================================================================*
20 *===========================================================================*/
23 int req
; /* either F_SETLK or F_SETLKW */
25 /* Perform the advisory locking required by POSIX. */
27 int r
, ltype
, i
, conflict
= 0, unlocking
= 0;
31 struct file_lock
*flp
, *flp2
, *empty
;
33 /* Fetch the flock structure from user space. */
34 r
= sys_datacopy_wrapper(who_e
, scratch(fp
).io
.io_buffer
, VFS_PROC_NR
,
35 (vir_bytes
) &flock
, sizeof(flock
));
36 if (r
!= OK
) return(EINVAL
);
38 /* Make some error checks. */
41 if (ltype
!= F_UNLCK
&& ltype
!= F_RDLCK
&& ltype
!= F_WRLCK
) return(EINVAL
);
42 if (req
== F_GETLK
&& ltype
== F_UNLCK
) return(EINVAL
);
43 if (!S_ISREG(f
->filp_vno
->v_mode
) && !S_ISBLK(f
->filp_vno
->v_mode
))
45 if (req
!= F_GETLK
&& ltype
== F_RDLCK
&& (mo
& R_BIT
) == 0) return(EBADF
);
46 if (req
!= F_GETLK
&& ltype
== F_WRLCK
&& (mo
& W_BIT
) == 0) return(EBADF
);
48 /* Compute the first and last bytes in the lock region. */
49 switch (flock
.l_whence
) {
50 case SEEK_SET
: first
= 0; break;
51 case SEEK_CUR
: first
= f
->filp_pos
; break;
52 case SEEK_END
: first
= f
->filp_vno
->v_size
; break;
53 default: return(EINVAL
);
56 /* Check for overflow. */
57 if (((long) flock
.l_start
> 0) && ((first
+ flock
.l_start
) < first
))
59 if (((long) flock
.l_start
< 0) && ((first
+ flock
.l_start
) > first
))
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
);
66 /* Check if this region conflicts with any existing lock. */
68 for (flp
= &file_lock
[0]; flp
< &file_lock
[NR_LOCKS
]; flp
++) {
69 if (flp
->lock_type
== 0) {
70 if (empty
== NULL
) empty
= flp
;
71 continue; /* 0 means unused slot */
73 if (flp
->lock_vnode
!= f
->filp_vno
) 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;
79 /* There might be a conflict. Process it. */
81 if (req
== F_GETLK
) break;
83 /* If we are trying to set a lock, it just failed. */
84 if (ltype
== F_RDLCK
|| ltype
== F_WRLCK
) {
86 /* For F_SETLK, just report back failure. */
89 /* For F_SETLKW, suspend the process. */
90 suspend(FP_BLOCKED_ON_LOCK
);
95 /* We are clearing a lock and we found something that overlaps. */
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 */
103 /* Part of a locked region has been unlocked. */
104 if (first
<= flp
->lock_first
) {
105 flp
->lock_first
= last
+ 1;
109 if (last
>= flp
->lock_last
) {
110 flp
->lock_last
= first
- 1;
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_vnode
= flp
->lock_vnode
;
122 flp2
->lock_first
= last
+ 1;
123 flp2
->lock_last
= flp
->lock_last
;
124 flp
->lock_last
= first
- 1;
127 if (unlocking
) lock_revive();
129 if (req
== F_GETLK
) {
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
;
139 /* It is GETLK and there is no conflict. */
140 flock
.l_type
= F_UNLCK
;
143 /* Copy the flock structure back to the caller. */
144 r
= sys_datacopy_wrapper(VFS_PROC_NR
, (vir_bytes
) &flock
, who_e
,
145 scratch(fp
).io
.io_buffer
, sizeof(flock
));
149 if (ltype
== F_UNLCK
) return(OK
); /* unlocked a region with no locks */
151 /* There is no conflict. If space exists, store new lock in the table. */
152 if (empty
== NULL
) return(ENOLCK
); /* table full */
153 empty
->lock_type
= ltype
;
154 empty
->lock_pid
= fp
->fp_pid
;
155 empty
->lock_vnode
= f
->filp_vno
;
156 empty
->lock_first
= first
;
157 empty
->lock_last
= last
;
163 /*===========================================================================*
165 *===========================================================================*/
168 /* Go find all the processes that are waiting for any kind of lock and
169 * revive them all. The ones that are still blocked will block again when
170 * they run. The others will complete. This strategy is a space-time
171 * tradeoff. Figuring out exactly which ones to unblock now would take
172 * extra code, and the only thing it would win would be some performance in
173 * extremely rare circumstances (namely, that somebody actually used
179 for (fptr
= &fproc
[0]; fptr
< &fproc
[NR_PROCS
]; fptr
++){
180 if (fptr
->fp_pid
== PID_FREE
) continue;
181 if (fptr
->fp_blocked_on
== FP_BLOCKED_ON_LOCK
) {
182 revive(fptr
->fp_endpoint
, 0);