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>
15 #include "scratchpad.h"
20 /*===========================================================================*
22 *===========================================================================*/
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;
33 struct file_lock
*flp
, *flp2
, *empty
;
35 /* Fetch the flock structure from user space. */
36 r
= sys_datacopy(who_e
, (vir_bytes
) scratch(fp
).io
.io_buffer
, VFS_PROC_NR
,
37 (vir_bytes
) &flock
, sizeof(flock
));
38 if (r
!= OK
) return(EINVAL
);
40 /* Make some error checks. */
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 (!S_ISREG(f
->filp_vno
->v_mode
) && !S_ISBLK(f
->filp_vno
->v_mode
))
47 if (req
!= F_GETLK
&& ltype
== F_RDLCK
&& (mo
& R_BIT
) == 0) return(EBADF
);
48 if (req
!= F_GETLK
&& ltype
== F_WRLCK
&& (mo
& W_BIT
) == 0) return(EBADF
);
50 /* Compute the first and last bytes in the lock region. */
51 switch (flock
.l_whence
) {
52 case SEEK_SET
: first
= 0; break;
54 if (ex64hi(f
->filp_pos
) != 0)
55 panic("lock_op: position in file too high");
56 first
= ex64lo(f
->filp_pos
);
58 case SEEK_END
: first
= f
->filp_vno
->v_size
; break;
59 default: return(EINVAL
);
62 /* Check for overflow. */
63 if (((long) flock
.l_start
> 0) && ((first
+ flock
.l_start
) < first
))
65 if (((long) flock
.l_start
< 0) && ((first
+ flock
.l_start
) > first
))
67 first
= first
+ flock
.l_start
;
68 last
= first
+ flock
.l_len
- 1;
69 if (flock
.l_len
== 0) last
= MAX_FILE_POS
;
70 if (last
< first
) return(EINVAL
);
72 /* Check if this region conflicts with any existing lock. */
74 for (flp
= &file_lock
[0]; flp
< &file_lock
[NR_LOCKS
]; flp
++) {
75 if (flp
->lock_type
== 0) {
76 if (empty
== NULL
) empty
= flp
;
77 continue; /* 0 means unused slot */
79 if (flp
->lock_vnode
!= f
->filp_vno
) continue; /* different file */
80 if (last
< flp
->lock_first
) continue; /* new one is in front */
81 if (first
> flp
->lock_last
) continue; /* new one is afterwards */
82 if (ltype
== F_RDLCK
&& flp
->lock_type
== F_RDLCK
) continue;
83 if (ltype
!= F_UNLCK
&& flp
->lock_pid
== fp
->fp_pid
) continue;
85 /* There might be a conflict. Process it. */
87 if (req
== F_GETLK
) break;
89 /* If we are trying to set a lock, it just failed. */
90 if (ltype
== F_RDLCK
|| ltype
== F_WRLCK
) {
92 /* For F_SETLK, just report back failure. */
95 /* For F_SETLKW, suspend the process. */
96 suspend(FP_BLOCKED_ON_LOCK
);
101 /* We are clearing a lock and we found something that overlaps. */
103 if (first
<= flp
->lock_first
&& last
>= flp
->lock_last
) {
104 flp
->lock_type
= 0; /* mark slot as unused */
105 nr_locks
--; /* number of locks is now 1 less */
109 /* Part of a locked region has been unlocked. */
110 if (first
<= flp
->lock_first
) {
111 flp
->lock_first
= last
+ 1;
115 if (last
>= flp
->lock_last
) {
116 flp
->lock_last
= first
- 1;
120 /* Bad luck. A lock has been split in two by unlocking the middle. */
121 if (nr_locks
== NR_LOCKS
) return(ENOLCK
);
122 for (i
= 0; i
< NR_LOCKS
; i
++)
123 if (file_lock
[i
].lock_type
== 0) break;
124 flp2
= &file_lock
[i
];
125 flp2
->lock_type
= flp
->lock_type
;
126 flp2
->lock_pid
= flp
->lock_pid
;
127 flp2
->lock_vnode
= flp
->lock_vnode
;
128 flp2
->lock_first
= last
+ 1;
129 flp2
->lock_last
= flp
->lock_last
;
130 flp
->lock_last
= first
- 1;
133 if (unlocking
) lock_revive();
135 if (req
== F_GETLK
) {
137 /* GETLK and conflict. Report on the conflicting lock. */
138 flock
.l_type
= flp
->lock_type
;
139 flock
.l_whence
= SEEK_SET
;
140 flock
.l_start
= flp
->lock_first
;
141 flock
.l_len
= flp
->lock_last
- flp
->lock_first
+ 1;
142 flock
.l_pid
= flp
->lock_pid
;
145 /* It is GETLK and there is no conflict. */
146 flock
.l_type
= F_UNLCK
;
149 /* Copy the flock structure back to the caller. */
150 r
= sys_datacopy(VFS_PROC_NR
, (vir_bytes
) &flock
,
151 who_e
, (vir_bytes
) scratch(fp
).io
.io_buffer
, sizeof(flock
));
155 if (ltype
== F_UNLCK
) return(OK
); /* unlocked a region with no locks */
157 /* There is no conflict. If space exists, store new lock in the table. */
158 if (empty
== NULL
) return(ENOLCK
); /* table full */
159 empty
->lock_type
= ltype
;
160 empty
->lock_pid
= fp
->fp_pid
;
161 empty
->lock_vnode
= f
->filp_vno
;
162 empty
->lock_first
= first
;
163 empty
->lock_last
= last
;
169 /*===========================================================================*
171 *===========================================================================*/
174 /* Go find all the processes that are waiting for any kind of lock and
175 * revive them all. The ones that are still blocked will block again when
176 * they run. The others will complete. This strategy is a space-time
177 * tradeoff. Figuring out exactly which ones to unblock now would take
178 * extra code, and the only thing it would win would be some performance in
179 * extremely rare circumstances (namely, that somebody actually used
185 for (fptr
= &fproc
[0]; fptr
< &fproc
[NR_PROCS
]; fptr
++){
186 if (fptr
->fp_pid
== PID_FREE
) continue;
187 if (fptr
->fp_blocked_on
== FP_BLOCKED_ON_LOCK
) {
188 revive(fptr
->fp_endpoint
, 0);