home *** CD-ROM | disk | FTP | other *** search
/ Whiteline: Alpha / Whiteline Alpha.iso / linux / atari / source / source.lzh / atari-linux-0.01pl3 / fs / locks.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-05  |  11.3 KB  |  459 lines

  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, dje@sspiff.uucp.
  6.  *
  7.  * This file is subject to the terms and conditions of the GNU General Public
  8.  * License.  See the file README.legal in the main directory of this archive
  9.  * for more details.
  10.  *
  11.  * FIXME: two things aren't handled yet:
  12.  *    - deadlock detection/avoidance (of dubious merit, but since it's in
  13.  *      the definition, I guess it should be provided eventually)
  14.  *    - mandatory locks (requires lots of changes elsewhere)
  15.  *
  16.  *  Edited by Kai Petzke, wpp@marie.physik.tu-berlin.de
  17.  */
  18.  
  19. #include <asm/segment.h>
  20.  
  21. #include <linux/sched.h>
  22. #include <linux/kernel.h>
  23. #include <linux/errno.h>
  24. #include <linux/stat.h>
  25. #include <linux/fcntl.h>
  26.  
  27. #define OFFSET_MAX    ((off_t)0x7fffffff)    /* FIXME: move elsewhere? */
  28.  
  29. static int copy_flock(struct file *filp, struct file_lock *fl, struct flock *l,
  30.                       unsigned int fd);
  31. static int conflict(struct file_lock *caller_fl, struct file_lock *sys_fl);
  32. static int overlap(struct file_lock *fl1, struct file_lock *fl2);
  33. static int lock_it(struct file *filp, struct file_lock *caller, unsigned int fd);
  34. static struct file_lock *alloc_lock(struct file_lock **pos, struct file_lock *fl,
  35.                                     unsigned int fd);
  36. static void free_lock(struct file_lock **fl);
  37.  
  38. static struct file_lock file_lock_table[NR_FILE_LOCKS];
  39. static struct file_lock *file_lock_free_list;
  40.  
  41. /*
  42.  * Called at boot time to initialize the lock table ...
  43.  */
  44.  
  45. void fcntl_init_locks(void)
  46. {
  47.     struct file_lock *fl;
  48.  
  49.     for (fl = &file_lock_table[0]; fl < file_lock_table + NR_FILE_LOCKS - 1; fl++) {
  50.         fl->fl_next = fl + 1;
  51.         fl->fl_owner = NULL;
  52.     }
  53.     file_lock_table[NR_FILE_LOCKS - 1].fl_next = NULL;
  54.     file_lock_table[NR_FILE_LOCKS - 1].fl_owner = NULL;
  55.     file_lock_free_list = &file_lock_table[0];
  56. }
  57.  
  58. int fcntl_getlk(unsigned int fd, struct flock *l)
  59. {
  60.     int error;
  61.     struct flock flock;
  62.     struct file *filp;
  63.     struct file_lock *fl,file_lock;
  64.  
  65.     if (fd >= NR_OPEN || !(filp = current->filp[fd]))
  66.         return -EBADF;
  67.     error = verify_area(VERIFY_WRITE,l, sizeof(*l));
  68.     if (error)
  69.         return error;
  70.     memcpy_fromfs(&flock, l, sizeof(flock));
  71.     if (flock.l_type == F_UNLCK)
  72.         return -EINVAL;
  73.     if (!copy_flock(filp, &file_lock, &flock, fd))
  74.         return -EINVAL;
  75.  
  76.     for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
  77.         if (conflict(&file_lock, fl)) {
  78.             flock.l_pid = fl->fl_owner->pid;
  79.             flock.l_start = fl->fl_start;
  80.             flock.l_len = fl->fl_end == OFFSET_MAX ? 0 :
  81.                 fl->fl_end - fl->fl_start + 1;
  82.             flock.l_whence = fl->fl_whence;
  83.             flock.l_type = fl->fl_type;
  84.             memcpy_tofs(l, &flock, sizeof(flock));
  85.             return 0;
  86.         }
  87.     }
  88.  
  89.     flock.l_type = F_UNLCK;            /* no conflict found */
  90.     memcpy_tofs(l, &flock, sizeof(flock));
  91.     return 0;
  92. }
  93.  
  94. /*
  95.  * This function implements both F_SETLK and F_SETLKW.
  96.  */
  97.  
  98. int fcntl_setlk(unsigned int fd, unsigned int cmd, struct flock *l)
  99. {
  100.     int error;
  101.     struct file *filp;
  102.     struct file_lock *fl,file_lock;
  103.     struct flock flock;
  104.  
  105.     /*
  106.      * Get arguments and validate them ...
  107.      */
  108.  
  109.     if (fd >= NR_OPEN || !(filp = current->filp[fd]))
  110.         return -EBADF;
  111.     error = verify_area(VERIFY_WRITE, l, sizeof(*l));
  112.     if (error)
  113.         return error;
  114.     memcpy_fromfs(&flock, l, sizeof(flock));
  115.     if (!copy_flock(filp, &file_lock, &flock, fd))
  116.         return -EINVAL;
  117.     switch (file_lock.fl_type) {
  118.     case F_RDLCK :
  119.         if (!(filp->f_mode & 1))
  120.             return -EBADF;
  121.         break;
  122.     case F_WRLCK :
  123.         if (!(filp->f_mode & 2))
  124.             return -EBADF;
  125.         break;
  126.     case F_SHLCK :
  127.         if (!(filp->f_mode & 3))
  128.             return -EBADF;
  129.         file_lock.fl_type = F_RDLCK;
  130.         break;
  131.     case F_EXLCK :
  132.         if (!(filp->f_mode & 3))
  133.             return -EBADF;
  134.         file_lock.fl_type = F_WRLCK;
  135.         break;
  136.     case F_UNLCK :
  137.         break;
  138.     }
  139.  
  140.       /*
  141.        * Scan for a conflicting lock ...
  142.        */
  143.   
  144.     if (file_lock.fl_type != F_UNLCK) {
  145. repeat:
  146.         for (fl = filp->f_inode->i_flock; fl != NULL; fl = fl->fl_next) {
  147.             if (!conflict(&file_lock, fl))
  148.                 continue;
  149.             /*
  150.              * File is locked by another process. If this is
  151.              * F_SETLKW wait for the lock to be released.
  152.              * FIXME: We need to check for deadlocks here.
  153.              */
  154.             if (cmd == F_SETLKW) {
  155.                 if (current->signal & ~current->blocked)
  156.                     return -ERESTARTSYS;
  157.                 interruptible_sleep_on(&fl->fl_wait);
  158.                 if (current->signal & ~current->blocked)
  159.                     return -ERESTARTSYS;
  160.                 goto repeat;
  161.             }
  162.             return -EAGAIN;
  163.           }
  164.       }
  165.  
  166.     /*
  167.      * Lock doesn't conflict with any other lock ...
  168.      */
  169.  
  170.     return lock_it(filp, &file_lock, fd);
  171. }
  172.  
  173. /*
  174.  * This function is called when the file is closed.
  175.  */
  176.  
  177. void fcntl_remove_locks(struct task_struct *task, struct file *filp,
  178.                         unsigned int fd)
  179. {
  180.     struct file_lock *fl;
  181.     struct file_lock **before;
  182.  
  183.     /* Find first lock owned by caller ... */
  184.  
  185.     before = &filp->f_inode->i_flock;
  186.     while ((fl = *before) && (task != fl->fl_owner || fd != fl->fl_fd))
  187.         before = &fl->fl_next;
  188.  
  189.     /* The list is sorted by owner and fd ... */
  190.  
  191.     while ((fl = *before) && task == fl->fl_owner && fd == fl->fl_fd)
  192.         free_lock(before);
  193. }
  194.  
  195. /*
  196.  * Verify a "struct flock" and copy it to a "struct file_lock" ...
  197.  * Result is a boolean indicating success.
  198.  */
  199.  
  200. static int copy_flock(struct file *filp, struct file_lock *fl, struct flock *l,
  201.                       unsigned int fd)
  202. {
  203.     off_t start;
  204.  
  205.     if (!filp->f_inode)    /* just in case */
  206.         return 0;
  207.     if (!S_ISREG(filp->f_inode->i_mode))
  208.         return 0;
  209.     if (l->l_type != F_UNLCK && l->l_type != F_RDLCK && l->l_type != F_WRLCK
  210.      && l->l_type != F_SHLCK && l->l_type != F_EXLCK)
  211.         return 0;
  212.     switch (l->l_whence) {
  213.     case 0 /*SEEK_SET*/ : start = 0; break;
  214.     case 1 /*SEEK_CUR*/ : start = filp->f_pos; break;
  215.     case 2 /*SEEK_END*/ : start = filp->f_inode->i_size; break;
  216.     default : return 0;
  217.     }
  218.     if ((start += l->l_start) < 0 || l->l_len < 0)
  219.         return 0;
  220.     fl->fl_type = l->l_type;
  221.     fl->fl_start = start;    /* we record the absolute position */
  222.     fl->fl_whence = 0;    /* FIXME: do we record {l_start} as passed? */
  223.     if (l->l_len == 0 || (fl->fl_end = start + l->l_len - 1) < 0)
  224.         fl->fl_end = OFFSET_MAX;
  225.     fl->fl_owner = current;
  226.     fl->fl_fd = fd;
  227.     fl->fl_wait = NULL;        /* just for cleanliness */
  228.     return 1;
  229. }
  230.  
  231. /*
  232.  * Determine if lock {sys_fl} blocks lock {caller_fl} ...
  233.  */
  234.  
  235. static int conflict(struct file_lock *caller_fl, struct file_lock *sys_fl)
  236. {
  237.     if (   caller_fl->fl_owner == sys_fl->fl_owner
  238.             && caller_fl->fl_fd == sys_fl->fl_fd)
  239.         return 0;
  240.     if (!overlap(caller_fl, sys_fl))
  241.         return 0;
  242.     switch (caller_fl->fl_type) {
  243.     case F_RDLCK :
  244.         return sys_fl->fl_type != F_RDLCK;
  245.     case F_WRLCK :
  246.         return 1;    /* overlapping region not owned by caller */
  247.     }
  248.     return 0;    /* shouldn't get here, but just in case */
  249. }
  250.  
  251. static int overlap(struct file_lock *fl1, struct file_lock *fl2)
  252. {
  253.     return fl1->fl_end >= fl2->fl_start && fl2->fl_end >= fl1->fl_start;
  254. }
  255.  
  256. /*
  257.  * Add a lock to a file ...
  258.  * Result is 0 for success or -ENOLCK.
  259.  *
  260.  * We merge adjacent locks whenever possible.
  261.  *
  262.  * WARNING: We assume the lock doesn't conflict with any other lock.
  263.  */
  264.   
  265. /*
  266.  * Rewritten by Kai Petzke:
  267.  * We sort the lock list first by owner, then by the starting address.
  268.  *
  269.  * To make freeing a lock much faster, we keep a pointer to the lock before the
  270.  * actual one. But the real gain of the new coding was, that lock_it() and
  271.  * unlock_it() became one function.
  272.  *
  273.  * To all purists: Yes, I use a few goto's. Just pass on to the next function.
  274.  */
  275.  
  276. static int lock_it(struct file *filp, struct file_lock *caller, unsigned int fd)
  277. {
  278.     struct file_lock *fl;
  279.     struct file_lock *left = 0;
  280.     struct file_lock *right = 0;
  281.     struct file_lock **before;
  282.     int added = 0;
  283.  
  284.     /*
  285.      * Find the first old lock with the same owner as the new lock.
  286.      */
  287.  
  288.     before = &filp->f_inode->i_flock;
  289.     while ((fl = *before) &&
  290.         (caller->fl_owner != fl->fl_owner ||
  291.          caller->fl_fd != fl->fl_fd))
  292.         before = &fl->fl_next;
  293.  
  294.     /*
  295.      * Look up all locks of this owner.
  296.      */
  297.  
  298.     while (   (fl = *before)
  299.                && caller->fl_owner == fl->fl_owner
  300.                && caller->fl_fd == fl->fl_fd) {
  301.         /*
  302.          * Detect adjacent or overlapping regions (if same lock type)
  303.          */
  304.         if (caller->fl_type == fl->fl_type) {
  305.             if (fl->fl_end < caller->fl_start - 1)
  306.                 goto next_lock;
  307.             /*
  308.              * If the next lock in the list has entirely bigger
  309.              * addresses than the new one, insert the lock here.
  310.              */
  311.             if (fl->fl_start > caller->fl_end + 1)
  312.                 break;
  313.  
  314.             /*
  315.              * If we come here, the new and old lock are of the
  316.              * same type and adjacent or overlapping. Make one
  317.              * lock yielding from the lower start address of both
  318.              * locks to the higher end address.
  319.              */
  320.             if (fl->fl_start > caller->fl_start)
  321.                 fl->fl_start = caller->fl_start;
  322.             else
  323.                 caller->fl_start = fl->fl_start;
  324.             if (fl->fl_end < caller->fl_end)
  325.                 fl->fl_end = caller->fl_end;
  326.             else
  327.                 caller->fl_end = fl->fl_end;
  328.             if (added) {
  329.                 free_lock(before);
  330.                 continue;
  331.             }
  332.             caller = fl;
  333.             added = 1;
  334.             goto next_lock;
  335.         }
  336.         /*
  337.          * Processing for different lock types is a bit more complex.
  338.          */
  339.         if (fl->fl_end < caller->fl_start)
  340.             goto next_lock;
  341.         if (fl->fl_start > caller->fl_end)
  342.             break;
  343.         if (caller->fl_type == F_UNLCK)
  344.             added = 1;
  345.         if (fl->fl_start < caller->fl_start)
  346.             left = fl;
  347.         /*
  348.          * If the next lock in the list has a higher end address than
  349.          * the new one, insert the new one here.
  350.          */
  351.         if (fl->fl_end > caller->fl_end) {
  352.             right = fl;
  353.             break;
  354.         }
  355.         if (fl->fl_start >= caller->fl_start) {
  356.             /*
  357.              * The new lock completely replaces an old one (This may
  358.              * happen several times).
  359.              */
  360.             if (added) {
  361.                 free_lock(before);
  362.                 continue;
  363.             }
  364.             /*
  365.              * Replace the old lock with the new one. Wake up
  366.              * anybody waiting for the old one, as the change in
  367.              * lock type migth satisfy his needs.
  368.              */
  369.             wake_up(&fl->fl_wait);
  370.             fl->fl_start = caller->fl_start;
  371.             fl->fl_end   = caller->fl_end;
  372.             fl->fl_type  = caller->fl_type;
  373.             caller = fl;
  374.             added = 1;
  375.         }
  376.         /*
  377.          * Go on to next lock.
  378.          */
  379. next_lock:
  380.         before = &(*before)->fl_next;
  381.     }
  382.  
  383.     if (! added) {
  384.         if (caller->fl_type == F_UNLCK)
  385.             return -EINVAL;
  386.         if (! (caller = alloc_lock(before, caller, fd)))
  387.             return -ENOLCK;
  388.     }
  389.     if (right) {
  390.         if (left == right) {
  391.             /*
  392.              * The new lock breaks the old one in two pieces, so we
  393.              * have to allocate one more lock (in this case, even
  394.              * F_UNLCK may fail!).
  395.              */
  396.             if (! (left = alloc_lock(before, right, fd))) {
  397.                 if (! added)
  398.                     free_lock(before);
  399.                 return -ENOLCK;
  400.             }
  401.         }
  402.         right->fl_start = caller->fl_end + 1;
  403.     }
  404.     if (left)
  405.         left->fl_end = caller->fl_start - 1;
  406.     return 0;
  407. }
  408.  
  409. /*
  410.  * File_lock() inserts a lock at the position pos of the linked list.
  411.  */
  412.  
  413. static struct file_lock *alloc_lock(struct file_lock **pos,
  414.                     struct file_lock *fl,
  415.                                     unsigned int     fd)
  416. {
  417.     struct file_lock *tmp;
  418.  
  419.     tmp = file_lock_free_list;
  420.     if (tmp == NULL)
  421.         return NULL;            /* no available entry */
  422.     if (tmp->fl_owner != NULL)
  423.         panic("alloc_lock: broken free list\n");
  424.  
  425.     /* remove from free list */
  426.     file_lock_free_list = tmp->fl_next;
  427.  
  428.     *tmp = *fl;
  429.  
  430.     tmp->fl_next = *pos;    /* insert into file's list */
  431.     *pos = tmp;
  432.  
  433.     tmp->fl_owner = current;    /* FIXME: needed? */
  434.     tmp->fl_fd = fd;        /* FIXME: needed? */
  435.     tmp->fl_wait = NULL;
  436.     return tmp;
  437. }
  438.  
  439. /*
  440.  * Add a lock to the free list ...
  441.  */
  442.  
  443. static void free_lock(struct file_lock **fl_p)
  444. {
  445.     struct file_lock *fl;
  446.  
  447.     fl = *fl_p;
  448.     if (fl->fl_owner == NULL)    /* sanity check */
  449.         panic("free_lock: broken lock list\n");
  450.  
  451.     *fl_p = (*fl_p)->fl_next;
  452.  
  453.     fl->fl_next = file_lock_free_list;    /* add to free list */
  454.     file_lock_free_list = fl;
  455.     fl->fl_owner = NULL;            /* for sanity checks */
  456.  
  457.     wake_up(&fl->fl_wait);
  458. }
  459.