📄 lock.c
字号:
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/fs/lock.c
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
22300 /* This file handles advisory file locking as required by POSIX.
22301 *
22302 * The entry points into this file are
22303 * lock_op: perform locking operations for FCNTL system call
22304 * lock_revive: revive processes when a lock is released
22305 */
22306
22307 #include "fs.h"
22308 #include <fcntl.h>
22309 #include <unistd.h> /* cc runs out of memory with unistd.h :-( */
22310 #include "file.h"
22311 #include "fproc.h"
22312 #include "inode.h"
22313 #include "lock.h"
22314 #include "param.h"
22315
22316 /*===========================================================================*
22317 * lock_op *
22318 *===========================================================================*/
22319 PUBLIC int lock_op(f, req)
22320 struct filp *f;
22321 int req; /* either F_SETLK or F_SETLKW */
22322 {
22323 /* Perform the advisory locking required by POSIX. */
22324
22325 int r, ltype, i, conflict = 0, unlocking = 0;
22326 mode_t mo;
22327 off_t first, last;
22328 struct flock flock;
22329 vir_bytes user_flock;
22330 struct file_lock *flp, *flp2, *empty;
22331
22332 /* Fetch the flock structure from user space. */
22333 user_flock = (vir_bytes) name1;
22334 r = sys_copy(who, D, (phys_bytes) user_flock,
22335 FS_PROC_NR, D, (phys_bytes) &flock, (phys_bytes) sizeof(flock));
22336 if (r != OK) return(EINVAL);
22337
22338 /* Make some error checks. */
22339 ltype = flock.l_type;
22340 mo = f->filp_mode;
22341 if (ltype != F_UNLCK && ltype != F_RDLCK && ltype != F_WRLCK) return(EINVAL);
22342 if (req == F_GETLK && ltype == F_UNLCK) return(EINVAL);
22343 if ( (f->filp_ino->i_mode & I_TYPE) != I_REGULAR) return(EINVAL);
22344 if (req != F_GETLK && ltype == F_RDLCK && (mo & R_BIT) == 0) return(EBADF);
22345 if (req != F_GETLK && ltype == F_WRLCK && (mo & W_BIT) == 0) return(EBADF);
22346
22347 /* Compute the first and last bytes in the lock region. */
22348 switch (flock.l_whence) {
22349 case SEEK_SET: first = 0; break;
22350 case SEEK_CUR: first = f->filp_pos; break;
22351 case SEEK_END: first = f->filp_ino->i_size; break;
22352 default: return(EINVAL);
22353 }
22354 /* Check for overflow. */
22355 if (((long)flock.l_start > 0) && ((first + flock.l_start) < first))
22356 return(EINVAL);
22357 if (((long)flock.l_start < 0) && ((first + flock.l_start) > first))
22358 return(EINVAL);
22359 first = first + flock.l_start;
22360 last = first + flock.l_len - 1;
22361 if (flock.l_len == 0) last = MAX_FILE_POS;
22362 if (last < first) return(EINVAL);
22363
22364 /* Check if this region conflicts with any existing lock. */
22365 empty = (struct file_lock *) 0;
22366 for (flp = &file_lock[0]; flp < & file_lock[NR_LOCKS]; flp++) {
22367 if (flp->lock_type == 0) {
22368 if (empty == (struct file_lock *) 0) empty = flp;
22369 continue; /* 0 means unused slot */
22370 }
22371 if (flp->lock_inode != f->filp_ino) continue; /* different file */
22372 if (last < flp->lock_first) continue; /* new one is in front */
22373 if (first > flp->lock_last) continue; /* new one is afterwards */
22374 if (ltype == F_RDLCK && flp->lock_type == F_RDLCK) continue;
22375 if (ltype != F_UNLCK && flp->lock_pid == fp->fp_pid) continue;
22376
22377 /* There might be a conflict. Process it. */
22378 conflict = 1;
22379 if (req == F_GETLK) break;
22380
22381 /* If we are trying to set a lock, it just failed. */
22382 if (ltype == F_RDLCK || ltype == F_WRLCK) {
22383 if (req == F_SETLK) {
22384 /* For F_SETLK, just report back failure. */
22385 return(EAGAIN);
22386 } else {
22387 /* For F_SETLKW, suspend the process. */
22388 suspend(XLOCK);
22389 return(0);
22390 }
22391 }
22392
22393 /* We are clearing a lock and we found something that overlaps. */
22394 unlocking = 1;
22395 if (first <= flp->lock_first && last >= flp->lock_last) {
22396 flp->lock_type = 0; /* mark slot as unused */
22397 nr_locks--; /* number of locks is now 1 less */
22398 continue;
22399 }
22400
22401 /* Part of a locked region has been unlocked. */
22402 if (first <= flp->lock_first) {
22403 flp->lock_first = last + 1;
22404 continue;
22405 }
22406
22407 if (last >= flp->lock_last) {
22408 flp->lock_last = first - 1;
22409 continue;
22410 }
22411
22412 /* Bad luck. A lock has been split in two by unlocking the middle. */
22413 if (nr_locks == NR_LOCKS) return(ENOLCK);
22414 for (i = 0; i < NR_LOCKS; i++)
22415 if (file_lock[i].lock_type == 0) break;
22416 flp2 = &file_lock[i];
22417 flp2->lock_type = flp->lock_type;
22418 flp2->lock_pid = flp->lock_pid;
22419 flp2->lock_inode = flp->lock_inode;
22420 flp2->lock_first = last + 1;
22421 flp2->lock_last = flp->lock_last;
22422 flp->lock_last = first - 1;
22423 nr_locks++;
22424 }
22425 if (unlocking) lock_revive();
22426
22427 if (req == F_GETLK) {
22428 if (conflict) {
22429 /* GETLK and conflict. Report on the conflicting lock. */
22430 flock.l_type = flp->lock_type;
22431 flock.l_whence = SEEK_SET;
22432 flock.l_start = flp->lock_first;
22433 flock.l_len = flp->lock_last - flp->lock_first + 1;
22434 flock.l_pid = flp->lock_pid;
22435
22436 } else {
22437 /* It is GETLK and there is no conflict. */
22438 flock.l_type = F_UNLCK;
22439 }
22440
22441 /* Copy the flock structure back to the caller. */
22442 r = sys_copy(FS_PROC_NR, D, (phys_bytes) &flock,
22443 who, D, (phys_bytes) user_flock, (phys_bytes) sizeof(flock));
22444 return(r);
22445 }
22446
22447 if (ltype == F_UNLCK) return(OK); /* unlocked a region with no locks */
22448
22449 /* There is no conflict. If space exists, store new lock in the table. */
22450 if (empty == (struct file_lock *) 0) return(ENOLCK); /* table full */
22451 empty->lock_type = ltype;
22452 empty->lock_pid = fp->fp_pid;
22453 empty->lock_inode = f->filp_ino;
22454 empty->lock_first = first;
22455 empty->lock_last = last;
22456 nr_locks++;
22457 return(OK);
22458 }
22460 /*===========================================================================*
22461 * lock_revive *
22462 *===========================================================================*/
22463 PUBLIC void lock_revive()
22464 {
22465 /* Go find all the processes that are waiting for any kind of lock and
22466 * revive them all. The ones that are still blocked will block again when
22467 * they run. The others will complete. This strategy is a space-time
22468 * tradeoff. Figuring out exactly which ones to unblock now would take
22469 * extra code, and the only thing it would win would be some performance in
22470 * extremely rare circumstances (namely, that somebody actually used
22471 * locking).
22472 */
22473
22474 int task;
22475 struct fproc *fptr;
22476
22477 for (fptr = &fproc[INIT_PROC_NR + 1]; fptr < &fproc[NR_PROCS]; fptr++){
22478 task = -fptr->fp_task;
22479 if (fptr->fp_suspended == SUSPENDED && task == XLOCK) {
22480 revive( (int) (fptr - fproc), 0);
22481 }
22482 }
22483 }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -