root/fs/locks.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fcntl_init_locks
  2. fcntl_getlk
  3. fcntl_setlk
  4. fcntl_remove_locks
  5. copy_flock
  6. conflict
  7. overlap
  8. lock_it
  9. alloc_lock
  10. free_lock

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

/* [previous][next][first][last][top][bottom][index][help] */