Annotation of XNU/bsd/miscfs/devfs/devfs_vnops.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
        !             3:  *
        !             4:  * @APPLE_LICENSE_HEADER_START@
        !             5:  * 
        !             6:  * The contents of this file constitute Original Code as defined in and
        !             7:  * are subject to the Apple Public Source License Version 1.1 (the
        !             8:  * "License").  You may not use this file except in compliance with the
        !             9:  * License.  Please obtain a copy of the License at
        !            10:  * http://www.apple.com/publicsource and read it before using this file.
        !            11:  * 
        !            12:  * This Original Code and all software distributed under the License are
        !            13:  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
        !            14:  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
        !            15:  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
        !            16:  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
        !            17:  * License for the specific language governing rights and limitations
        !            18:  * under the License.
        !            19:  * 
        !            20:  * @APPLE_LICENSE_HEADER_END@
        !            21:  */
        !            22: /*
        !            23:  * Copyright 1997,1998 Julian Elischer.  All rights reserved.
        !            24:  * [email protected]
        !            25:  * 
        !            26:  * Redistribution and use in source and binary forms, with or without
        !            27:  * modification, are permitted provided that the following conditions are
        !            28:  * met:
        !            29:  *  1. Redistributions of source code must retain the above copyright
        !            30:  *     notice, this list of conditions and the following disclaimer.
        !            31:  *  2. Redistributions in binary form must reproduce the above copyright notice,
        !            32:  *     this list of conditions and the following disclaimer in the documentation
        !            33:  *     and/or other materials provided with the distribution.
        !            34:  * 
        !            35:  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS
        !            36:  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
        !            37:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
        !            38:  * DISCLAIMED.  IN NO EVENT SHALL THE HOLDER OR CONTRIBUTORS BE LIABLE FOR
        !            39:  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            40:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
        !            41:  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
        !            42:  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            43:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            44:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            45:  * SUCH DAMAGE.
        !            46:  * 
        !            47:  * devfs_vnops.c
        !            48:  */
        !            49: 
        !            50: /*
        !            51:  * HISTORY
        !            52:  *  Clark Warner ([email protected]) Tue Feb 10 2000
        !            53:  *  - Added err_copyfile to the vnode operations table
        !            54:  *  Dieter Siegmund ([email protected]) Thu Apr  8 14:08:19 PDT 1999
        !            55:  *  - instead of duplicating specfs here, created a vnode-ops table
        !            56:  *    that redirects most operations to specfs (as is done with ufs);
        !            57:  *  - removed routines that made no sense
        !            58:  *  - cleaned up reclaim: replaced devfs_vntodn() with a macro VTODN()
        !            59:  *  - cleaned up symlink, link locking
        !            60:  *  - added the devfs_lock to protect devfs data structures against
        !            61:  *    driver's calling devfs_add_devswf()/etc.
        !            62:  *  Dieter Siegmund ([email protected]) Wed Jul 14 13:37:59 PDT 1999
        !            63:  *  - free the devfs devnode in devfs_inactive(), not just in devfs_reclaim()
        !            64:  *    to free up kernel memory as soon as it's available
        !            65:  *  - got rid of devfsspec_{read, write}
        !            66:  *  Dieter Siegmund ([email protected]) Fri Sep 17 09:58:38 PDT 1999
        !            67:  *  - update the mod/access times
        !            68:  */
        !            69: 
        !            70: #include <sys/param.h>
        !            71: #include <sys/systm.h>
        !            72: #include <sys/buf.h>
        !            73: #include <sys/namei.h>
        !            74: #include <sys/kernel.h>
        !            75: #include <sys/fcntl.h>
        !            76: #include <sys/conf.h>
        !            77: #include <sys/disklabel.h>
        !            78: #include <sys/lock.h>
        !            79: #include <sys/stat.h>
        !            80: #include <sys/mount.h>
        !            81: #include <sys/proc.h>
        !            82: #include <sys/time.h>
        !            83: #include <sys/vnode.h>
        !            84: #include <miscfs/specfs/specdev.h>
        !            85: #include <sys/dirent.h>
        !            86: #include <sys/vmmeter.h>
        !            87: #include <sys/vm.h>
        !            88: 
        !            89: #include "devfsdefs.h"
        !            90: 
        !            91: /*
        !            92:  * Convert a component of a pathname into a pointer to a locked node.
        !            93:  * This is a very central and rather complicated routine.
        !            94:  * If the file system is not maintained in a strict tree hierarchy,
        !            95:  * this can result in a deadlock situation (see comments in code below).
        !            96:  *
        !            97:  * The flag argument is LOOKUP, CREATE, RENAME, or DELETE depending on
        !            98:  * whether the name is to be looked up, created, renamed, or deleted.
        !            99:  * When CREATE, RENAME, or DELETE is specified, information usable in
        !           100:  * creating, renaming, or deleting a directory entry may be calculated.
        !           101:  * If flag has LOCKPARENT or'ed into it and the target of the pathname
        !           102:  * exists, lookup returns both the target and its parent directory locked.
        !           103:  * When creating or renaming and LOCKPARENT is specified, the target may
        !           104:  * not be ".".  When deleting and LOCKPARENT is specified, the target may
        !           105:  * be "."., but the caller must check to ensure it does an vrele and DNUNLOCK
        !           106:  * instead of two DNUNLOCKs.
        !           107:  *
        !           108:  * Overall outline of devfs_lookup:
        !           109:  *
        !           110:  *     check accessibility of directory
        !           111:  *     null terminate the component (lookup leaves the whole string alone)
        !           112:  *     look for name in cache, if found, then if at end of path
        !           113:  *       and deleting or creating, drop it, else return name
        !           114:  *     search for name in directory, to found or notfound
        !           115:  * notfound:
        !           116:  *     if creating, return locked directory,
        !           117:  *     else return error
        !           118:  * found:
        !           119:  *     if at end of path and deleting, return information to allow delete
        !           120:  *     if at end of path and rewriting (RENAME and LOCKPARENT), lock target
        !           121:  *       node and return info to allow rewrite
        !           122:  *     if not at end, add name to cache; if at end and neither creating
        !           123:  *       nor deleting, add name to cache
        !           124:  * On return to lookup, remove the null termination we put in at the start.
        !           125:  *
        !           126:  * NOTE: (LOOKUP | LOCKPARENT) currently returns the parent node unlocked.
        !           127:  */
        !           128: static int
        !           129: devfs_lookup(struct vop_lookup_args *ap)
        !           130:         /*struct vop_lookup_args {
        !           131:                 struct vnode * a_dvp; directory vnode ptr
        !           132:                 struct vnode ** a_vpp; where to put the result
        !           133:                 struct componentname * a_cnp; the name we want
        !           134:         };*/
        !           135: {
        !           136:        struct componentname *cnp = ap->a_cnp;
        !           137:        struct vnode *dir_vnode = ap->a_dvp;
        !           138:        struct vnode **result_vnode = ap->a_vpp;
        !           139:        devnode_t *   dir_node;       /* the directory we are searching */
        !           140:        devnode_t *   node = NULL;       /* the node we are searching for */
        !           141:        devdirent_t * nodename;
        !           142:        int flags = cnp->cn_flags;
        !           143:        int op = cnp->cn_nameiop;       /* LOOKUP, CREATE, RENAME, or DELETE */
        !           144:        int lockparent = flags & LOCKPARENT;
        !           145:        int wantparent = flags & (LOCKPARENT|WANTPARENT);
        !           146:        int error = 0;
        !           147:        struct proc *p = cnp->cn_proc;
        !           148:        char    heldchar;       /* the char at the end of the name componet */
        !           149: 
        !           150:        *result_vnode = NULL; /* safe not sorry */ /*XXX*/
        !           151: 
        !           152:        if (dir_vnode->v_usecount == 0)
        !           153:            printf("devfs_lookup: dir had no refs ");
        !           154:        dir_node = VTODN(dir_vnode);
        !           155: 
        !           156:        /*
        !           157:         * Check accessiblity of directory.
        !           158:         */
        !           159:        if (dir_node->dn_type != DEV_DIR) {
        !           160:                return (ENOTDIR);
        !           161:        }
        !           162: 
        !           163:        if ((error = VOP_ACCESS(dir_vnode, VEXEC, cnp->cn_cred, p)) != 0) {
        !           164:                return (error);
        !           165:        }
        !           166: 
        !           167:        /* temporarily terminate string component */
        !           168:        heldchar = cnp->cn_nameptr[cnp->cn_namelen];
        !           169:        cnp->cn_nameptr[cnp->cn_namelen] = '\0';
        !           170:        DEVFS_LOCK(p);
        !           171:        nodename = dev_findname(dir_node,cnp->cn_nameptr);
        !           172:        if (nodename) {
        !           173:            /* entry exists */
        !           174:            node = nodename->de_dnp;
        !           175:            node->dn_last_lookup = nodename; /* for unlink */
        !           176:            /* Do potential vnode allocation here inside the lock 
        !           177:             * to make sure that our device node has a non-NULL dn_vn
        !           178:             * associated with it.  The device node might otherwise
        !           179:             * get deleted out from under us (see devfs_dn_free()).
        !           180:             */
        !           181:            error = devfs_dntovn(node, result_vnode, p);
        !           182:        }
        !           183:        DEVFS_UNLOCK(p);
        !           184:        /* restore saved character */
        !           185:        cnp->cn_nameptr[cnp->cn_namelen] = heldchar;
        !           186: 
        !           187:        if (error)
        !           188:            return (error);
        !           189: 
        !           190:        if (!nodename) { /* no entry */
        !           191:                /* If it doesn't exist and we're not the last component,
        !           192:                 * or we're at the last component, but we're not creating
        !           193:                 * or renaming, return ENOENT.
        !           194:                 */
        !           195:                if (!(flags & ISLASTCN) || !(op == CREATE || op == RENAME)) {
        !           196:                        return ENOENT;
        !           197:                }
        !           198:                /*
        !           199:                 * Access for write is interpreted as allowing
        !           200:                 * creation of files in the directory.
        !           201:                 */
        !           202:                if ((error = VOP_ACCESS(dir_vnode, VWRITE,
        !           203:                                cnp->cn_cred, p)) != 0)
        !           204:                {
        !           205:                        return (error);
        !           206:                }
        !           207:                /*
        !           208:                 * We return with the directory locked, so that
        !           209:                 * the parameters we set up above will still be
        !           210:                 * valid if we actually decide to add a new entry.
        !           211:                 * We return ni_vp == NULL to indicate that the entry
        !           212:                 * does not currently exist; we leave a pointer to
        !           213:                 * the (locked) directory vnode in namei_data->ni_dvp.
        !           214:                 * The pathname buffer is saved so that the name
        !           215:                 * can be obtained later.
        !           216:                 *
        !           217:                 * NB - if the directory is unlocked, then this
        !           218:                 * information cannot be used.
        !           219:                 */
        !           220:                cnp->cn_flags |= SAVENAME;
        !           221:                if (!lockparent)
        !           222:                        VOP_UNLOCK(dir_vnode, 0, p);
        !           223:                return (EJUSTRETURN);
        !           224:        }
        !           225: 
        !           226:        /*
        !           227:         * If deleting, and at end of pathname, return
        !           228:         * parameters which can be used to remove file.
        !           229:         * If the wantparent flag isn't set, we return only
        !           230:         * the directory (in namei_data->ni_dvp), otherwise we go
        !           231:         * on and lock the node, being careful with ".".
        !           232:         */
        !           233:        if (op == DELETE && (flags & ISLASTCN)) {
        !           234:                /*
        !           235:                 * Write access to directory required to delete files.
        !           236:                 */
        !           237:                if ((error = VOP_ACCESS(dir_vnode, VWRITE,
        !           238:                                cnp->cn_cred, p)) != 0)
        !           239:                        return (error);
        !           240:                /*
        !           241:                 * we are trying to delete '.'.  What does this mean? XXX
        !           242:                 */
        !           243:                if (dir_node == node) {
        !           244:                        VREF(dir_vnode);
        !           245:                        *result_vnode = dir_vnode;
        !           246:                        return (0);
        !           247:                }
        !           248: #ifdef NOTYET
        !           249:                /*
        !           250:                 * If directory is "sticky", then user must own
        !           251:                 * the directory, or the file in it, else she
        !           252:                 * may not delete it (unless she's root). This
        !           253:                 * implements append-only directories.
        !           254:                 */
        !           255:                if ((dir_node->mode & ISVTX) &&
        !           256:                    cnp->cn_cred->cr_uid != 0 &&
        !           257:                    cnp->cn_cred->cr_uid != dir_node->uid &&
        !           258:                    cnp->cn_cred->cr_uid != node->uid) {
        !           259:                        VOP_UNLOCK(*result_vnode, 0, p);
        !           260:                        return (EPERM);
        !           261:                }
        !           262: #endif
        !           263:                if (!lockparent)
        !           264:                        VOP_UNLOCK(dir_vnode, 0, p);
        !           265:                return (0);
        !           266:        }
        !           267: 
        !           268:        /*
        !           269:         * If rewriting (RENAME), return the vnode and the
        !           270:         * information required to rewrite the present directory
        !           271:         * Must get node of directory entry to verify it's a
        !           272:         * regular file, or empty directory.
        !           273:         */
        !           274:        if (op == RENAME && wantparent && (flags & ISLASTCN)) {
        !           275:                /*
        !           276:                 * Are we allowed to change the holding directory?
        !           277:                 */
        !           278:                if ((error = VOP_ACCESS(dir_vnode, VWRITE,
        !           279:                                cnp->cn_cred, p)) != 0)
        !           280:                        return (error);
        !           281:                /*
        !           282:                 * Careful about locking second node.
        !           283:                 * This can only occur if the target is ".".
        !           284:                 */
        !           285:                if (dir_node == node)
        !           286:                        return (EISDIR);
        !           287:                /* hmm save the 'from' name (we need to delete it) */
        !           288:                cnp->cn_flags |= SAVENAME;
        !           289:                if (!lockparent)
        !           290:                        VOP_UNLOCK(dir_vnode, 0, p);
        !           291:                return (0);
        !           292:        }
        !           293: 
        !           294:        /*
        !           295:         * Step through the translation in the name.  We do not unlock the
        !           296:         * directory because we may need it again if a symbolic link
        !           297:         * is relative to the current directory.  Instead we save it
        !           298:         * unlocked as "saved_dir_node" XXX.  We must get the target
        !           299:         * node before unlocking
        !           300:         * the directory to insure that the node will not be removed
        !           301:         * before we get it.  We prevent deadlock by always fetching
        !           302:         * nodes from the root, moving down the directory tree. Thus
        !           303:         * when following backward pointers ".." we must unlock the
        !           304:         * parent directory before getting the requested directory.
        !           305:         * There is a potential race condition here if both the current
        !           306:         * and parent directories are removed before the lock for the
        !           307:         * node associated with ".." returns.  We hope that this occurs
        !           308:         * infrequently since we cannot avoid this race condition without
        !           309:         * implementing a sophisticated deadlock detection algorithm.
        !           310:         * Note also that this simple deadlock detection scheme will not
        !           311:         * work if the file system has any hard links other than ".."
        !           312:         * that point backwards in the directory structure.
        !           313:         */
        !           314:        if (flags & ISDOTDOT) {
        !           315:                VOP_UNLOCK(dir_vnode, 0, p);    /* race to get the node */
        !           316:                if (lockparent && (flags & ISLASTCN))
        !           317:                        vn_lock(dir_vnode, LK_EXCLUSIVE | LK_RETRY, p);
        !           318:        } else if (dir_node == node) {
        !           319: #if 0
        !           320:            /* 
        !           321:             * this next statement is wrong: we already did a vget in 
        !           322:             * devfs_dntovn(); DWS 4/16/1999
        !           323:             */
        !           324:                 VREF(dir_vnode);        /* we want ourself, ie "." */
        !           325: #endif
        !           326:                *result_vnode = dir_vnode;
        !           327:        } else {
        !           328:                if (!lockparent || (flags & ISLASTCN))
        !           329:                        VOP_UNLOCK(dir_vnode, 0, p);
        !           330:        }
        !           331: 
        !           332:        return (0);
        !           333: }
        !           334: 
        !           335: static int
        !           336: devfs_access(struct vop_access_args *ap)
        !           337:         /*struct vop_access_args  {
        !           338:                 struct vnode *a_vp;
        !           339:                 int  a_mode;
        !           340:                 struct ucred *a_cred;
        !           341:                 struct proc *a_p;
        !           342:         } */ 
        !           343: {
        !           344:        /*
        !           345:         *  mode is filled with a combination of VREAD, VWRITE,
        !           346:         *  and/or VEXEC bits turned on.  In an octal number these
        !           347:         *  are the Y in 0Y00.
        !           348:         */
        !           349:        struct vnode *vp = ap->a_vp;
        !           350:        int mode = ap->a_mode;
        !           351:        struct ucred *cred = ap->a_cred;
        !           352:        devnode_t *     file_node;
        !           353:        gid_t   *gp;
        !           354:        int     i;
        !           355:        struct proc *p = ap->a_p;
        !           356: 
        !           357:        file_node = VTODN(vp);
        !           358:        /* 
        !           359:         * if we are not running as a process, we are in the 
        !           360:         * kernel and we DO have permission
        !           361:         */
        !           362:        if (p == NULL)
        !           363:            return 0;
        !           364: 
        !           365:        /*
        !           366:         * Access check is based on only one of owner, group, public.
        !           367:         * If not owner, then check group. If not a member of the
        !           368:         * group, then check public access.
        !           369:         */
        !           370:        if (cred->cr_uid != file_node->dn_uid)
        !           371:        {
        !           372:                /* failing that.. try groups */
        !           373:                mode >>= 3;
        !           374:                gp = cred->cr_groups;
        !           375:                for (i = 0; i < cred->cr_ngroups; i++, gp++)
        !           376:                {
        !           377:                        if (file_node->dn_gid == *gp)
        !           378:                        {
        !           379:                                goto found;
        !           380:                        }
        !           381:                }
        !           382:                /* failing that.. try general access */
        !           383:                mode >>= 3;
        !           384: found:
        !           385:                ;
        !           386:        }
        !           387:        if ((file_node->dn_mode & mode) == mode)
        !           388:                return (0);
        !           389:        /*
        !           390:         *  Root gets to do anything.
        !           391:         * but only use suser prives as a last resort
        !           392:         * (Use of super powers is recorded in ap->a_p->p_acflag)
        !           393:         */
        !           394:        if( suser(cred, &ap->a_p->p_acflag) == 0) /* XXX what if no proc? */
        !           395:                return 0;
        !           396:        return (EACCES);
        !           397: }
        !           398: 
        !           399: static int
        !           400: devfs_getattr(struct vop_getattr_args *ap)
        !           401:         /*struct vop_getattr_args {
        !           402:                 struct vnode *a_vp;
        !           403:                 struct vattr *a_vap;
        !           404:                 struct ucred *a_cred;
        !           405:                 struct proc *a_p;
        !           406:         } */ 
        !           407: {
        !           408:        struct vnode *vp = ap->a_vp;
        !           409:        struct vattr *vap = ap->a_vap;
        !           410:        devnode_t *     file_node;
        !           411:        struct timeval  tv;
        !           412: 
        !           413:        file_node = VTODN(vp);
        !           414:        tv = time;
        !           415:        dn_times(file_node, tv, tv);
        !           416:        vap->va_rdev = 0;/* default value only */
        !           417:        vap->va_mode = file_node->dn_mode;
        !           418:        switch (file_node->dn_type)
        !           419:        {
        !           420:        case    DEV_DIR:
        !           421:                vap->va_rdev = (dev_t)file_node->dn_dvm;
        !           422:                vap->va_mode |= (S_IFDIR);
        !           423:                break;
        !           424:        case    DEV_CDEV:
        !           425:                vap->va_rdev = file_node->dn_typeinfo.dev;
        !           426:                vap->va_mode |= (S_IFCHR);
        !           427:                break;
        !           428:        case    DEV_BDEV:
        !           429:                vap->va_rdev = file_node->dn_typeinfo.dev;
        !           430:                vap->va_mode |= (S_IFBLK);
        !           431:                break;
        !           432:        case    DEV_SLNK:
        !           433:                vap->va_mode |= (S_IFLNK);
        !           434:                break;
        !           435:        }
        !           436:        vap->va_type = vp->v_type;
        !           437:        vap->va_nlink = file_node->dn_links;
        !           438:        vap->va_uid = file_node->dn_uid;
        !           439:        vap->va_gid = file_node->dn_gid;
        !           440:        vap->va_fsid = (int32_t)(void *)file_node->dn_dvm;
        !           441:        vap->va_fileid = (int32_t)(void *)file_node;
        !           442:        vap->va_size = file_node->dn_len; /* now a u_quad_t */
        !           443:        /* this doesn't belong here */
        !           444:        if (vp->v_type == VBLK)
        !           445:                vap->va_blocksize = BLKDEV_IOSIZE;
        !           446:        else if (vp->v_type == VCHR)
        !           447:                vap->va_blocksize = MAXPHYSIO;
        !           448:        else
        !           449:                vap->va_blocksize = vp->v_mount->mnt_stat.f_iosize;
        !           450:        /* if the time is bogus, set it to the boot time */
        !           451:        if (file_node->dn_ctime.tv_sec == 0)
        !           452:            file_node->dn_ctime.tv_sec = boottime.tv_sec;
        !           453:        if (file_node->dn_mtime.tv_sec == 0)
        !           454:            file_node->dn_mtime.tv_sec = boottime.tv_sec;
        !           455:        if (file_node->dn_atime.tv_sec == 0)
        !           456:            file_node->dn_atime.tv_sec = boottime.tv_sec;
        !           457:        vap->va_ctime = file_node->dn_ctime;
        !           458:        vap->va_mtime = file_node->dn_mtime;
        !           459:        vap->va_atime = file_node->dn_atime;
        !           460:        vap->va_gen = 0;
        !           461:        vap->va_flags = 0;
        !           462:        vap->va_bytes = file_node->dn_len;              /* u_quad_t */
        !           463:        vap->va_filerev = 0; /* XXX */          /* u_quad_t */
        !           464:        vap->va_vaflags = 0; /* XXX */
        !           465:        return 0;
        !           466: }
        !           467: 
        !           468: static int
        !           469: devfs_setattr(struct vop_setattr_args *ap)
        !           470:         /*struct vop_setattr_args  {
        !           471:                 struct vnode *a_vp;
        !           472:                 struct vattr *a_vap;
        !           473:                 struct ucred *a_cred;
        !           474:                 struct proc *a_p;
        !           475:         } */ 
        !           476: {
        !           477:        struct vnode *vp = ap->a_vp;
        !           478:        struct vattr *vap = ap->a_vap;
        !           479:        struct ucred *cred = ap->a_cred;
        !           480:        struct proc *p = ap->a_p;
        !           481:        int error = 0;
        !           482:        gid_t *gp;
        !           483:        int i;
        !           484:        devnode_t *     file_node;
        !           485:        struct timeval atimeval, mtimeval;
        !           486: 
        !           487:        if (vap->va_flags != VNOVAL)    /* XXX needs to be implemented */
        !           488:                return (EOPNOTSUPP);
        !           489: 
        !           490:        file_node = VTODN(vp);
        !           491: 
        !           492:        if ((vap->va_type != VNON)  ||
        !           493:            (vap->va_nlink != VNOVAL)  ||
        !           494:            (vap->va_fsid != VNOVAL)  ||
        !           495:            (vap->va_fileid != VNOVAL)  ||
        !           496:            (vap->va_blocksize != VNOVAL)  ||
        !           497:            (vap->va_rdev != VNOVAL)  ||
        !           498:            (vap->va_bytes != VNOVAL)  ||
        !           499:            (vap->va_gen != VNOVAL ))
        !           500:        {
        !           501:                return EINVAL;
        !           502:        }
        !           503: 
        !           504:        /*
        !           505:         * Go through the fields and update iff not VNOVAL.
        !           506:         */
        !           507:        if (vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL) {
        !           508:            if (cred->cr_uid != file_node->dn_uid &&
        !           509:                (error = suser(cred, &p->p_acflag)) &&
        !           510:                ((vap->va_vaflags & VA_UTIMES_NULL) == 0 || 
        !           511:                 (error = VOP_ACCESS(vp, VWRITE, cred, p))))
        !           512:                return (error);
        !           513:            if (vap->va_atime.tv_sec != VNOVAL)
        !           514:                file_node->dn_flags |= DN_ACCESS;
        !           515:            if (vap->va_mtime.tv_sec != VNOVAL)
        !           516:                file_node->dn_flags |= DN_CHANGE | DN_UPDATE;
        !           517:            atimeval.tv_sec = vap->va_atime.tv_sec;
        !           518:            atimeval.tv_usec = vap->va_atime.tv_nsec / 1000;
        !           519:            mtimeval.tv_sec = vap->va_mtime.tv_sec;
        !           520:            mtimeval.tv_usec = vap->va_mtime.tv_nsec / 1000;
        !           521:            if (error = VOP_UPDATE(vp, &atimeval, &mtimeval, 1))
        !           522:                return (error);
        !           523:        }
        !           524: 
        !           525:        /*
        !           526:         * Change the permissions.. must be root or owner to do this.
        !           527:         */
        !           528:        if (vap->va_mode != (u_short)VNOVAL) {
        !           529:                if ((cred->cr_uid != file_node->dn_uid)
        !           530:                 && (error = suser(cred, &p->p_acflag)))
        !           531:                        return (error);
        !           532:                file_node->dn_mode &= ~07777;
        !           533:                file_node->dn_mode |= vap->va_mode & 07777;
        !           534:        }
        !           535: 
        !           536:        /*
        !           537:         * Change the owner.. must be root to do this.
        !           538:         */
        !           539:        if (vap->va_uid != (uid_t)VNOVAL) {
        !           540:                if (error = suser(cred, &p->p_acflag))
        !           541:                        return (error);
        !           542:                file_node->dn_uid = vap->va_uid;
        !           543:        }
        !           544: 
        !           545:        /*
        !           546:         * Change the group.. must be root or owner to do this.
        !           547:         * If we are the owner, we must be in the target group too.
        !           548:         * don't use suser() unless you have to as it reports
        !           549:         * whether you needed suser powers or not.
        !           550:         */
        !           551:        if (vap->va_gid != (gid_t)VNOVAL) {
        !           552:                if (cred->cr_uid == file_node->dn_uid){
        !           553:                        gp = cred->cr_groups;
        !           554:                        for (i = 0; i < cred->cr_ngroups; i++, gp++) {
        !           555:                                if (vap->va_gid == *gp)
        !           556:                                        goto cando; 
        !           557:                        }
        !           558:                }
        !           559:                /*
        !           560:                 * we can't do it with normal privs,
        !           561:                 * do we have an ace up our sleeve?
        !           562:                 */
        !           563:                if (error = suser(cred, &p->p_acflag))
        !           564:                        return (error);
        !           565: cando:
        !           566:                file_node->dn_gid = vap->va_gid;
        !           567:        }
        !           568: #if 0
        !           569:        /*
        !           570:         * Copied from somewhere else
        !           571:         * but only kept as a marker and reminder of the fact that
        !           572:         * flags should be handled some day
        !           573:         */
        !           574:        if (vap->va_flags != VNOVAL) {
        !           575:                if (error = suser(cred, &p->p_acflag))
        !           576:                        return error;
        !           577:                if (cred->cr_uid == 0)
        !           578:                ;
        !           579:                else {
        !           580:                }
        !           581:        }
        !           582: #endif
        !           583:        return error;
        !           584: }
        !           585: 
        !           586: static int
        !           587: devfs_read(struct vop_read_args *ap)
        !           588:         /*struct vop_read_args {
        !           589:                 struct vnode *a_vp;
        !           590:                 struct uio *a_uio;
        !           591:                 int  a_ioflag;
        !           592:                 struct ucred *a_cred;
        !           593:         } */
        !           594: {
        !           595:        devnode_t * dn_p = VTODN(ap->a_vp);
        !           596: 
        !           597:        switch (ap->a_vp->v_type) {
        !           598:          case VDIR: {
        !           599:              dn_p->dn_flags |= DN_ACCESS;
        !           600:              return VOP_READDIR(ap->a_vp, ap->a_uio, ap->a_cred,
        !           601:                                 NULL, NULL, NULL);
        !           602:          }
        !           603:          default: {
        !           604:              printf("devfs_read(): bad file type %d", ap->a_vp->v_type);
        !           605:              return(EINVAL);
        !           606:              break;
        !           607:          }
        !           608:        }
        !           609:        return (0); /* not reached */
        !           610: }
        !           611: 
        !           612: static int
        !           613: devfs_close(ap)
        !           614:        struct vop_close_args /* {
        !           615:                struct vnode *a_vp;
        !           616:                int  a_fflag;
        !           617:                struct ucred *a_cred;
        !           618:                struct proc *a_p;
        !           619:        } */ *ap;
        !           620: {
        !           621:        struct vnode *          vp = ap->a_vp;
        !           622:        register devnode_t *    dnp = VTODN(vp);
        !           623: 
        !           624:        simple_lock(&vp->v_interlock);
        !           625:        if (vp->v_usecount > 1)
        !           626:            dn_times(dnp, time, time);
        !           627:        simple_unlock(&vp->v_interlock);
        !           628:        return (0);
        !           629: }
        !           630: 
        !           631: static int
        !           632: devfsspec_close(ap)
        !           633:        struct vop_close_args /* {
        !           634:                struct vnode *a_vp;
        !           635:                int  a_fflag;
        !           636:                struct ucred *a_cred;
        !           637:                struct proc *a_p;
        !           638:        } */ *ap;
        !           639: {
        !           640:        struct vnode *          vp = ap->a_vp;
        !           641:        register devnode_t *    dnp = VTODN(vp);
        !           642: 
        !           643:        simple_lock(&vp->v_interlock);
        !           644:        if (vp->v_usecount > 1)
        !           645:            dn_times(dnp, time, time);
        !           646:        simple_unlock(&vp->v_interlock);
        !           647:        return (VOCALL (spec_vnodeop_p, VOFFSET(vop_close), ap));
        !           648: }
        !           649: 
        !           650: static int
        !           651: devfsspec_read(struct vop_read_args *ap)
        !           652:         /*struct vop_read_args {
        !           653:                 struct vnode *a_vp;
        !           654:                 struct uio *a_uio;
        !           655:                 int  a_ioflag;
        !           656:                 struct ucred *a_cred;
        !           657:         } */
        !           658: {
        !           659:     VTODN(ap->a_vp)->dn_flags |= DN_ACCESS;
        !           660:     return (VOCALL (spec_vnodeop_p, VOFFSET(vop_read), ap));
        !           661: }
        !           662: 
        !           663: static int
        !           664: devfsspec_write(struct vop_write_args *ap)
        !           665:         /*struct vop_write_args  {
        !           666:                 struct vnode *a_vp;
        !           667:                 struct uio *a_uio;
        !           668:                 int  a_ioflag;
        !           669:                 struct ucred *a_cred;
        !           670:         } */
        !           671: {
        !           672:     VTODN(ap->a_vp)->dn_flags |= DN_CHANGE | DN_UPDATE;
        !           673:     return (VOCALL (spec_vnodeop_p, VOFFSET(vop_write), ap));
        !           674: }
        !           675: 
        !           676: /*
        !           677:  *  Write data to a file or directory.
        !           678:  */
        !           679: static int
        !           680: devfs_write(struct vop_write_args *ap)
        !           681:         /*struct vop_write_args  {
        !           682:                 struct vnode *a_vp;
        !           683:                 struct uio *a_uio;
        !           684:                 int  a_ioflag;
        !           685:                 struct ucred *a_cred;
        !           686:         } */
        !           687: {
        !           688:        switch (ap->a_vp->v_type) {
        !           689:        case VDIR:
        !           690:                return(EISDIR);
        !           691:        default:
        !           692:                printf("devfs_write(): bad file type %d", ap->a_vp->v_type);
        !           693:                return (EINVAL);
        !           694:        }
        !           695:        return 0; /* not reached */
        !           696: }
        !           697: 
        !           698: static int
        !           699: devfs_remove(struct vop_remove_args *ap)
        !           700:         /*struct vop_remove_args  {
        !           701:                 struct vnode *a_dvp;
        !           702:                 struct vnode *a_vp;
        !           703:                 struct componentname *a_cnp;
        !           704:         } */ 
        !           705: {
        !           706:        struct vnode *vp = ap->a_vp;
        !           707:        struct vnode *dvp = ap->a_dvp;
        !           708:        struct componentname *cnp = ap->a_cnp;
        !           709:        devnode_t *  tp;
        !           710:        devnode_t *  tdp;
        !           711:        devdirent_t * tnp;
        !           712:        int doingdirectory = 0;
        !           713:        int error = 0;
        !           714:        uid_t ouruid = cnp->cn_cred->cr_uid;
        !           715:        struct proc *p = cnp->cn_proc;
        !           716: 
        !           717:        /*
        !           718:         * Lock our directories and get our name pointers
        !           719:         * assume that the names are null terminated as they
        !           720:         * are the end of the path. Get pointers to all our
        !           721:         * devfs structures.
        !           722:         */
        !           723:        tp = VTODN(vp);
        !           724:        tdp = VTODN(dvp);
        !           725:        /*
        !           726:         * Assuming we are atomic, dev_lookup left this for us
        !           727:         */
        !           728:        tnp = tp->dn_last_lookup;
        !           729: 
        !           730:        /*
        !           731:         * Check we are doing legal things WRT the new flags
        !           732:         */
        !           733:        if ((tp->dn_flags & (IMMUTABLE | APPEND))
        !           734:          || (tdp->dn_flags & APPEND) /*XXX eh?*/ ) {
        !           735:            error = EPERM;
        !           736:            goto abort;
        !           737:        }
        !           738: 
        !           739:        /*
        !           740:         * Make sure that we don't try do something stupid
        !           741:         */
        !           742:        if ((tp->dn_type) == DEV_DIR) {
        !           743:                /*
        !           744:                 * Avoid ".", "..", and aliases of "." for obvious reasons.
        !           745:                 */
        !           746:                if ( (cnp->cn_namelen == 1 && cnp->cn_nameptr[0] == '.') 
        !           747:                    || (cnp->cn_flags&ISDOTDOT) ) {
        !           748:                        error = EINVAL;
        !           749:                        goto abort;
        !           750:                }
        !           751:                doingdirectory++;
        !           752:        }
        !           753: 
        !           754:        /***********************************
        !           755:         * Start actually doing things.... *
        !           756:         ***********************************/
        !           757:        tdp->dn_flags |= DN_CHANGE | DN_UPDATE;
        !           758: 
        !           759:        /*
        !           760:         * own the parent directory, or the destination of the rename,
        !           761:         * otherwise the destination may not be changed (except by
        !           762:         * root). This implements append-only directories.
        !           763:         * XXX shoudn't this be in generic code? 
        !           764:         */
        !           765:        if ((tdp->dn_mode & S_ISTXT)
        !           766:          && ouruid != 0
        !           767:          && ouruid != tdp->dn_uid
        !           768:          && ouruid != tp->dn_uid ) {
        !           769:            error = EPERM;
        !           770:            goto abort;
        !           771:        }
        !           772:        /*
        !           773:         * Target must be empty if a directory and have no links
        !           774:         * to it. Also, ensure source and target are compatible
        !           775:         * (both directories, or both not directories).
        !           776:         */
        !           777:        if (( doingdirectory) && (tp->dn_links > 2)) {
        !           778:            error = ENOTEMPTY;
        !           779:            goto abort;
        !           780:        }
        !           781:        DEVFS_LOCK(p);
        !           782:        dev_free_name(tnp);
        !           783:        DEVFS_UNLOCK(p);
        !           784:  abort:
        !           785:        if (dvp == vp)
        !           786:            vrele(vp);
        !           787:        else
        !           788:            vput(vp);
        !           789:        vput(dvp);
        !           790:        return (error);
        !           791: }
        !           792: 
        !           793: /*
        !           794:  */
        !           795: static int
        !           796: devfs_link(struct vop_link_args *ap)
        !           797:         /*struct vop_link_args  {
        !           798:                 struct vnode *a_tdvp;
        !           799:                 struct vnode *a_vp;
        !           800:                 struct componentname *a_cnp;
        !           801:         } */ 
        !           802: {
        !           803:        struct vnode *vp = ap->a_vp;
        !           804:        struct vnode *tdvp = ap->a_tdvp;
        !           805:        struct componentname *cnp = ap->a_cnp;
        !           806:        struct proc *p = cnp->cn_proc;
        !           807:        devnode_t * fp;
        !           808:        devnode_t * tdp;
        !           809:        devdirent_t * tnp;
        !           810:        int error = 0;
        !           811:        struct timeval tv;
        !           812: 
        !           813:        /*
        !           814:         * First catch an arbitrary restriction for this FS
        !           815:         */
        !           816:        if (cnp->cn_namelen > DEVMAXNAMESIZE) {
        !           817:                error = ENAMETOOLONG;
        !           818:                goto out1;
        !           819:        }
        !           820: 
        !           821:        /*
        !           822:         * Lock our directories and get our name pointers
        !           823:         * assume that the names are null terminated as they
        !           824:         * are the end of the path. Get pointers to all our
        !           825:         * devfs structures.
        !           826:         */
        !           827:        tdp = VTODN(tdvp);
        !           828:        fp = VTODN(vp);
        !           829:        
        !           830:        if (tdvp->v_mount != vp->v_mount) {
        !           831:                error = EXDEV;
        !           832:                VOP_ABORTOP(tdvp, cnp); 
        !           833:                goto out2;
        !           834:        }
        !           835:        if (tdvp != vp && (error = vn_lock(vp, LK_EXCLUSIVE, p))) {
        !           836:                VOP_ABORTOP(tdvp, cnp);
        !           837:                goto out2;
        !           838:        }
        !           839: 
        !           840:        /*
        !           841:         * Check we are doing legal things WRT the new flags
        !           842:         */
        !           843:        if (fp->dn_flags & (IMMUTABLE | APPEND)) {
        !           844:                VOP_ABORTOP(tdvp, cnp);
        !           845:                error = EPERM;
        !           846:                goto out1;
        !           847:        }
        !           848: 
        !           849:        /***********************************
        !           850:         * Start actually doing things.... *
        !           851:         ***********************************/
        !           852:        fp->dn_flags |= DN_CHANGE;
        !           853:        tv = time;
        !           854:        error = VOP_UPDATE(vp, &tv, &tv, 1);
        !           855:        if (!error) {
        !           856:            DEVFS_LOCK(p);
        !           857:            error = dev_add_name(cnp->cn_nameptr, tdp, NULL, fp, &tnp);
        !           858:            DEVFS_UNLOCK(p);
        !           859:        }
        !           860: out1:
        !           861:        if (tdvp != vp)
        !           862:                VOP_UNLOCK(vp, 0, p);
        !           863: out2:
        !           864:        vput(tdvp);
        !           865:        return (error);
        !           866: }
        !           867: 
        !           868: /*
        !           869:  * Check if source directory is in the path of the target directory.
        !           870:  * Target is supplied locked, source is unlocked.
        !           871:  * The target is always vput before returning.
        !           872:  */
        !           873: int
        !           874: devfs_checkpath(source, target)
        !           875:        devnode_t *source, *target;
        !           876: {
        !           877:     int error = 0;
        !           878:     devnode_t * ntmp;
        !           879:     devnode_t * tmp;
        !           880:     struct vnode *vp;
        !           881: 
        !           882:     vp = target->dn_vn;
        !           883:     tmp = target;
        !           884: 
        !           885:     do {
        !           886:        if (tmp == source) {
        !           887:            error = EINVAL;
        !           888:            break;
        !           889:        }
        !           890:        ntmp = tmp;
        !           891:     } while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp);
        !           892: 
        !           893:     if (vp != NULL)
        !           894:        vput(vp);
        !           895:     return (error);
        !           896: }
        !           897: 
        !           898: /*
        !           899:  * Rename system call. Seems overly complicated to me...
        !           900:  *     rename("foo", "bar");
        !           901:  * is essentially
        !           902:  *     unlink("bar");
        !           903:  *     link("foo", "bar");
        !           904:  *     unlink("foo");
        !           905:  * but ``atomically''.
        !           906:  *
        !           907:  * When the target exists, both the directory
        !           908:  * and target vnodes are locked.
        !           909:  * the source and source-parent vnodes are referenced
        !           910:  *
        !           911:  *
        !           912:  * Basic algorithm is:
        !           913:  *
        !           914:  * 1) Bump link count on source while we're linking it to the
        !           915:  *    target.  This also ensure the inode won't be deleted out
        !           916:  *    from underneath us while we work (it may be truncated by
        !           917:  *    a concurrent `trunc' or `open' for creation).
        !           918:  * 2) Link source to destination.  If destination already exists,
        !           919:  *    delete it first.
        !           920:  * 3) Unlink source reference to node if still around. If a
        !           921:  *    directory was moved and the parent of the destination
        !           922:  *    is different from the source, patch the ".." entry in the
        !           923:  *    directory.
        !           924:  */
        !           925: static int
        !           926: devfs_rename(struct vop_rename_args *ap)
        !           927:         /*struct vop_rename_args  {
        !           928:                 struct vnode *a_fdvp; 
        !           929:                 struct vnode *a_fvp;  
        !           930:                 struct componentname *a_fcnp;
        !           931:                 struct vnode *a_tdvp;
        !           932:                 struct vnode *a_tvp;
        !           933:                 struct componentname *a_tcnp;
        !           934:         } */
        !           935: {
        !           936:        struct vnode *tvp = ap->a_tvp;
        !           937:        struct vnode *tdvp = ap->a_tdvp;
        !           938:        struct vnode *fvp = ap->a_fvp;
        !           939:        struct vnode *fdvp = ap->a_fdvp;
        !           940:        struct componentname *tcnp = ap->a_tcnp;
        !           941:        struct componentname *fcnp = ap->a_fcnp;
        !           942:        struct proc *p = fcnp->cn_proc;
        !           943:        devnode_t *fp, *fdp, *tp, *tdp;
        !           944:        devdirent_t *fnp,*tnp;
        !           945:        int doingdirectory = 0;
        !           946:        int error = 0;
        !           947:        struct timeval tv;
        !           948: 
        !           949:        /*
        !           950:         * First catch an arbitrary restriction for this FS
        !           951:         */
        !           952:        if(tcnp->cn_namelen > DEVMAXNAMESIZE) {
        !           953:                error = ENAMETOOLONG;
        !           954:                goto abortit;
        !           955:        }
        !           956: 
        !           957:        /*
        !           958:         * Lock our directories and get our name pointers
        !           959:         * assume that the names are null terminated as they
        !           960:         * are the end of the path. Get pointers to all our
        !           961:         * devfs structures.
        !           962:         */
        !           963:        tdp = VTODN(tdvp);
        !           964:        fdp = VTODN(fdvp);
        !           965:        fp = VTODN(fvp);
        !           966:        fnp = fp->dn_last_lookup;
        !           967:        tp = NULL;
        !           968:        tnp = NULL;
        !           969:        if (tvp) {
        !           970:            tp = VTODN(tvp);
        !           971:            tnp = tp->dn_last_lookup;
        !           972:        }
        !           973:        
        !           974:        /*
        !           975:         * trying to move it out of devfs?
        !           976:          * if we move a dir across mnt points. we need to fix all
        !           977:         * the mountpoint pointers! XXX
        !           978:         * so for now keep dirs within the same mount
        !           979:         */
        !           980:        if ((fvp->v_mount != tdvp->v_mount) ||
        !           981:            (tvp && (fvp->v_mount != tvp->v_mount))) {
        !           982:                error = EXDEV;
        !           983: abortit:
        !           984:                VOP_ABORTOP(tdvp, tcnp); 
        !           985:                if (tdvp == tvp) /* eh? */
        !           986:                        vrele(tdvp);
        !           987:                else
        !           988:                        vput(tdvp);
        !           989:                if (tvp)
        !           990:                        vput(tvp);
        !           991:                VOP_ABORTOP(fdvp, fcnp); /* XXX, why not in NFS? */
        !           992:                vrele(fdvp);
        !           993:                vrele(fvp);
        !           994:                return (error);
        !           995:        }
        !           996: 
        !           997:        /*
        !           998:         * Check we are doing legal things WRT the new flags
        !           999:         */
        !          1000:        if ((tp && (tp->dn_flags & (IMMUTABLE | APPEND)))
        !          1001:          || (fp->dn_flags & (IMMUTABLE | APPEND))
        !          1002:          || (fdp->dn_flags & APPEND)) {
        !          1003:                error = EPERM;
        !          1004:                goto abortit;
        !          1005:        }
        !          1006: 
        !          1007:        /*
        !          1008:         * Make sure that we don't try do something stupid
        !          1009:         */
        !          1010:        if ((fp->dn_type) == DEV_DIR) {
        !          1011:                /*
        !          1012:                 * Avoid ".", "..", and aliases of "." for obvious reasons.
        !          1013:                 */
        !          1014:                if ((fcnp->cn_namelen == 1 && fcnp->cn_nameptr[0] == '.') 
        !          1015:                    || (fcnp->cn_flags&ISDOTDOT) 
        !          1016:                    || (tcnp->cn_namelen == 1 && tcnp->cn_nameptr[0] == '.') 
        !          1017:                    || (tcnp->cn_flags&ISDOTDOT) 
        !          1018:                    || (tdp == fp )) {
        !          1019:                        error = EINVAL;
        !          1020:                        goto abortit;
        !          1021:                }
        !          1022:                doingdirectory++;
        !          1023:        }
        !          1024: 
        !          1025:        /*
        !          1026:         * If ".." must be changed (ie the directory gets a new
        !          1027:         * parent) then the source directory must not be in the
        !          1028:         * directory hierarchy above the target, as this would
        !          1029:         * orphan everything below the source directory. Also
        !          1030:         * the user must have write permission in the source so
        !          1031:         * as to be able to change "..". 
        !          1032:         */
        !          1033:        if (doingdirectory && (tdp != fdp)) {
        !          1034:                devnode_t * tmp, *ntmp;
        !          1035:                error = VOP_ACCESS(fvp, VWRITE, tcnp->cn_cred, tcnp->cn_proc);
        !          1036:                tmp = tdp;
        !          1037:                do {
        !          1038:                        if(tmp == fp) {
        !          1039:                                /* XXX unlock stuff here probably */
        !          1040:                                error = EINVAL;
        !          1041:                                goto out;
        !          1042:                        }
        !          1043:                        ntmp = tmp;
        !          1044:                } while ((tmp = tmp->dn_typeinfo.Dir.parent) != ntmp);
        !          1045:        }
        !          1046: 
        !          1047:        /***********************************
        !          1048:         * Start actually doing things.... *
        !          1049:         ***********************************/
        !          1050:        fp->dn_flags |= DN_CHANGE;
        !          1051:        tv = time;
        !          1052:        if (error = VOP_UPDATE(fvp, &tv, &tv, 1)) {
        !          1053:            VOP_UNLOCK(fvp, 0, p);
        !          1054:            goto bad;
        !          1055:        }
        !          1056:        /*
        !          1057:         * Check if just deleting a link name.
        !          1058:         */
        !          1059:        if (fvp == tvp) {
        !          1060:                if (fvp->v_type == VDIR) {
        !          1061:                        error = EINVAL;
        !          1062:                        goto abortit;
        !          1063:                }
        !          1064: 
        !          1065:                /* Release destination completely. */
        !          1066:                VOP_ABORTOP(tdvp, tcnp);
        !          1067:                vput(tdvp);
        !          1068:                vput(tvp);
        !          1069: 
        !          1070:                /* Delete source. */
        !          1071:                VOP_ABORTOP(fdvp, fcnp); /*XXX*/
        !          1072:                vrele(fdvp);
        !          1073:                vrele(fvp);
        !          1074:                dev_free_name(fnp);
        !          1075:                return 0;
        !          1076:        }
        !          1077: 
        !          1078:        vrele(fdvp);
        !          1079: 
        !          1080:        /*
        !          1081:         * 1) Bump link count while we're moving stuff
        !          1082:         *    around.  If we crash somewhere before
        !          1083:         *    completing our work,  too bad :)
        !          1084:         */
        !          1085:        fp->dn_links++;
        !          1086:        /*
        !          1087:         * If the target exists zap it (unless it's a non-empty directory)
        !          1088:         * We could do that as well but won't
        !          1089:         */
        !          1090:        if (tp) {
        !          1091:                int ouruid = tcnp->cn_cred->cr_uid;
        !          1092:                /*
        !          1093:                 * If the parent directory is "sticky", then the user must
        !          1094:                 * own the parent directory, or the destination of the rename,
        !          1095:                 * otherwise the destination may not be changed (except by
        !          1096:                 * root). This implements append-only directories.
        !          1097:                 * XXX shoudn't this be in generic code? 
        !          1098:                 */
        !          1099:                if ((tdp->dn_mode & S_ISTXT)
        !          1100:                  && ouruid != 0
        !          1101:                  && ouruid != tdp->dn_uid
        !          1102:                  && ouruid != tp->dn_uid ) {
        !          1103:                        error = EPERM;
        !          1104:                        goto bad;
        !          1105:                }
        !          1106:                /*
        !          1107:                 * Target must be empty if a directory and have no links
        !          1108:                 * to it. Also, ensure source and target are compatible
        !          1109:                 * (both directories, or both not directories).
        !          1110:                 */
        !          1111:                if (( doingdirectory) && (tp->dn_links > 2)) {
        !          1112:                                error = ENOTEMPTY;
        !          1113:                                goto bad;
        !          1114:                }
        !          1115:                dev_free_name(tnp);
        !          1116:                tp = NULL;
        !          1117:        }
        !          1118:        dev_add_name(tcnp->cn_nameptr,tdp,NULL,fp,&tnp);
        !          1119:        fnp->de_dnp = NULL;
        !          1120:        fp->dn_links--; /* one less link to it.. */
        !          1121:        dev_free_name(fnp);
        !          1122:        fp->dn_links--; /* we added one earlier*/
        !          1123:        if (tdp)
        !          1124:                vput(tdvp);
        !          1125:        if (tp)
        !          1126:                vput(fvp);
        !          1127:        vrele(fvp);
        !          1128:        return (error);
        !          1129: 
        !          1130: bad:
        !          1131:        if (tp)
        !          1132:                vput(tvp);
        !          1133:        vput(tdvp);
        !          1134: out:
        !          1135:        if (vn_lock(fvp, LK_EXCLUSIVE | LK_RETRY, p) == 0) {
        !          1136:                fp->dn_links--; /* we added one earlier*/
        !          1137:                vput(fvp);
        !          1138:        } else
        !          1139:                vrele(fvp);
        !          1140:        return (error);
        !          1141: }
        !          1142: 
        !          1143: static int
        !          1144: devfs_symlink(struct vop_symlink_args *ap)
        !          1145:         /*struct vop_symlink_args {
        !          1146:                 struct vnode *a_dvp;
        !          1147:                 struct vnode **a_vpp;
        !          1148:                 struct componentname *a_cnp;
        !          1149:                 struct vattr *a_vap;
        !          1150:                 char *a_target;
        !          1151:         } */
        !          1152: {
        !          1153:        struct componentname * cnp = ap->a_cnp;
        !          1154:        struct vnode *vp = NULL;
        !          1155:        int error = 0;
        !          1156:        devnode_t * dir_p;
        !          1157:        devnode_type_t typeinfo;
        !          1158:        devdirent_t * nm_p;
        !          1159:        devnode_t * dev_p;
        !          1160:        struct vattr *  vap = ap->a_vap;
        !          1161:        struct vnode * * vpp = ap->a_vpp;
        !          1162:        struct proc *p = cnp->cn_proc;
        !          1163:        struct timeval tv;
        !          1164: 
        !          1165:        dir_p = VTODN(ap->a_dvp);
        !          1166:        typeinfo.Slnk.name = ap->a_target;
        !          1167:        typeinfo.Slnk.namelen = strlen(ap->a_target);
        !          1168:        DEVFS_LOCK(p);
        !          1169:        error = dev_add_entry(cnp->cn_nameptr, dir_p, DEV_SLNK, 
        !          1170:                              &typeinfo, NULL, NULL, &nm_p);
        !          1171:        DEVFS_UNLOCK(p);
        !          1172:        if (error) {
        !          1173:            goto failure;
        !          1174:        }
        !          1175:        
        !          1176:        dev_p = nm_p->de_dnp;
        !          1177:        dev_p->dn_uid = dir_p->dn_uid;
        !          1178:        dev_p->dn_gid = dir_p->dn_gid;
        !          1179:        dev_p->dn_mode = vap->va_mode;
        !          1180:        dn_copy_times(dev_p, dir_p);
        !          1181:        error = devfs_dntovn(dev_p, vpp, p);
        !          1182:        if (error)
        !          1183:            goto failure;
        !          1184:        vp = *vpp;
        !          1185:        vput(vp);
        !          1186:  failure:
        !          1187:        if ((cnp->cn_flags & SAVESTART) == 0)
        !          1188:            FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
        !          1189:        vput(ap->a_dvp);
        !          1190:        return error;
        !          1191: }
        !          1192: 
        !          1193: /*
        !          1194:  * Mknod vnode call
        !          1195:  */
        !          1196: /* ARGSUSED */
        !          1197: int
        !          1198: devfs_mknod(ap)
        !          1199:        struct vop_mknod_args /* {
        !          1200:                struct vnode *a_dvp;
        !          1201:                struct vnode **a_vpp;
        !          1202:                struct componentname *a_cnp;
        !          1203:                struct vattr *a_vap;
        !          1204:        } */ *ap;
        !          1205: {
        !          1206:        struct componentname * cnp = ap->a_cnp;
        !          1207:        devnode_t *     dev_p;
        !          1208:        devdirent_t *   devent;
        !          1209:        devnode_t *     dir_p;  /* devnode for parent directory */
        !          1210:        struct vnode *  dvp = ap->a_dvp;
        !          1211:        int             error = 0;
        !          1212:        devnode_type_t  typeinfo;
        !          1213:        struct vattr *  vap = ap->a_vap;
        !          1214:        struct vnode ** vpp = ap->a_vpp;
        !          1215:        struct proc *   p = cnp->cn_proc;
        !          1216: 
        !          1217:        *vpp = NULL;
        !          1218:        if (!vap->va_type == VBLK && !vap->va_type == VCHR) {
        !          1219:            error = EINVAL; /* only support mknod of special files */
        !          1220:            goto failure;
        !          1221:        }
        !          1222:        dir_p = VTODN(dvp);
        !          1223:        typeinfo.dev = vap->va_rdev;
        !          1224:        DEVFS_LOCK(p);
        !          1225:        error = dev_add_entry(cnp->cn_nameptr, dir_p, 
        !          1226:                              (vap->va_type == VBLK) ? DEV_BDEV : DEV_CDEV,
        !          1227:                              &typeinfo, NULL, NULL, &devent);
        !          1228:        DEVFS_UNLOCK(p);
        !          1229:        if (error) {
        !          1230:            goto failure;
        !          1231:        }
        !          1232:        dev_p = devent->de_dnp;
        !          1233:        error = devfs_dntovn(dev_p, vpp, p);
        !          1234:        if (error)
        !          1235:            goto failure;
        !          1236:        dev_p->dn_uid = cnp->cn_cred->cr_uid;
        !          1237:        dev_p->dn_gid = dir_p->dn_gid;
        !          1238:        dev_p->dn_mode = vap->va_mode;
        !          1239:  failure:
        !          1240:        if (*vpp) {
        !          1241:            vput(*vpp);
        !          1242:            *vpp = 0;
        !          1243:        }
        !          1244:        if ((cnp->cn_flags & SAVESTART) == 0)
        !          1245:            FREE_ZONE(cnp->cn_pnbuf, cnp->cn_pnlen, M_NAMEI);
        !          1246:        vput(dvp);
        !          1247:        return (error);
        !          1248: }
        !          1249: 
        !          1250: /*
        !          1251:  * Vnode op for readdir
        !          1252:  */
        !          1253: static int
        !          1254: devfs_readdir(struct vop_readdir_args *ap)
        !          1255:         /*struct vop_readdir_args {
        !          1256:                 struct vnode *a_vp;
        !          1257:                 struct uio *a_uio;
        !          1258:                 struct ucred *a_cred;
        !          1259:                int *eofflag;
        !          1260:                int *ncookies;
        !          1261:                u_int **cookies;
        !          1262:         } */
        !          1263: {
        !          1264:        struct vnode *vp = ap->a_vp;
        !          1265:        struct uio *uio = ap->a_uio;
        !          1266:        struct dirent dirent;
        !          1267:        devnode_t * dir_node;
        !          1268:        devdirent_t *   name_node;
        !          1269:        char    *name;
        !          1270:        int error = 0;
        !          1271:        int reclen;
        !          1272:        int nodenumber;
        !          1273:        int     startpos,pos;
        !          1274:        struct proc *   p = uio->uio_procp;
        !          1275: 
        !          1276:        /*  set up refs to dir */
        !          1277:        dir_node = VTODN(vp);
        !          1278:        if(dir_node->dn_type != DEV_DIR)
        !          1279:                return(ENOTDIR);
        !          1280: 
        !          1281:        pos = 0;
        !          1282:        startpos = uio->uio_offset;
        !          1283:        DEVFS_LOCK(p);
        !          1284:        name_node = dir_node->dn_typeinfo.Dir.dirlist;
        !          1285:        nodenumber = 0;
        !          1286:        dir_node->dn_flags |= DN_ACCESS;
        !          1287: 
        !          1288:        while ((name_node || (nodenumber < 2)) && (uio->uio_resid > 0))
        !          1289:        {
        !          1290:                switch(nodenumber)
        !          1291:                {
        !          1292:                case    0:
        !          1293:                        dirent.d_fileno = (int32_t)(void *)dir_node;
        !          1294:                        name = ".";
        !          1295:                        dirent.d_namlen = 1;
        !          1296:                        dirent.d_type = DT_DIR;
        !          1297:                        break;
        !          1298:                case    1:
        !          1299:                        if(dir_node->dn_typeinfo.Dir.parent)
        !          1300:                            dirent.d_fileno
        !          1301:                                = (int32_t)dir_node->dn_typeinfo.Dir.parent;
        !          1302:                        else
        !          1303:                                dirent.d_fileno = (u_int32_t)dir_node;
        !          1304:                        name = "..";
        !          1305:                        dirent.d_namlen = 2;
        !          1306:                        dirent.d_type = DT_DIR;
        !          1307:                        break;
        !          1308:                default:
        !          1309:                        dirent.d_fileno = (int32_t)(void *)name_node->de_dnp;
        !          1310:                        dirent.d_namlen = strlen(name_node->de_name);
        !          1311:                        name = name_node->de_name;
        !          1312:                        switch(name_node->de_dnp->dn_type) {
        !          1313:                        case DEV_BDEV:
        !          1314:                                dirent.d_type = DT_BLK;
        !          1315:                                break;
        !          1316:                        case DEV_CDEV:
        !          1317:                                dirent.d_type = DT_CHR;
        !          1318:                                break;
        !          1319:                        case DEV_DIR:
        !          1320:                                dirent.d_type = DT_DIR;
        !          1321:                                break;
        !          1322:                        case DEV_SLNK:
        !          1323:                                dirent.d_type = DT_LNK;
        !          1324:                                break;
        !          1325:                        default:
        !          1326:                                dirent.d_type = DT_UNKNOWN;
        !          1327:                        }
        !          1328:                }
        !          1329: #define        GENERIC_DIRSIZ(dp) \
        !          1330:     ((sizeof (struct dirent) - (MAXNAMLEN+1)) + (((dp)->d_namlen+1 + 3) &~ 3))
        !          1331: 
        !          1332:                reclen = dirent.d_reclen = GENERIC_DIRSIZ(&dirent);
        !          1333: 
        !          1334:                if(pos >= startpos)     /* made it to the offset yet? */
        !          1335:                {
        !          1336:                        if (uio->uio_resid < reclen) /* will it fit? */
        !          1337:                                break;
        !          1338:                        strcpy( dirent.d_name,name);
        !          1339:                        if ((error = uiomove ((caddr_t)&dirent,
        !          1340:                                        dirent.d_reclen, uio)) != 0)
        !          1341:                                break;
        !          1342:                }
        !          1343:                pos += reclen;
        !          1344:                if((nodenumber >1) && name_node)
        !          1345:                        name_node = name_node->de_next;
        !          1346:                nodenumber++;
        !          1347:        }
        !          1348:        DEVFS_UNLOCK(p);
        !          1349:        uio->uio_offset = pos;
        !          1350: 
        !          1351:        return (error);
        !          1352: }
        !          1353: 
        !          1354: 
        !          1355: /*
        !          1356:  */
        !          1357: static int
        !          1358: devfs_readlink(struct vop_readlink_args *ap)
        !          1359:         /*struct vop_readlink_args {
        !          1360:                 struct vnode *a_vp;
        !          1361:                 struct uio *a_uio;
        !          1362:                 struct ucred *a_cred;
        !          1363:         } */
        !          1364: {
        !          1365:        struct vnode *vp = ap->a_vp;
        !          1366:        struct uio *uio = ap->a_uio;
        !          1367:        devnode_t * lnk_node;
        !          1368:        int error = 0;
        !          1369: 
        !          1370:        /*  set up refs to dir */
        !          1371:        lnk_node = VTODN(vp);
        !          1372:        if(lnk_node->dn_type != DEV_SLNK)
        !          1373:                return(EINVAL);
        !          1374:        if ((error = VOP_ACCESS(vp, VREAD, ap->a_cred, NULL)) != 0) { /* XXX */
        !          1375:                return error;
        !          1376:        }
        !          1377:        error = uiomove(lnk_node->dn_typeinfo.Slnk.name, 
        !          1378:                        lnk_node->dn_typeinfo.Slnk.namelen, uio);
        !          1379:        return error;
        !          1380: }
        !          1381: 
        !          1382: static int
        !          1383: devfs_abortop(struct vop_abortop_args *ap)
        !          1384:         /*struct vop_abortop_args {
        !          1385:                 struct vnode *a_dvp;
        !          1386:                 struct componentname *a_cnp;
        !          1387:         } */
        !          1388: {
        !          1389:        if ((ap->a_cnp->cn_flags & (HASBUF | SAVESTART)) == HASBUF) {
        !          1390:                FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
        !          1391:        }
        !          1392:        return 0;
        !          1393: }
        !          1394: 
        !          1395: 
        !          1396: static int
        !          1397: devfs_reclaim(struct vop_reclaim_args *ap)
        !          1398:         /*struct vop_reclaim_args {
        !          1399:                struct vnode *a_vp;
        !          1400:         } */
        !          1401: {
        !          1402:     struct vnode *     vp = ap->a_vp;
        !          1403:     devnode_t *        dnp = VTODN(vp);
        !          1404:     
        !          1405:     if (dnp) {
        !          1406:        /* 
        !          1407:         * do the same as devfs_inactive in case it is not called
        !          1408:         * before us (can that ever happen?)
        !          1409:         */
        !          1410:        dnp->dn_vn = NULL;
        !          1411:        vp->v_data = NULL;
        !          1412:        if (dnp->dn_delete) {
        !          1413:            devnode_free(dnp);
        !          1414:        }
        !          1415:     }
        !          1416:     return(0);
        !          1417: }
        !          1418: 
        !          1419: /*
        !          1420:  * Print out the contents of a /devfs vnode.
        !          1421:  */
        !          1422: static int
        !          1423: devfs_print(struct vop_print_args *ap)
        !          1424:        /*struct vop_print_args {
        !          1425:                struct vnode *a_vp;
        !          1426:        } */
        !          1427: {
        !          1428: 
        !          1429:        return (0);
        !          1430: }
        !          1431: 
        !          1432: /**************************************************************************\
        !          1433: * pseudo ops *
        !          1434: \**************************************************************************/
        !          1435: 
        !          1436: /*
        !          1437:  *
        !          1438:  *     struct vop_inactive_args {
        !          1439:  *             struct vnode *a_vp;
        !          1440:  *             struct proc *a_p;
        !          1441:  *     } 
        !          1442:  */
        !          1443: 
        !          1444: static int
        !          1445: devfs_inactive(struct vop_inactive_args *ap)
        !          1446: {
        !          1447:     struct vnode *     vp = ap->a_vp;
        !          1448:     devnode_t *        dnp = VTODN(vp);
        !          1449:     
        !          1450:     if (dnp) {
        !          1451:        dnp->dn_vn = NULL;
        !          1452:        vp->v_data = NULL;
        !          1453:        if (dnp->dn_delete) {
        !          1454:            devnode_free(dnp);
        !          1455:        }
        !          1456:     }
        !          1457:     VOP_UNLOCK(vp, 0, ap->a_p);
        !          1458:     return (0);
        !          1459: }
        !          1460: 
        !          1461: int
        !          1462: devfs_update(ap)
        !          1463:        struct vop_update_args /* {
        !          1464:                struct vnode *a_vp;
        !          1465:                struct timeval *a_access;
        !          1466:                struct timeval *a_modify;
        !          1467:                int a_waitfor;
        !          1468:        } */ *ap;
        !          1469: {
        !          1470:        register struct fs *fs;
        !          1471:        int error;
        !          1472:        devnode_t * ip;
        !          1473: 
        !          1474:        ip = VTODN(ap->a_vp);
        !          1475:        if (ap->a_vp->v_mount->mnt_flag & MNT_RDONLY) {
        !          1476:                ip->dn_flags &=
        !          1477:                    ~(DN_ACCESS | DN_CHANGE | DN_MODIFIED | DN_UPDATE);
        !          1478:                return (0);
        !          1479:        }
        !          1480:        if ((ip->dn_flags &
        !          1481:            (DN_ACCESS | DN_CHANGE | DN_MODIFIED | DN_UPDATE)) == 0)
        !          1482:                return (0);
        !          1483:        dn_times(ip, time, time);
        !          1484:        return (0);
        !          1485: }
        !          1486: 
        !          1487: /* The following ops are used by directories and symlinks */
        !          1488: int (**devfs_vnodeop_p)();
        !          1489: static struct vnodeopv_entry_desc devfs_vnodeop_entries[] = {
        !          1490:        { &vop_default_desc, vn_default_error },
        !          1491:        { &vop_lookup_desc, devfs_lookup },             /* lookup */
        !          1492:        { &vop_create_desc, err_create },               /* create */
        !          1493:        { &vop_whiteout_desc, err_whiteout },           /* whiteout */
        !          1494:        { &vop_mknod_desc, devfs_mknod },               /* mknod */
        !          1495:        { &vop_open_desc, nop_open },                   /* open */
        !          1496:        { &vop_close_desc, devfs_close },               /* close */
        !          1497:        { &vop_access_desc, devfs_access },             /* access */
        !          1498:        { &vop_getattr_desc, devfs_getattr },           /* getattr */
        !          1499:        { &vop_setattr_desc, devfs_setattr },           /* setattr */
        !          1500:        { &vop_read_desc, devfs_read },                 /* read */
        !          1501:        { &vop_write_desc, devfs_write },               /* write */
        !          1502:        { &vop_lease_desc, nop_lease },                 /* lease */
        !          1503:        { &vop_ioctl_desc, err_ioctl },                 /* ioctl */
        !          1504:        { &vop_select_desc, err_select },               /* select */
        !          1505:        { &vop_revoke_desc, err_revoke },               /* revoke */
        !          1506:        { &vop_mmap_desc, err_mmap },                   /* mmap */
        !          1507:        { &vop_fsync_desc, nop_fsync },                 /* fsync */
        !          1508:        { &vop_seek_desc, err_seek },                   /* seek */
        !          1509:        { &vop_remove_desc, devfs_remove },             /* remove */
        !          1510:        { &vop_link_desc, devfs_link },                 /* link */
        !          1511:        { &vop_rename_desc, devfs_rename },             /* rename */
        !          1512:        { &vop_mkdir_desc, err_mkdir },                 /* mkdir */
        !          1513:        { &vop_rmdir_desc, err_rmdir },                 /* rmdir */
        !          1514:        { &vop_symlink_desc, devfs_symlink },           /* symlink */
        !          1515:        { &vop_readdir_desc, devfs_readdir },           /* readdir */
        !          1516:        { &vop_readlink_desc, devfs_readlink },         /* readlink */
        !          1517:        { &vop_abortop_desc, devfs_abortop },           /* abortop */
        !          1518:        { &vop_inactive_desc, devfs_inactive },         /* inactive */
        !          1519:        { &vop_reclaim_desc, devfs_reclaim },           /* reclaim */
        !          1520:        { &vop_lock_desc, nop_lock },                   /* lock */
        !          1521:        { &vop_unlock_desc, nop_unlock },               /* unlock */
        !          1522:        { &vop_bmap_desc, err_bmap },                   /* bmap */
        !          1523:        { &vop_strategy_desc, err_strategy },           /* strategy */
        !          1524:        { &vop_print_desc, err_print },                 /* print */
        !          1525:        { &vop_islocked_desc, nop_islocked },           /* islocked */
        !          1526:        { &vop_pathconf_desc, err_pathconf },           /* pathconf */
        !          1527:        { &vop_advlock_desc, err_advlock },             /* advlock */
        !          1528:        { &vop_blkatoff_desc, err_blkatoff },           /* blkatoff */
        !          1529:        { &vop_valloc_desc, err_valloc },               /* valloc */
        !          1530:        { &vop_reallocblks_desc, err_reallocblks },     /* reallocblks */
        !          1531:        { &vop_vfree_desc, err_vfree },                 /* vfree */
        !          1532:        { &vop_truncate_desc, err_truncate },           /* truncate */
        !          1533:        { &vop_update_desc, devfs_update },             /* update */
        !          1534:        { &vop_bwrite_desc, err_bwrite },
        !          1535:        { &vop_pagein_desc, err_pagein },               /* Pagein */
        !          1536:        { &vop_pageout_desc, err_pageout },             /* Pageout */
        !          1537:        { &vop_copyfile_desc, err_copyfile },           /* Copyfile */
        !          1538:        { (struct vnodeop_desc*)NULL, (int(*)())NULL }
        !          1539: };
        !          1540: struct vnodeopv_desc devfs_vnodeop_opv_desc =
        !          1541:        { &devfs_vnodeop_p, devfs_vnodeop_entries };
        !          1542: 
        !          1543: /* The following ops are used by the device nodes */
        !          1544: int (**devfs_spec_vnodeop_p)();
        !          1545: static struct vnodeopv_entry_desc devfs_spec_vnodeop_entries[] = {
        !          1546:        { &vop_default_desc, vn_default_error },
        !          1547:        { &vop_lookup_desc, spec_lookup },              /* lookup */
        !          1548:        { &vop_create_desc, spec_create },              /* create */
        !          1549:        { &vop_mknod_desc, spec_mknod },                /* mknod */
        !          1550:        { &vop_open_desc, spec_open },                  /* open */
        !          1551:        { &vop_close_desc, devfsspec_close },           /* close */
        !          1552:        { &vop_access_desc, devfs_access },             /* access */
        !          1553:        { &vop_getattr_desc, devfs_getattr },           /* getattr */
        !          1554:        { &vop_setattr_desc, devfs_setattr },           /* setattr */
        !          1555:        { &vop_read_desc, devfsspec_read },             /* read */
        !          1556:        { &vop_write_desc, devfsspec_write },           /* write */
        !          1557:        { &vop_lease_desc, spec_lease_check },          /* lease */
        !          1558:        { &vop_ioctl_desc, spec_ioctl },                /* ioctl */
        !          1559:        { &vop_select_desc, spec_select },              /* select */
        !          1560:        { &vop_revoke_desc, spec_revoke },              /* revoke */
        !          1561:        { &vop_mmap_desc, spec_mmap },                  /* mmap */
        !          1562:        { &vop_fsync_desc, spec_fsync },                /* fsync */
        !          1563:        { &vop_seek_desc, spec_seek },                  /* seek */
        !          1564:        { &vop_remove_desc, devfs_remove },             /* remove */
        !          1565:        { &vop_link_desc, devfs_link },                 /* link */
        !          1566:        { &vop_rename_desc, spec_rename },              /* rename */
        !          1567:        { &vop_mkdir_desc, spec_mkdir },                /* mkdir */
        !          1568:        { &vop_rmdir_desc, spec_rmdir },                /* rmdir */
        !          1569:        { &vop_symlink_desc, spec_symlink },            /* symlink */
        !          1570:        { &vop_readdir_desc, spec_readdir },            /* readdir */
        !          1571:        { &vop_readlink_desc, spec_readlink },          /* readlink */
        !          1572:        { &vop_abortop_desc, spec_abortop },            /* abortop */
        !          1573:        { &vop_inactive_desc, devfs_inactive },         /* inactive */
        !          1574:        { &vop_reclaim_desc, devfs_reclaim },           /* reclaim */
        !          1575:        { &vop_lock_desc, nop_lock },                   /* lock */
        !          1576:        { &vop_unlock_desc, nop_unlock },               /* unlock */
        !          1577:        { &vop_bmap_desc, spec_bmap },                  /* bmap */
        !          1578:        { &vop_strategy_desc, spec_strategy },          /* strategy */
        !          1579:        { &vop_print_desc, devfs_print },               /* print */
        !          1580:        { &vop_islocked_desc, nop_islocked },           /* islocked */
        !          1581:        { &vop_pathconf_desc, spec_pathconf },          /* pathconf */
        !          1582:        { &vop_advlock_desc, spec_advlock },            /* advlock */
        !          1583:        { &vop_blkatoff_desc, spec_blkatoff },          /* blkatoff */
        !          1584:        { &vop_valloc_desc, spec_valloc },              /* valloc */
        !          1585:        { &vop_reallocblks_desc, spec_reallocblks },    /* reallocblks */
        !          1586:        { &vop_vfree_desc, nop_vfree },                 /* vfree */
        !          1587:        { &vop_truncate_desc, spec_truncate },          /* truncate */
        !          1588:        { &vop_update_desc, devfs_update },             /* update */
        !          1589:        { &vop_bwrite_desc, vn_bwrite },
        !          1590: #ifdef NeXT
        !          1591:        { &vop_devblocksize_desc, spec_devblocksize },  /* devblocksize */
        !          1592: #endif /* NeXT */
        !          1593:        { &vop_pagein_desc, spec_pagein },              /* Pagein */
        !          1594:        { &vop_pageout_desc, spec_pageout },            /* Pageout */
        !          1595:        { &vop_copyfile_desc, err_copyfile },           /* Copyfile */
        !          1596:        { (struct vnodeop_desc*)NULL, (int(*)())NULL }
        !          1597: };
        !          1598: struct vnodeopv_desc devfs_spec_vnodeop_opv_desc =
        !          1599:        { &devfs_spec_vnodeop_p, devfs_spec_vnodeop_entries };
        !          1600: 

unix.superglobalmegacorp.com

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