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>
20 /*===========================================================================*
22 *===========================================================================*/
23 PUBLIC
int lock_op(f
, req
)
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;
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. */
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;
55 if (ex64hi(f
->filp_pos
) != 0)
57 panic(__FILE__
, "lock_op: position in file too high",
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
))
67 if (((long)flock
.l_start
< 0) && ((first
+ flock
.l_start
) > first
))
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. */
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
) {
94 /* For F_SETLK, just report back failure. */
97 /* For F_SETLKW, suspend the process. */
103 /* We are clearing a lock and we found something that overlaps. */
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 */
111 /* Part of a locked region has been unlocked. */
112 if (first
<= flp
->lock_first
) {
113 flp
->lock_first
= last
+ 1;
117 if (last
>= flp
->lock_last
) {
118 flp
->lock_last
= first
- 1;
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;
135 if (unlocking
) lock_revive();
137 if (req
== F_GETLK
) {
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
;
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
));
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
;
170 /*===========================================================================*
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
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);