|
|
1.1 ! root 1: /* ! 2: * linux/fs/locks.c ! 3: * ! 4: * Provide support for fcntl()'s F_GETLK, F_SETLK, and F_SETLKW calls. ! 5: * Doug Evans, 92Aug07, [email protected]. ! 6: * ! 7: * FIXME: two things aren't handled yet: ! 8: * - deadlock detection/avoidance (of dubious merit, but since it's in ! 9: * the definition, I guess it should be provided eventually) ! 10: * - mandatory locks (requires lots of changes elsewhere) ! 11: */ ! 12: ! 13: #include <asm/segment.h> ! 14: ! 15: #include <linux/sched.h> ! 16: #include <linux/kernel.h> ! 17: #include <linux/errno.h> ! 18: #include <linux/stat.h> ! 19: #include <linux/fcntl.h> ! 20: ! 21: #define OFFSET_MAX 0x7fffffff /* FIXME: move elsewhere? */ ! 22: ! 23: static int copy_flock(struct file *filp, struct file_lock *fl, struct flock *l); ! 24: static int conflict(struct file_lock *caller_fl, struct file_lock *sys_fl); ! 25: static int overlap(struct file_lock *fl1, struct file_lock *fl2); ! 26: static int lock_it(struct file *filp, struct file_lock *caller); ! 27: static int unlock_it(struct file *filp, struct file_lock *caller); ! 28: static struct file_lock *alloc_lock(struct file *filp, struct file_lock *template); ! 29: static void free_lock(struct file *filp, struct file_lock *fl); ! 30: ! 31: static struct file_lock file_lock_table[NR_FILE_LOCKS]; ! 32: static struct file_lock *file_lock_free_list; ! 33: ! 34: /* ! 35: * Called at boot time to initialize the lock table ... ! 36: */ ! 37: ! 38: void fcntl_init_locks(void) ! 39: { ! 40: struct file_lock *fl; ! 41: ! 42: for (fl = &file_lock_table[0]; fl < file_lock_table + NR_FILE_LOCKS - 1; fl++) { ! 43: fl->fl_next = fl + 1; ! 44: fl->fl_owner = NULL; ! 45: } ! 46: file_lock_table[NR_FILE_LOCKS - 1].fl_next = NULL; ! 47: file_lock_table[NR_FILE_LOCKS - 1].fl_owner = NULL; ! 48: file_lock_free_list = &file_lock_table[0]; ! 49: } ! 50: ! 51: int fcntl_getlk(unsigned int fd, struct flock *l) ! 52: { ! 53: struct flock flock; ! 54: struct file *filp; ! 55: struct file_lock *fl,file_lock; ! 56: ! 57: if (fd >= NR_OPEN || !(filp = current->filp[fd])) ! 58: return -EBADF; ! 59: verify_area(l, sizeof(*l)); ! 60: memcpy_fromfs(&flock, l, sizeof(flock)); ! 61: if (flock.l_type == F_UNLCK) ! 62: return -EINVAL; ! 63: if (!copy_flock(filp, &file_lock, &flock)) ! 64: return -EINVAL; ! 65: ! 66: for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) { ! 67: if (conflict(&file_lock, fl)) { ! 68: flock.l_pid = fl->fl_owner->pid; ! 69: flock.l_start = fl->fl_start; ! 70: flock.l_len = fl->fl_end == OFFSET_MAX ? 0 : ! 71: fl->fl_end - fl->fl_start + 1; ! 72: flock.l_whence = fl->fl_whence; ! 73: flock.l_type = fl->fl_type; ! 74: memcpy_tofs(l, &flock, sizeof(flock)); ! 75: return 0; ! 76: } ! 77: } ! 78: ! 79: flock.l_type = F_UNLCK; /* no conflict found */ ! 80: memcpy_tofs(l, &flock, sizeof(flock)); ! 81: return 0; ! 82: } ! 83: ! 84: /* ! 85: * This function implements both F_SETLK and F_SETLKW. ! 86: */ ! 87: ! 88: int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l) ! 89: { ! 90: struct file *filp; ! 91: struct file_lock *fl,file_lock; ! 92: struct flock flock; ! 93: ! 94: /* ! 95: * Get arguments and validate them ... ! 96: */ ! 97: ! 98: if (fd >= NR_OPEN || !(filp = current->filp[fd])) ! 99: return -EBADF; ! 100: verify_area(l, sizeof(*l)); ! 101: memcpy_fromfs(&flock, l, sizeof(flock)); ! 102: if (!copy_flock(filp, &file_lock, &flock)) ! 103: return -EINVAL; ! 104: switch (file_lock.fl_type) { ! 105: case F_RDLCK : ! 106: if (!(filp->f_mode & 1)) ! 107: return -EBADF; ! 108: break; ! 109: case F_WRLCK : ! 110: if (!(filp->f_mode & 2)) ! 111: return -EBADF; ! 112: break; ! 113: case F_UNLCK : ! 114: break; ! 115: } ! 116: ! 117: /* ! 118: * F_UNLCK needs to be handled differently ... ! 119: */ ! 120: ! 121: if (file_lock.fl_type == F_UNLCK) ! 122: return unlock_it(filp, &file_lock); ! 123: ! 124: /* ! 125: * Scan for a conflicting lock ... ! 126: */ ! 127: ! 128: repeat: ! 129: for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) { ! 130: if (!conflict(&file_lock, fl)) ! 131: continue; ! 132: /* ! 133: * File is locked by another process. If this is F_SETLKW ! 134: * wait for the lock to be released. ! 135: * FIXME: We need to check for deadlocks here. ! 136: */ ! 137: if (cmd == F_SETLKW) { ! 138: interruptible_sleep_on(&fl->fl_wait); ! 139: goto repeat; ! 140: } ! 141: return -EAGAIN; ! 142: } ! 143: ! 144: /* ! 145: * Lock doesn't conflict with any other lock ... ! 146: */ ! 147: ! 148: return lock_it(filp, &file_lock); ! 149: } ! 150: ! 151: /* ! 152: * This function is called when the file is closed. ! 153: */ ! 154: ! 155: void fcntl_remove_locks(struct task_struct *task, struct file *filp) ! 156: { ! 157: struct file_lock *fl,*next; ! 158: ! 159: for (fl = filp->f_inode->i_flock; fl != NULL; ) { ! 160: /* ! 161: * If this one is freed, {fl_next} gets clobbered when the ! 162: * entry is moved to the free list, so grab it now ... ! 163: */ ! 164: next = fl->fl_next; ! 165: if (fl->fl_owner == task) ! 166: free_lock(filp, fl); ! 167: fl = next; ! 168: } ! 169: } ! 170: ! 171: /* ! 172: * Verify a "struct flock" and copy it to a "struct file_lock" ... ! 173: * Result is a boolean indicating success. ! 174: */ ! 175: ! 176: static int copy_flock(struct file *filp, struct file_lock *fl, struct flock *l) ! 177: { ! 178: off_t start; ! 179: ! 180: if (!filp->f_inode) /* just in case */ ! 181: return 0; ! 182: if (!S_ISREG(filp->f_inode->i_mode)) ! 183: return 0; ! 184: if (l->l_type != F_UNLCK && l->l_type != F_RDLCK && l->l_type != F_WRLCK) ! 185: return 0; ! 186: switch (l->l_whence) { ! 187: case 0 /*SEEK_SET*/ : start = 0; break; ! 188: case 1 /*SEEK_CUR*/ : start = filp->f_pos; break; ! 189: case 2 /*SEEK_END*/ : start = filp->f_inode->i_size; break; ! 190: default : return 0; ! 191: } ! 192: if ((start += l->l_start) < 0 || l->l_len < 0) ! 193: return 0; ! 194: fl->fl_type = l->l_type; ! 195: fl->fl_start = start; /* we record the absolute position */ ! 196: fl->fl_whence = 0; /* FIXME: do we record {l_start} as passed? */ ! 197: if (l->l_len == 0 || (fl->fl_end = start + l->l_len - 1) < 0) ! 198: fl->fl_end = OFFSET_MAX; ! 199: fl->fl_owner = current; ! 200: fl->fl_wait = NULL; /* just for cleanliness */ ! 201: return 1; ! 202: } ! 203: ! 204: /* ! 205: * Determine if lock {sys_fl} blocks lock {caller_fl} ... ! 206: */ ! 207: ! 208: static int conflict(struct file_lock *caller_fl, struct file_lock *sys_fl) ! 209: { ! 210: if (caller_fl->fl_owner == sys_fl->fl_owner) ! 211: return 0; ! 212: if (!overlap(caller_fl, sys_fl)) ! 213: return 0; ! 214: switch (caller_fl->fl_type) { ! 215: case F_RDLCK : ! 216: return sys_fl->fl_type != F_RDLCK; ! 217: case F_WRLCK : ! 218: return 1; /* overlapping region not owned by caller */ ! 219: } ! 220: return 0; /* shouldn't get here, but just in case */ ! 221: } ! 222: ! 223: static int overlap(struct file_lock *fl1, struct file_lock *fl2) ! 224: { ! 225: if (fl1->fl_start <= fl2->fl_start) { ! 226: return fl1->fl_end >= fl2->fl_start; ! 227: } else { ! 228: return fl2->fl_end >= fl1->fl_start; ! 229: } ! 230: } ! 231: ! 232: /* ! 233: * Add a lock to a file ... ! 234: * Result is 0 for success or -ENOLCK. ! 235: * ! 236: * We try to be real clever here and always minimize the number of table ! 237: * entries we use. For example we merge adjacent locks whenever possible. This ! 238: * consumes a bit of cpu and code space, is it really worth it? Beats me. ! 239: * ! 240: * I've tried to keep the following as small and simple as possible. If you can ! 241: * make it smaller or simpler, please do. /dje 92Aug11 ! 242: * ! 243: * WARNING: We assume the lock doesn't conflict with any other lock. ! 244: */ ! 245: ! 246: static int lock_it(struct file *filp, struct file_lock *caller) ! 247: { ! 248: struct file_lock *fl,*new; ! 249: ! 250: /* ! 251: * It's easier if we allocate a slot for the lock first, and then ! 252: * release it later if we have to (IE: if it can be merged with ! 253: * another). This way the for() loop always knows that {caller} is an ! 254: * existing entry. This will cause the routine to fail unnecessarily ! 255: * in rare cases, but perfection can be pushed too far. :-) ! 256: */ ! 257: ! 258: if ((caller = alloc_lock(filp, caller)) == NULL) ! 259: return -ENOLCK; ! 260: ! 261: /* ! 262: * First scan to see if we are changing/augmenting an existing lock ... ! 263: */ ! 264: ! 265: for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) { ! 266: if (caller->fl_owner != fl->fl_owner) ! 267: continue; ! 268: if (caller == fl) ! 269: continue; ! 270: if (!overlap(caller, fl)) { ! 271: /* ! 272: * Detect adjacent regions (if same lock type) ... ! 273: */ ! 274: if (caller->fl_type != fl->fl_type) ! 275: continue; ! 276: if (caller->fl_end + 1 == fl->fl_start) { ! 277: fl->fl_start = caller->fl_start; ! 278: free_lock(filp, caller); ! 279: caller = fl; ! 280: /* must continue, may overlap others now */ ! 281: } else if (caller->fl_start - 1 == fl->fl_end) { ! 282: fl->fl_end = caller->fl_end; ! 283: free_lock(filp, caller); ! 284: caller = fl; ! 285: /* must continue, may overlap others now */ ! 286: } ! 287: continue; ! 288: } ! 289: /* ! 290: * We've found an overlapping region. Is it a change of lock ! 291: * type, or are we changing the size of the locked space? ! 292: */ ! 293: if (caller->fl_type != fl->fl_type) { ! 294: if (caller->fl_start > fl->fl_start && caller->fl_end < fl->fl_end) { ! 295: /* ! 296: * The new lock splits the old one in two ... ! 297: * {fl} is the bottom piece, {caller} is the ! 298: * new lock, and {new} is the top piece. ! 299: */ ! 300: if ((new = alloc_lock(filp, fl)) == NULL) { ! 301: free_lock(filp, caller); ! 302: return -ENOLCK; ! 303: } ! 304: fl->fl_end = caller->fl_start - 1; ! 305: new->fl_start = caller->fl_end + 1; ! 306: return 0; ! 307: } ! 308: if (caller->fl_start <= fl->fl_start && caller->fl_end >= fl->fl_end) { ! 309: /* ! 310: * The new lock completely replaces old one ... ! 311: */ ! 312: free_lock(filp, fl); ! 313: return 0; ! 314: } ! 315: if (caller->fl_end < fl->fl_end) { ! 316: fl->fl_start = caller->fl_end + 1; ! 317: /* must continue, may be more overlaps */ ! 318: } else if (caller->fl_start > fl->fl_start) { ! 319: fl->fl_end = caller->fl_start - 1; ! 320: /* must continue, may be more overlaps */ ! 321: } else { ! 322: printk("lock_it: program bug: unanticipated overlap\n"); ! 323: free_lock(filp, caller); ! 324: return -ENOLCK; ! 325: } ! 326: } else { /* The new lock augments an existing lock ... */ ! 327: int grew = 0; ! 328: ! 329: if (caller->fl_start < fl->fl_start) { ! 330: fl->fl_start = caller->fl_start; ! 331: grew = 1; ! 332: } ! 333: if (caller->fl_end > fl->fl_end) { ! 334: fl->fl_end = caller->fl_end; ! 335: grew = 1; ! 336: } ! 337: free_lock(filp, caller); ! 338: caller = fl; ! 339: if (!grew) ! 340: return 0; ! 341: /* must continue, may be more overlaps */ ! 342: } ! 343: } ! 344: ! 345: /* ! 346: * New lock doesn't overlap any regions ... ! 347: * alloc_lock() has already been called, so we're done! ! 348: */ ! 349: ! 350: return 0; ! 351: } ! 352: ! 353: /* ! 354: * Handle F_UNLCK ... ! 355: * Result is 0 for success, or -EINVAL or -ENOLCK. ! 356: * ENOLCK can happen when a lock is split into two. ! 357: */ ! 358: ! 359: static int unlock_it(struct file *filp, struct file_lock *caller) ! 360: { ! 361: int one_unlocked = 0; ! 362: struct file_lock *fl,*next; ! 363: ! 364: for (fl = filp->f_inode->i_flock; fl != NULL; ) { ! 365: if (caller->fl_owner != fl->fl_owner || !overlap(caller, fl)) { ! 366: fl = fl->fl_next; ! 367: continue; ! 368: } ! 369: one_unlocked = 1; ! 370: if (caller->fl_start > fl->fl_start && caller->fl_end < fl->fl_end) { ! 371: /* ! 372: * Lock is split in two ... ! 373: * {fl} is the bottom piece, {next} is the top piece. ! 374: */ ! 375: if ((next = alloc_lock(filp, fl)) == NULL) ! 376: return -ENOLCK; ! 377: fl->fl_end = caller->fl_start - 1; ! 378: next->fl_start = caller->fl_end + 1; ! 379: return 0; ! 380: } ! 381: /* ! 382: * At this point we know there is an overlap and we know the ! 383: * lock isn't split into two ... ! 384: * ! 385: * Unless the lock table is broken, entries will not overlap. ! 386: * IE: User X won't have an entry locking bytes 1-3 and another ! 387: * entry locking bytes 3-5. Therefore, if the area being ! 388: * unlocked is a subset of the total area, we don't need to ! 389: * traverse any more of the list. The code is a tad more ! 390: * complicated by this optimization. Perhaps it's not worth it. ! 391: * ! 392: * WARNING: We assume free_lock() does not alter ! 393: * {fl_start, fl_end}. ! 394: * ! 395: * {fl_next} gets clobbered when the entry is moved to ! 396: * the free list, so grab it now ... ! 397: */ ! 398: next = fl->fl_next; ! 399: if (caller->fl_start <= fl->fl_start && caller->fl_end >= fl->fl_end) { ! 400: free_lock(filp, fl); ! 401: } else if (caller->fl_start > fl->fl_start) { ! 402: fl->fl_end = caller->fl_start - 1; ! 403: } else { ! 404: /* caller->fl_end < fl->fl_end */ ! 405: fl->fl_start = caller->fl_end + 1; ! 406: } ! 407: if (caller->fl_start >= fl->fl_start && caller->fl_end <= fl->fl_end) ! 408: return 0; /* no more to be found */ ! 409: fl = next; ! 410: /* must continue, there may be more to unlock */ ! 411: } ! 412: ! 413: return one_unlocked ? 0 : -EINVAL; ! 414: } ! 415: ! 416: static struct file_lock *alloc_lock(struct file *filp, struct file_lock *template) ! 417: { ! 418: struct file_lock *new; ! 419: ! 420: if (file_lock_free_list == NULL) ! 421: return NULL; /* no available entry */ ! 422: if (file_lock_free_list->fl_owner != NULL) ! 423: panic("alloc_lock: broken free list\n"); ! 424: ! 425: new = file_lock_free_list; /* remove from free list */ ! 426: file_lock_free_list = file_lock_free_list->fl_next; ! 427: ! 428: *new = *template; ! 429: ! 430: new->fl_next = filp->f_inode->i_flock; /* insert into file's list */ ! 431: filp->f_inode->i_flock = new; ! 432: ! 433: new->fl_owner = current; /* FIXME: needed? */ ! 434: new->fl_wait = NULL; ! 435: return new; ! 436: } ! 437: ! 438: /* ! 439: * Add a lock to the free list ... ! 440: * ! 441: * WARNING: We must not alter {fl_start, fl_end}. See unlock_it(). ! 442: */ ! 443: ! 444: static void free_lock(struct file *filp, struct file_lock *fl) ! 445: { ! 446: struct file_lock **fl_p; ! 447: ! 448: if (fl->fl_owner == NULL) /* sanity check */ ! 449: panic("free_lock: broken lock list\n"); ! 450: ! 451: /* ! 452: * We only use a singly linked list to save some memory space ! 453: * (the only place we'd use a doubly linked list is here). ! 454: */ ! 455: ! 456: for (fl_p = &filp->f_inode->i_flock; *fl_p != NULL; fl_p = &(*fl_p)->fl_next) { ! 457: if (*fl_p == fl) ! 458: break; ! 459: } ! 460: if (*fl_p == NULL) { ! 461: printk("free_lock: lock is not in file's lock list\n"); ! 462: } else { ! 463: *fl_p = (*fl_p)->fl_next; ! 464: } ! 465: ! 466: fl->fl_next = file_lock_free_list; /* add to free list */ ! 467: file_lock_free_list = fl; ! 468: fl->fl_owner = NULL; /* for sanity checks */ ! 469: ! 470: wake_up(&fl->fl_wait); ! 471: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.