Annotation of XNU/bsd/miscfs/umapfs/umap_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: /*     $NetBSD: umap_vnops.c,v 1.3 1994/08/19 11:25:42 mycroft Exp $   */
        !            23: 
        !            24: /*
        !            25:  * Copyright (c) 1992, 1993
        !            26:  *     The Regents of the University of California.  All rights reserved.
        !            27:  *
        !            28:  * This code is derived from software donated to Berkeley by
        !            29:  * the UCLA Ficus project.
        !            30:  *
        !            31:  * Redistribution and use in source and binary forms, with or without
        !            32:  * modification, are permitted provided that the following conditions
        !            33:  * are met:
        !            34:  * 1. Redistributions of source code must retain the above copyright
        !            35:  *    notice, this list of conditions and the following disclaimer.
        !            36:  * 2. Redistributions in binary form must reproduce the above copyright
        !            37:  *    notice, this list of conditions and the following disclaimer in the
        !            38:  *    documentation and/or other materials provided with the distribution.
        !            39:  * 3. All advertising materials mentioning features or use of this software
        !            40:  *    must display the following acknowledgement:
        !            41:  *     This product includes software developed by the University of
        !            42:  *     California, Berkeley and its contributors.
        !            43:  * 4. Neither the name of the University nor the names of its contributors
        !            44:  *    may be used to endorse or promote products derived from this software
        !            45:  *    without specific prior written permission.
        !            46:  *
        !            47:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
        !            48:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
        !            49:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
        !            50:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
        !            51:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
        !            52:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
        !            53:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
        !            54:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
        !            55:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
        !            56:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
        !            57:  * SUCH DAMAGE.
        !            58:  *
        !            59:  *     @(#)umap_vnops.c        8.3 (Berkeley) 1/5/94
        !            60:  */
        !            61: 
        !            62: /*
        !            63:  * Umap Layer
        !            64:  */
        !            65: 
        !            66: #include <sys/param.h>
        !            67: #include <sys/systm.h>
        !            68: #include <sys/time.h>
        !            69: #include <sys/types.h>
        !            70: #include <sys/vnode.h>
        !            71: #include <sys/mount.h>
        !            72: #include <sys/namei.h>
        !            73: #include <sys/malloc.h>
        !            74: #include <sys/buf.h>
        !            75: #include <miscfs/umapfs/umap.h>
        !            76: 
        !            77: 
        !            78: int umap_bug_bypass = 0;   /* for debugging: enables bypass printf'ing */
        !            79: 
        !            80: /*
        !            81:  * This is the 10-Apr-92 bypass routine.
        !            82:  * See null_vnops.c:null_bypass for more details.
        !            83:  */ 
        !            84: int
        !            85: umap_bypass(ap)
        !            86:        struct vop_generic_args /* {
        !            87:                struct vnodeop_desc *a_desc;
        !            88:                <other random data follows, presumably>
        !            89:        } */ *ap;
        !            90: {
        !            91:        extern int (**umap_vnodeop_p)();  /* not extern, really "forward" */
        !            92:        struct ucred **credpp = 0, *credp = 0;
        !            93:        struct ucred *savecredp, *savecompcredp = 0;
        !            94:        struct ucred *compcredp = 0;
        !            95:        struct vnode **this_vp_p;
        !            96:        int error;
        !            97:        struct vnode *old_vps[VDESC_MAX_VPS];
        !            98:        struct vnode *vp1 = 0;
        !            99:        struct vnode **vps_p[VDESC_MAX_VPS];
        !           100:        struct vnode ***vppp;
        !           101:        struct vnodeop_desc *descp = ap->a_desc;
        !           102:        int reles, i;
        !           103:        struct componentname **compnamepp = 0;
        !           104: 
        !           105:        if (umap_bug_bypass)
        !           106:                printf ("umap_bypass: %s\n", descp->vdesc_name);
        !           107: 
        !           108: #ifdef SAFETY
        !           109:        /*
        !           110:         * We require at least one vp.
        !           111:         */
        !           112:        if (descp->vdesc_vp_offsets == NULL ||
        !           113:            descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET)
        !           114:                panic ("umap_bypass: no vp's in map.\n");
        !           115: #endif
        !           116: 
        !           117:        /*
        !           118:         * Map the vnodes going in.
        !           119:         * Later, we'll invoke the operation based on
        !           120:         * the first mapped vnode's operation vector.
        !           121:         */
        !           122:        reles = descp->vdesc_flags;
        !           123:        for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
        !           124:                if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
        !           125:                        break;   /* bail out at end of list */
        !           126:                vps_p[i] = this_vp_p = 
        !           127:                        VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[i], ap);
        !           128: 
        !           129:                if (i == 0) {
        !           130:                        vp1 = *vps_p[0];
        !           131:                }
        !           132: 
        !           133:                /*
        !           134:                 * We're not guaranteed that any but the first vnode
        !           135:                 * are of our type.  Check for and don't map any
        !           136:                 * that aren't.  (Must map first vp or vclean fails.)
        !           137:                 */
        !           138: 
        !           139:                if (i && (*this_vp_p)->v_op != umap_vnodeop_p) {
        !           140:                        old_vps[i] = NULL;
        !           141:                } else {
        !           142:                        old_vps[i] = *this_vp_p;
        !           143:                        *(vps_p[i]) = UMAPVPTOLOWERVP(*this_vp_p);
        !           144:                        if (reles & 1)
        !           145:                                VREF(*this_vp_p);
        !           146:                }
        !           147:                        
        !           148:        }
        !           149: 
        !           150:        /*
        !           151:         * Fix the credentials.  (That's the purpose of this layer.)
        !           152:         */
        !           153: 
        !           154:        if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) {
        !           155: 
        !           156:                credpp = VOPARG_OFFSETTO(struct ucred**, 
        !           157:                    descp->vdesc_cred_offset, ap);
        !           158: 
        !           159:                /* Save old values */
        !           160: 
        !           161:                savecredp = *credpp;
        !           162:                if (savecredp != NOCRED)
        !           163:                        *credpp = crdup(savecredp);
        !           164:                credp = *credpp;
        !           165: 
        !           166:                if (umap_bug_bypass && credp->cr_uid != 0)
        !           167:                        printf("umap_bypass: user was %d, group %d\n", 
        !           168:                            credp->cr_uid, credp->cr_gid);
        !           169: 
        !           170:                /* Map all ids in the credential structure. */
        !           171: 
        !           172:                umap_mapids(vp1->v_mount, credp);
        !           173: 
        !           174:                if (umap_bug_bypass && credp->cr_uid != 0)
        !           175:                        printf("umap_bypass: user now %d, group %d\n", 
        !           176:                            credp->cr_uid, credp->cr_gid);
        !           177:        }
        !           178: 
        !           179:        /* BSD often keeps a credential in the componentname structure
        !           180:         * for speed.  If there is one, it better get mapped, too. 
        !           181:         */
        !           182: 
        !           183:        if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) {
        !           184: 
        !           185:                compnamepp = VOPARG_OFFSETTO(struct componentname**, 
        !           186:                    descp->vdesc_componentname_offset, ap);
        !           187: 
        !           188:                savecompcredp = (*compnamepp)->cn_cred;
        !           189:                if (savecompcredp != NOCRED)
        !           190:                        (*compnamepp)->cn_cred = crdup(savecompcredp);
        !           191:                compcredp = (*compnamepp)->cn_cred;
        !           192: 
        !           193:                if (umap_bug_bypass && compcredp->cr_uid != 0)
        !           194:                        printf("umap_bypass: component credit user was %d, group %d\n", 
        !           195:                            compcredp->cr_uid, compcredp->cr_gid);
        !           196: 
        !           197:                /* Map all ids in the credential structure. */
        !           198: 
        !           199:                umap_mapids(vp1->v_mount, compcredp);
        !           200: 
        !           201:                if (umap_bug_bypass && compcredp->cr_uid != 0)
        !           202:                        printf("umap_bypass: component credit user now %d, group %d\n", 
        !           203:                            compcredp->cr_uid, compcredp->cr_gid);
        !           204:        }
        !           205: 
        !           206:        /*
        !           207:         * Call the operation on the lower layer
        !           208:         * with the modified argument structure.
        !           209:         */
        !           210:        error = VCALL(*(vps_p[0]), descp->vdesc_offset, ap);
        !           211: 
        !           212:        /*
        !           213:         * Maintain the illusion of call-by-value
        !           214:         * by restoring vnodes in the argument structure
        !           215:         * to their original value.
        !           216:         */
        !           217:        reles = descp->vdesc_flags;
        !           218:        for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
        !           219:                if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
        !           220:                        break;   /* bail out at end of list */
        !           221:                if (old_vps[i]) {
        !           222:                        *(vps_p[i]) = old_vps[i];
        !           223:                        if (reles & 1)
        !           224:                                vrele(*(vps_p[i]));
        !           225:                };
        !           226:        };
        !           227: 
        !           228:        /*
        !           229:         * Map the possible out-going vpp
        !           230:         * (Assumes that the lower layer always returns
        !           231:         * a VREF'ed vpp unless it gets an error.)
        !           232:         */
        !           233:        if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET &&
        !           234:            !(descp->vdesc_flags & VDESC_NOMAP_VPP) &&
        !           235:            !error) {
        !           236:                if (descp->vdesc_flags & VDESC_VPP_WILLRELE)
        !           237:                        goto out;
        !           238:                vppp = VOPARG_OFFSETTO(struct vnode***,
        !           239:                                 descp->vdesc_vpp_offset, ap);
        !           240:                error = umap_node_create(old_vps[0]->v_mount, **vppp, *vppp);
        !           241:        };
        !           242: 
        !           243:  out:
        !           244:        /* 
        !           245:         * Free duplicate cred structure and restore old one.
        !           246:         */
        !           247:        if (descp->vdesc_cred_offset != VDESC_NO_OFFSET) {
        !           248:                if (umap_bug_bypass && credp && credp->cr_uid != 0)
        !           249:                        printf("umap_bypass: returning-user was %d\n",
        !           250:                                        credp->cr_uid);
        !           251: 
        !           252:                if (savecredp != NOCRED) {
        !           253:                        if (credp != NOCRED)
        !           254:                                crfree(credp);
        !           255:                        *credpp = savecredp;
        !           256:                        if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0)
        !           257:                                printf("umap_bypass: returning-user now %d\n\n", 
        !           258:                                    savecredp->cr_uid);
        !           259:                }
        !           260:        }
        !           261: 
        !           262:        if (descp->vdesc_componentname_offset != VDESC_NO_OFFSET) {
        !           263:                if (umap_bug_bypass && compcredp && compcredp->cr_uid != 0)
        !           264:                        printf("umap_bypass: returning-component-user was %d\n", 
        !           265:                                compcredp->cr_uid);
        !           266: 
        !           267:                if (savecompcredp != NOCRED) {
        !           268:                        if (compcredp != NOCRED)
        !           269:                                crfree(compcredp);
        !           270:                        (*compnamepp)->cn_cred = savecompcredp;
        !           271:                        if (umap_bug_bypass && credpp && (*credpp)->cr_uid != 0)
        !           272:                                printf("umap_bypass: returning-component-user now %d\n", 
        !           273:                                    savecompcredp->cr_uid);
        !           274:                }
        !           275:        }
        !           276: 
        !           277:        return (error);
        !           278: }
        !           279: 
        !           280: 
        !           281: /*
        !           282:  *  We handle getattr to change the fsid.
        !           283:  */
        !           284: int
        !           285: umap_getattr(ap)
        !           286:        struct vop_getattr_args /* {
        !           287:                struct vnode *a_vp;
        !           288:                struct vattr *a_vap;
        !           289:                struct ucred *a_cred;
        !           290:                struct proc *a_p;
        !           291:        } */ *ap;
        !           292: {
        !           293:        uid_t uid;
        !           294:        gid_t gid;
        !           295:        int error, tmpid, nentries, gnentries;
        !           296:        uid_t (*mapdata)[2];
        !           297:        gid_t (*gmapdata)[2];
        !           298:        struct vnode **vp1p;
        !           299:        struct vnodeop_desc *descp = ap->a_desc;
        !           300: 
        !           301:        if (error = umap_bypass(ap))
        !           302:                return (error);
        !           303:        /* Requires that arguments be restored. */
        !           304:        ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
        !           305: 
        !           306:        /*
        !           307:         * Umap needs to map the uid and gid returned by a stat
        !           308:         * into the proper values for this site.  This involves
        !           309:         * finding the returned uid in the mapping information,
        !           310:         * translating it into the uid on the other end,
        !           311:         * and filling in the proper field in the vattr
        !           312:         * structure pointed to by ap->a_vap.  The group
        !           313:         * is easier, since currently all groups will be
        !           314:         * translate to the NULLGROUP.
        !           315:         */
        !           316: 
        !           317:        /* Find entry in map */
        !           318: 
        !           319:        uid = ap->a_vap->va_uid;
        !           320:        gid = ap->a_vap->va_gid;
        !           321:        if (umap_bug_bypass)
        !           322:                printf("umap_getattr: mapped uid = %d, mapped gid = %d\n", uid, 
        !           323:                    gid);
        !           324: 
        !           325:        vp1p = VOPARG_OFFSETTO(struct vnode**, descp->vdesc_vp_offsets[0], ap);
        !           326:        nentries =  MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_nentries;
        !           327:        mapdata =  (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_mapdata);
        !           328:        gnentries =  MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gnentries;
        !           329:        gmapdata =  (MOUNTTOUMAPMOUNT((*vp1p)->v_mount)->info_gmapdata);
        !           330: 
        !           331:        /* Reverse map the uid for the vnode.  Since it's a reverse
        !           332:                map, we can't use umap_mapids() to do it. */
        !           333: 
        !           334:        tmpid = umap_reverse_findid(uid, mapdata, nentries);
        !           335: 
        !           336:        if (tmpid != -1) {
        !           337:                ap->a_vap->va_uid = (uid_t) tmpid;
        !           338:                if (umap_bug_bypass)
        !           339:                        printf("umap_getattr: original uid = %d\n", uid);
        !           340:        } else 
        !           341:                ap->a_vap->va_uid = (uid_t) NOBODY;
        !           342: 
        !           343:        /* Reverse map the gid for the vnode. */
        !           344: 
        !           345:        tmpid = umap_reverse_findid(gid, gmapdata, gnentries);
        !           346: 
        !           347:        if (tmpid != -1) {
        !           348:                ap->a_vap->va_gid = (gid_t) tmpid;
        !           349:                if (umap_bug_bypass)
        !           350:                        printf("umap_getattr: original gid = %d\n", gid);
        !           351:        } else
        !           352:                ap->a_vap->va_gid = (gid_t) NULLGROUP;
        !           353:        
        !           354:        return (0);
        !           355: }
        !           356: 
        !           357: int
        !           358: umap_inactive(ap)
        !           359:        struct vop_inactive_args /* {
        !           360:                struct vnode *a_vp;
        !           361:        } */ *ap;
        !           362: {
        !           363:        /*
        !           364:         * Do nothing (and _don't_ bypass).
        !           365:         * Wait to vrele lowervp until reclaim,
        !           366:         * so that until then our umap_node is in the
        !           367:         * cache and reusable.
        !           368:         *
        !           369:         */
        !           370:        return (0);
        !           371: }
        !           372: 
        !           373: int
        !           374: umap_reclaim(ap)
        !           375:        struct vop_reclaim_args /* {
        !           376:                struct vnode *a_vp;
        !           377:        } */ *ap;
        !           378: {
        !           379:        struct vnode *vp = ap->a_vp;
        !           380:        struct umap_node *xp = VTOUMAP(vp);
        !           381:        struct vnode *lowervp = xp->umap_lowervp;
        !           382:        
        !           383:        /* After this assignment, this node will not be re-used. */
        !           384:        xp->umap_lowervp = NULL;
        !           385:        LIST_REMOVE(xp, umap_hash);
        !           386:        FREE(vp->v_data, M_TEMP);
        !           387:        vp->v_data = NULL;
        !           388:        vrele(lowervp);
        !           389:        return (0);
        !           390: }
        !           391: 
        !           392: int
        !           393: umap_strategy(ap)
        !           394:        struct vop_strategy_args /* {
        !           395:                struct buf *a_bp;
        !           396:        } */ *ap;
        !           397: {
        !           398:        struct buf *bp = ap->a_bp;
        !           399:        int error;
        !           400:        struct vnode *savedvp;
        !           401: 
        !           402:        savedvp = bp->b_vp;
        !           403:        bp->b_vp = UMAPVPTOLOWERVP(bp->b_vp);
        !           404: 
        !           405:        error = VOP_STRATEGY(ap->a_bp);
        !           406: 
        !           407:        bp->b_vp = savedvp;
        !           408: 
        !           409:        return (error);
        !           410: }
        !           411: 
        !           412: int
        !           413: umap_bwrite(ap)
        !           414:        struct vop_bwrite_args /* {
        !           415:                struct buf *a_bp;
        !           416:        } */ *ap;
        !           417: {
        !           418:        struct buf *bp = ap->a_bp;
        !           419:        int error;
        !           420:        struct vnode *savedvp;
        !           421: 
        !           422:        savedvp = bp->b_vp;
        !           423:        bp->b_vp = UMAPVPTOLOWERVP(bp->b_vp);
        !           424: 
        !           425:        error = VOP_BWRITE(ap->a_bp);
        !           426: 
        !           427:        bp->b_vp = savedvp;
        !           428: 
        !           429:        return (error);
        !           430: }
        !           431: 
        !           432: 
        !           433: int
        !           434: umap_print(ap)
        !           435:        struct vop_print_args /* {
        !           436:                struct vnode *a_vp;
        !           437:        } */ *ap;
        !           438: {
        !           439:        struct vnode *vp = ap->a_vp;
        !           440:        printf("\ttag VT_UMAPFS, vp=%x, lowervp=%x\n", vp, UMAPVPTOLOWERVP(vp));
        !           441:        return (0);
        !           442: }
        !           443: 
        !           444: int
        !           445: umap_rename(ap)
        !           446:        struct vop_rename_args  /* {
        !           447:                struct vnode *a_fdvp;
        !           448:                struct vnode *a_fvp;
        !           449:                struct componentname *a_fcnp;
        !           450:                struct vnode *a_tdvp;
        !           451:                struct vnode *a_tvp;
        !           452:                struct componentname *a_tcnp;
        !           453:        } */ *ap;
        !           454: {
        !           455:        int error;
        !           456:        struct componentname *compnamep;
        !           457:        struct ucred *compcredp, *savecompcredp;
        !           458:        struct vnode *vp;
        !           459: 
        !           460:        /*
        !           461:         * Rename is irregular, having two componentname structures.
        !           462:         * We need to map the cre in the second structure,
        !           463:         * and then bypass takes care of the rest.
        !           464:         */
        !           465: 
        !           466:        vp = ap->a_fdvp;
        !           467:        compnamep = ap->a_tcnp;
        !           468:        compcredp = compnamep->cn_cred;
        !           469: 
        !           470:        savecompcredp = compcredp;
        !           471:        compcredp = compnamep->cn_cred = crdup(savecompcredp);
        !           472: 
        !           473:        if (umap_bug_bypass && compcredp->cr_uid != 0)
        !           474:                printf("umap_rename: rename component credit user was %d, group %d\n", 
        !           475:                    compcredp->cr_uid, compcredp->cr_gid);
        !           476: 
        !           477:        /* Map all ids in the credential structure. */
        !           478: 
        !           479:        umap_mapids(vp->v_mount, compcredp);
        !           480: 
        !           481:        if (umap_bug_bypass && compcredp->cr_uid != 0)
        !           482:                printf("umap_rename: rename component credit user now %d, group %d\n", 
        !           483:                    compcredp->cr_uid, compcredp->cr_gid);
        !           484: 
        !           485:        error = umap_bypass(ap);
        !           486:        
        !           487:        /* Restore the additional mapped componentname cred structure. */
        !           488: 
        !           489:        crfree(compcredp);
        !           490:        compnamep->cn_cred = savecompcredp;
        !           491: 
        !           492:        return error;
        !           493: }
        !           494: 
        !           495: /*
        !           496:  * Global vfs data structures
        !           497:  */
        !           498: /*
        !           499:  * XXX - strategy, bwrite are hand coded currently.  They should
        !           500:  * go away with a merged buffer/block cache.
        !           501:  *
        !           502:  */
        !           503: int (**umap_vnodeop_p)();
        !           504: struct vnodeopv_entry_desc umap_vnodeop_entries[] = {
        !           505:        { &vop_default_desc, umap_bypass },
        !           506: 
        !           507:        { &vop_getattr_desc, umap_getattr },
        !           508:        { &vop_inactive_desc, umap_inactive },
        !           509:        { &vop_reclaim_desc, umap_reclaim },
        !           510:        { &vop_print_desc, umap_print },
        !           511:        { &vop_rename_desc, umap_rename },
        !           512: 
        !           513:        { &vop_strategy_desc, umap_strategy },
        !           514:        { &vop_bwrite_desc, umap_bwrite },
        !           515: 
        !           516:        { (struct vnodeop_desc*) NULL, (int(*)()) NULL }
        !           517: };
        !           518: struct vnodeopv_desc umap_vnodeop_opv_desc =
        !           519:        { &umap_vnodeop_p, umap_vnodeop_entries };

unix.superglobalmegacorp.com

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