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>
18 /*===========================================================================*
20 *===========================================================================*/
21 int lock_op(int fd
, int req
, vir_bytes arg
)
23 /* Perform the advisory locking required by POSIX. */
24 int r
, ltype
, i
, conflict
= 0, unlocking
= 0;
29 struct file_lock
*flp
, *flp2
, *empty
;
31 assert(req
== F_GETLK
|| req
== F_SETLK
|| req
== F_SETLKW
);
36 /* Fetch the flock structure from user space. */
37 r
= sys_datacopy_wrapper(who_e
, arg
, VFS_PROC_NR
, (vir_bytes
)&flock
,
39 if (r
!= OK
) return(EINVAL
);
41 /* Make some error checks. */
44 if (ltype
!= F_UNLCK
&& ltype
!= F_RDLCK
&& ltype
!= F_WRLCK
) return(EINVAL
);
45 if (req
== F_GETLK
&& ltype
== F_UNLCK
) return(EINVAL
);
46 if (!S_ISREG(f
->filp_vno
->v_mode
) && !S_ISBLK(f
->filp_vno
->v_mode
))
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
: first
= f
->filp_pos
; break;
55 case SEEK_END
: first
= f
->filp_vno
->v_size
; break;
56 default: return(EINVAL
);
59 /* Check for overflow. */
60 if (((long) flock
.l_start
> 0) && ((first
+ flock
.l_start
) < first
))
62 if (((long) flock
.l_start
< 0) && ((first
+ flock
.l_start
) > first
))
64 first
= first
+ flock
.l_start
;
65 last
= first
+ flock
.l_len
- 1;
66 if (flock
.l_len
== 0) last
= MAX_FILE_POS
;
67 if (last
< first
) return(EINVAL
);
69 /* Check if this region conflicts with any existing lock. */
71 for (flp
= &file_lock
[0]; flp
< &file_lock
[NR_LOCKS
]; flp
++) {
72 if (flp
->lock_type
== 0) {
73 if (empty
== NULL
) empty
= flp
;
74 continue; /* 0 means unused slot */
76 if (flp
->lock_vnode
!= f
->filp_vno
) continue; /* different file */
77 if (last
< flp
->lock_first
) continue; /* new one is in front */
78 if (first
> flp
->lock_last
) continue; /* new one is afterwards */
79 if (ltype
== F_RDLCK
&& flp
->lock_type
== F_RDLCK
) continue;
80 if (ltype
!= F_UNLCK
&& flp
->lock_pid
== fp
->fp_pid
) continue;
82 /* There might be a conflict. Process it. */
84 if (req
== F_GETLK
) break;
86 /* If we are trying to set a lock, it just failed. */
87 if (ltype
== F_RDLCK
|| ltype
== F_WRLCK
) {
89 /* For F_SETLK, just report back failure. */
92 /* For F_SETLKW, suspend the process. */
94 fp
->fp_flock
.cmd
= req
;
95 fp
->fp_flock
.arg
= arg
;
96 suspend(FP_BLOCKED_ON_FLOCK
);
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_wrapper(VFS_PROC_NR
, (vir_bytes
)&flock
, who_e
, arg
,
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 *===========================================================================*/
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
186 for (fptr
= &fproc
[0]; fptr
< &fproc
[NR_PROCS
]; fptr
++){
187 if (fptr
->fp_pid
== PID_FREE
) continue;
188 if (fptr
->fp_blocked_on
== FP_BLOCKED_ON_FLOCK
) {
189 revive(fptr
->fp_endpoint
, 0);