Annotation of linux/fs/locks.c, revision 1.1

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.