Annotation of researchv10no/netfs/serv/zarf.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * simple unix filesystem interface;
                      3:  * access local files through system calls
                      4:  */
                      5: 
                      6: #include <sys/types.h>
                      7: #include <sys/stat.h>
                      8: #include <stdio.h>
                      9: #include <errno.h>
                     10: #include <rf.h>
                     11: #include "zarf.h"
                     12: 
                     13: extern int errno;
                     14: 
                     15: static Rfile *rootf;
                     16: static int servuid, servgid;
                     17: 
                     18: char rootname[200];    /* arbitrarily large */
                     19: extern Namemap *exulist, *exglist;
                     20: 
                     21: #define        rfmode(m)       (m)
                     22: #define        fsmode(m)       (m)
                     23: #define        FSPERM  07777   /* just the permission bits */
                     24: 
                     25: static int clientdev();
                     26: static int maxopen();
                     27: static int samefile();
                     28: 
                     29: char *malloc();
                     30: long lseek();
                     31: 
                     32: /*
                     33:  * init:
                     34:  * return the root
                     35:  */
                     36: 
                     37: Rfile *
                     38: fsinit(argc, argv)
                     39: int argc;
                     40: char **argv;
                     41: {
                     42:        register Rfile *f;
                     43:        char *passwd = "/etc/passwd";
                     44:        char *group = "/etc/group";
                     45:        FILE *fp;
                     46:        int foundex;
                     47: 
                     48:        foundex = 0;
                     49:        while (--argc > 0) {
                     50:                if (**++argv != '-')
                     51:                        continue;       /* skip unknown args */
                     52:                switch (argv[0][1]) {
                     53:                case 'd':
                     54:                        if (--argc <= 0) {
                     55:                                rflog("-d: missing arg\n");
                     56:                                continue;
                     57:                        }
                     58:                        rfdebug = atoi(*++argv);
                     59:                        break;
                     60: 
                     61:                case 'p':
                     62:                        if (--argc <= 0) {
                     63:                                rflog("-p: missing arg\n");
                     64:                                continue;
                     65:                        }
                     66:                        passwd = *++argv;
                     67:                        break;
                     68: 
                     69:                case 'g':
                     70:                        if (--argc <= 0) {
                     71:                                rflog("-g: missing arg\n");
                     72:                                continue;
                     73:                        }
                     74:                        group = *++argv;
                     75:                        break;
                     76: 
                     77:                case 'r':
                     78:                        if (--argc <= 0) {
                     79:                                rflog("-r: missing arg\n");
                     80:                                continue;
                     81:                        }
                     82:                        strcpy(rootname, *++argv);
                     83:                        break;
                     84:                case 'e':
                     85:                        if (--argc <= 0) {
                     86:                                rflog("-e: missing arg\n");
                     87:                                continue;
                     88:                        }
                     89:                        if (foundex == 0 && (fp = fopen(*++argv, "r")) != NULL) {
                     90:                                foundex = rdexcept(fp);
                     91:                                fclose(fp);
                     92:                        }
                     93:                        break;
                     94: 
                     95:                default:
                     96:                        rflog("unknown flag %s\n", *argv);
                     97:                        break;
                     98:                }
                     99:        }
                    100:        rfuidmap = rfmkidmap(passwd, exulist);
                    101:        rfgidmap = rfmkidmap(group, exglist);
                    102:        if ((f = (Rfile *)malloc(sizeof(Rfile))) == NULL)
                    103:                return (NULL);
                    104:        if ((f->fs = malloc(sizeof(Fsfile))) == NULL) {
                    105:                free((char *)f);
                    106:                return (NULL);
                    107:        }
                    108:        if ((fsp(f)->name = malloc(strlen(rootname)+1)) == NULL) {
                    109:                free(f->fs);
                    110:                free((char *)f);
                    111:                return (NULL);
                    112:        }
                    113:        if (rootname[0] == 0)
                    114:                strcpy(rootname, "/");
                    115:        strcpy(fsp(f)->name, rootname);
                    116:        fsp(f)->fd = -1;
                    117:        fsp(f)->flags = WONTREAD|WONTWRITE|NOSTAT;
                    118:        fsstat(f);
                    119:        maxopen(f, 0);
                    120:        rootf = f;
                    121:        umask(0);
                    122:        servuid = geteuid();
                    123:        servgid = getegid();
                    124:        setgid(servgid);        /* to avoid inconsistencies */
                    125:        setuid(servuid);
                    126:        return (f);
                    127: }
                    128: 
                    129: Rfile *
                    130: fswalk(df, name)
                    131: Rfile *df;
                    132: char *name;
                    133: {
                    134:        register Rfile *f;
                    135:        char *nname;
                    136: 
                    137:        if (rfdebug)
                    138:                rflog("walk %d,%ld '%s' '%s'\n", df->dev, df->ino, fsp(df)->name, name);
                    139:        if ((f = (Rfile *)malloc(sizeof(Rfile))) == NULL) {
                    140:                rflog("fswalk: no mem\n");
                    141:                fserrno = RFEINVAL;
                    142:                return (NULL);
                    143:        }
                    144:        if ((f->fs = malloc(sizeof(Fsfile))) == NULL) {
                    145:                free((char *)f);
                    146:                rflog("fswalk: no mem\n");
                    147:                fserrno = RFEINVAL;
                    148:                return (NULL);
                    149:        }
                    150:        if ((nname = malloc(strlen(fsp(df)->name) + strlen(name) + 2)) == NULL) {
                    151:                free(f->fs);
                    152:                free((char *)f);
                    153:                rflog("fswalk: no mem\n");
                    154:                fserrno = RFEINVAL;
                    155:                return (NULL);
                    156:        }
                    157:        fsp(f)->name = nname;
                    158:        strcpy(nname, fsp(df)->name);
                    159:        strcat(nname, "/");
                    160:        strcat(nname, name);
                    161:        fsp(f)->fd = -1;
                    162:        fsp(f)->flags = WONTREAD|WONTWRITE|NOSTAT;
                    163:        if (fsstat(f) < 0)
                    164:                goto bad;
                    165:        /*
                    166:         * hack to avoid hanging in open:
                    167:         * don't open yet unless regular file (not device)
                    168:         * and nonzero link count (not Research mounted stream)
                    169:         * better not to defer open for ordinary files,
                    170:         * in case someone unlinks the name we know
                    171:         */
                    172:        if ((fsmode(f->mode) & S_IFMT) == S_IFREG && f->nlink != 0)
                    173:                maxopen(f, 0);
                    174:        if (df == rootf) {              /* magic for . and .., mostly for pwd */
                    175: #if NOTDEF
                    176:                if (name[0] == 0 || strcmp(name, ".") == 0) {
                    177:                        close(fsp(f)->fd);
                    178:                        free(nname);
                    179:                        free(f->fs);
                    180:                        free((char *)f);
                    181:                        return (df);
                    182:                }
                    183: #endif
                    184:                if (strcmp(name, "..") == 0) {
                    185:                        close(fsp(f)->fd);
                    186:                        fserrno = 0;            /* pseudo-error: popped out of root */
                    187:                        goto bad;
                    188:                }
                    189:        }
                    190:        return (f);
                    191: 
                    192: bad:
                    193:        free(nname);
                    194:        free(f->fs);
                    195:        free((char *)f);
                    196:        return (NULL);
                    197: }
                    198: 
                    199: Rfile *
                    200: fscreate(df, name, mode, uid, gid)
                    201: Rfile *df;
                    202: char *name;
                    203: int mode;
                    204: int uid, gid;
                    205: {
                    206:        register Rfile *f;
                    207:        char *nname;
                    208: 
                    209:        if ((f = (Rfile *)malloc(sizeof(Rfile))) == NULL) {
                    210:                rflog("fscreate: no mem\n");
                    211:                fserrno = RFEINVAL;
                    212:                return (NULL);
                    213:        }
                    214:        if ((f->fs = malloc(sizeof(Fsfile))) == NULL) {
                    215:                free((char *)f);
                    216:                rflog("fscreate: no mem\n");
                    217:                fserrno = RFEINVAL;
                    218:                return (NULL);
                    219:        }
                    220:        if ((nname = malloc(strlen(fsp(df)->name) + strlen(name) + 2)) == NULL) {
                    221:                free(f->fs);
                    222:                free((char *)f);
                    223:                rflog("fscreate: no mem\n");
                    224:                fserrno = RFEINVAL;
                    225:                return (NULL);
                    226:        }
                    227:        fsp(f)->name = nname;
                    228:        strcpy(nname, fsp(df)->name);
                    229:        strcat(nname, "/");
                    230:        strcat(nname, name);
                    231:        if ((fsp(f)->fd = creat(nname, fsmode(mode))) < 0) {
                    232:                fserrno = errno;
                    233:                goto bad;
                    234:        }
                    235:        fsp(f)->flags = WONTREAD|NOSTAT;
                    236:        if (fsstat(f) < 0) {
                    237:                rflog("fscreate: can't stat %s, err %d\n", nname, errno);
                    238:                fserrno = RFEINVAL;
                    239:                close(fsp(f)->fd);
                    240:                goto bad;
                    241:        }
                    242:        if (uid != f->uid || gid != f->gid) {
                    243:                if (chown(nname, uid, gid) >= 0) {
                    244:                        f->uid = uid;           /* stat would be too slow */
                    245:                        f->gid = gid;
                    246:                } else if (servuid == 0) {      /* if not root, ignore error */
                    247:                        rflog("fscreate: can't set owner %s err %d\n", nname, errno);
                    248:                        fserrno = errno;
                    249:                        close(fsp(f)->fd);
                    250:                        goto bad;
                    251:                }
                    252:        }
                    253:        return (f);
                    254: 
                    255: bad:
                    256:        free(nname);
                    257:        free(f->fs);
                    258:        free((char *)f);
                    259:        return (NULL);
                    260: }
                    261: 
                    262: int
                    263: fsmkdir(df, name, mode, uid, gid)
                    264: register Rfile *df;
                    265: char *name;
                    266: int mode;
                    267: int uid, gid;
                    268: {
                    269:        char *nname;
                    270:        struct stat st;
                    271: 
                    272:        if ((nname = malloc(strlen(fsp(df)->name) + strlen(name) + 2)) == NULL) {
                    273:                rflog("fsmkdir: no mem\n");
                    274:                fserrno = RFEINVAL;
                    275:                return (-1);
                    276:        }
                    277:        strcpy(nname, fsp(df)->name);
                    278:        strcat(nname, "/");
                    279:        strcat(nname, name);
                    280:        if (mkdir(nname, fsmode(mode)) < 0) {
                    281:                fserrno = errno;
                    282:                free(nname);
                    283:                return (-1);
                    284:        }
                    285:        if (stat(nname, &st) < 0
                    286:        ||  (uid != st.st_uid || gid != st.st_gid) && chown(nname, uid, gid) < 0) {
                    287:                rflog("fsmkdir: can't set owner %s err %d\n", nname, errno);
                    288:                fserrno = errno;
                    289:                rmdir(nname);   /* maybe this will work */
                    290:                free(nname);
                    291:                return (-1);
                    292:        }
                    293:        free(nname);
                    294:        return (0);
                    295: }
                    296: 
                    297: int
                    298: fsrmdir(df, name)
                    299: register Rfile *df;
                    300: char *name;
                    301: {
                    302:        char *nname;
                    303: 
                    304:        if ((nname = malloc(strlen(fsp(df)->name) + strlen(name) + 2)) == NULL) {
                    305:                rflog("fsrmdir: no mem\n");
                    306:                fserrno = RFEINVAL;
                    307:                return (-1);
                    308:        }
                    309:        strcpy(nname, fsp(df)->name);
                    310:        strcat(nname, "/");
                    311:        strcat(nname, name);
                    312:        if (rmdir(nname) < 0) {
                    313:                fserrno = errno;
                    314:                free(nname);
                    315:                return (-1);
                    316:        }
                    317:        free(nname);
                    318:        return (0);
                    319: }
                    320: 
                    321: int
                    322: fsdelete(df, name)
                    323: register Rfile *df;
                    324: char *name;
                    325: {
                    326:        char *nname;
                    327: 
                    328:        if ((nname = malloc(strlen(fsp(df)->name) + strlen(name) + 2)) == NULL) {
                    329:                rflog("fsdelete: no mem\n");
                    330:                fserrno = RFEINVAL;
                    331:                return (-1);
                    332:        }
                    333:        strcpy(nname, fsp(df)->name);
                    334:        strcat(nname, "/");
                    335:        strcat(nname, name);
                    336:        if (unlink(nname) < 0) {
                    337:                fserrno = errno;
                    338:                free(nname);
                    339:                return (-1);
                    340:        }
                    341:        free(nname);
                    342:        return (0);
                    343: }
                    344: 
                    345: int
                    346: fslink(df, name, f)
                    347: register Rfile *df, *f;
                    348: char *name;
                    349: {
                    350:        char *nname;
                    351: 
                    352:        if ((nname = malloc(strlen(fsp(df)->name) + strlen(name) + 2)) == NULL) {
                    353:                rflog("fslink: no mem\n");
                    354:                fserrno = RFEINVAL;
                    355:                return (-1);
                    356:        }
                    357:        if (samefile(f) == 0) {
                    358:                rflog("fslink: wrong file %s\n", fsp(f)->name);
                    359:                fserrno = RFEINVAL;
                    360:                return (-1);
                    361:        }
                    362:        strcpy(nname, fsp(df)->name);
                    363:        strcat(nname, "/");
                    364:        strcat(nname, name);
                    365:        if (link(fsp(f)->name, nname) < 0) {
                    366:                fserrno = errno;
                    367:                free(nname);
                    368:                return (-1);
                    369:        }
                    370:        free(nname);
                    371:        return (0);
                    372: }
                    373: 
                    374: int
                    375: fsdone(f)
                    376: register Rfile *f;
                    377: {
                    378: 
                    379:        if (fsp(f)->flags & DIDDIR)
                    380:                dodircleanup(f);
                    381:        close(fsp(f)->fd);
                    382:        free(fsp(f)->name);
                    383:        free(f->fs);
                    384:        free((char *)f);
                    385:        return (0);
                    386: }
                    387: 
                    388: /*
                    389:  * this should really use fchmod and fchown, but
                    390:  * (1) some systems don't have them;
                    391:  * (2) there must be code to call chmod anyway,
                    392:  * else how can we chmod a file in mode 0 if not super-user?
                    393:  * (3) there's no futime anyway
                    394:  *
                    395:  * the test in front is to skip samefile (and perhaps a spurious
                    396:  * error message) if nothing is really changing;
                    397:  * usually this just means ta or tm is being updated to the
                    398:  * value the local system has already stored after read or write
                    399:  */
                    400: 
                    401: int
                    402: fsupdate(f, nf)
                    403: register Rfile *f, *nf;
                    404: {
                    405:        long ft[2];
                    406:        int nfd;
                    407:        int status;
                    408: 
                    409:        if ((fsmode(nf->mode)&FSPERM) == (fsmode(f->mode)&FSPERM)
                    410:        &&  nf->uid == f->uid && nf->gid == f->gid
                    411:        &&  nf->ta == f->ta && nf->tm == f->tm
                    412:        &&  nf->size == f->size)
                    413:                return (0);
                    414:        if (samefile(f) == 0) {
                    415:                rflog("fsupdate: wrong file %s\n", fsp(f)->name);
                    416:                fserrno = RFEINVAL;
                    417:                return (-1);
                    418:        }
                    419:        status = -1;
                    420:        if ((fsmode(nf->mode)&FSPERM) != (fsmode(f->mode)&FSPERM)) {
                    421:                if (chmod(fsp(f)->name, fsmode(nf->mode) & FSPERM) < 0) {
                    422:                        fserrno = errno;
                    423:                        goto out;
                    424:                }
                    425:        }
                    426:        if (nf->ta || nf->tm) {
                    427:                ft[0] = nf->ta;
                    428:                ft[1] = nf->tm;
                    429:                if (utime(fsp(f)->name, ft) < 0) {
                    430:                        fserrno = errno;
                    431:                        goto out;
                    432:                }
                    433:        }
                    434:        if (f->uid != nf->uid || f->gid != nf->gid) {
                    435:                if (chown(fsp(f)->name, nf->uid, nf->gid) < 0) {
                    436:                        fserrno = errno;
                    437:                        goto out;
                    438:                }
                    439:        }
                    440:        if (f->size != nf->size && nf->size == 0) {
                    441:                if ((nfd = creat(fsp(f)->name, 0)) < 0) {
                    442:                        fserrno = errno;
                    443:                        goto out;
                    444:                }
                    445:                close(nfd);
                    446:        }
                    447:        status = 0;
                    448: out:
                    449:        fsstat(f);
                    450:        return (status);
                    451: }
                    452: 
                    453: fsstat(f)
                    454: register Rfile *f;
                    455: {
                    456:        struct stat st;
                    457: 
                    458:        if (fsp(f)->fd >= 0 && fstat(fsp(f)->fd, &st) < 0) {
                    459:                fserrno = errno;
                    460:                return (-1);
                    461:        }
                    462:        else if (stat(fsp(f)->name, &st) < 0) {
                    463:                fserrno = errno;
                    464:                return (-1);
                    465:        }
                    466:        if (fsp(f)->flags & NOSTAT)
                    467:                fsp(f)->flags &=~ NOSTAT;
                    468:        else if (clientdev(st.st_dev) != f->dev || st.st_ino != f->ino) {
                    469:                rflog("fsstat: wrong file %s\n", fsp(f)->name);
                    470:                fserrno = RFEINVAL;
                    471:                return (-1);
                    472:        }
                    473:        f->ino = st.st_ino;
                    474:        f->dev = clientdev(st.st_dev);
                    475:        f->mode = rfmode(st.st_mode);
                    476:        switch(st.st_mode & S_IFMT) {
                    477:        case S_IFDIR:
                    478:                f->type = RFTDIR;
                    479:                break;
                    480: 
                    481:        case S_IFCHR:
                    482:        case S_IFBLK:
                    483:                f->mode &=~ 07777;      /* deny access to devices */
                    484:                /* fall through */
                    485:        case S_IFREG:
                    486:                f->type = RFTREG;
                    487:                break;
                    488:        }
                    489:        f->nlink = st.st_nlink;
                    490:        f->uid = st.st_uid;
                    491:        f->gid = st.st_gid;
                    492:        f->size = st.st_size;
                    493:        f->ta = st.st_atime;
                    494:        f->tm = st.st_mtime;
                    495:        f->tc = st.st_ctime;
                    496:        return (0);
                    497: }
                    498: 
                    499: int
                    500: fsread(f, off, buf, len)
                    501: register Rfile *f;
                    502: long off;
                    503: char *buf;
                    504: int len;
                    505: {
                    506:        register int n;
                    507: 
                    508:        if (fsp(f)->flags & WONTREAD)
                    509:                maxopen(f, 0);
                    510:        if (fsp(f)->flags & CANTREAD) {
                    511:                fserrno = RFEACCES;     /* probably */
                    512:                return (-1);
                    513:        }
                    514:        lseek(fsp(f)->fd, off, 0);
                    515:        if ((n = read(fsp(f)->fd, buf, len)) < 0)
                    516:                fserrno = errno;
                    517:        return (n);
                    518: }
                    519: 
                    520: int
                    521: fsdirread(f, off, buf, len, offp)
                    522: register Rfile *f;
                    523: long off;
                    524: char *buf;
                    525: int len;
                    526: long *offp;
                    527: {
                    528: 
                    529:        if (fsp(f)->flags & WONTREAD)
                    530:                maxopen(f, 0);
                    531:        if (fsp(f)->flags & CANTREAD) {
                    532:                fserrno = RFEACCES;     /* probably */
                    533:                return (-1);
                    534:        }
                    535:        fsp(f)->flags |= DIDDIR;
                    536:        return (dodirread(f, buf, len, off, offp));
                    537: }
                    538: 
                    539: int
                    540: fswrite(f, off, buf, len)
                    541: register Rfile *f;
                    542: long off;
                    543: char *buf;
                    544: int len;
                    545: {
                    546:        register int n;
                    547: 
                    548:        if (fsp(f)->flags & WONTWRITE)
                    549:                maxopen(f, 1);
                    550:        if (fsp(f)->flags & CANTWRITE) {
                    551:                fserrno = RFEACCES;
                    552:                return (-1);
                    553:        }
                    554:        lseek(fsp(f)->fd, off, 0);
                    555:        if ((n = write(fsp(f)->fd, buf, len)) < 0)
                    556:                fserrno = errno;
                    557:        else if (off + len > f->size)
                    558:                f->size = off + len;    /* should stat, but too slow */
                    559:        return (n);
                    560: }
                    561: 
                    562: /*
                    563:  * turn a client st_dev into an rf minor device
                    564:  * exactly eight bits allowed
                    565:  */
                    566: 
                    567: #define        MAXDEVS 255
                    568: 
                    569: int maxdev = 0;
                    570: dev_t devtab[MAXDEVS];
                    571: 
                    572: static int
                    573: clientdev(d)
                    574: register dev_t d;
                    575: {
                    576:        register int i;
                    577: 
                    578:        for (i = 0; i < maxdev; i++)
                    579:                if (devtab[i] == d)
                    580:                        return (i);
                    581:        if (i >= MAXDEVS) {
                    582:                rflog("too many devs, can't map %x\n", d);
                    583:                return (MAXDEVS);
                    584:        }
                    585:        devtab[i] = d;
                    586:        maxdev = i + 1;
                    587:        return (i);
                    588: }
                    589: 
                    590: /*
                    591:  * open a file as broadly as possible
                    592:  * to avoid `text busy,' don't open for write
                    593:  * unless specifically requested
                    594:  * or unless file has no execute permissions (hack)
                    595:  * directories usually have execute permissions (further hack)
                    596:  */
                    597: 
                    598: #define        ANYEXEC ((RFPEX<<RFPOWNER)|(RFPEX<<RFPGROUP)|(RFPEX<<RFPOTHER))
                    599: 
                    600: static int
                    601: maxopen(f, mustwrite)
                    602: register Rfile *f;
                    603: int mustwrite;
                    604: {
                    605:        register Fsfile *fs;
                    606:        int fd;
                    607:        struct stat st;
                    608:        register int flags;
                    609: 
                    610:        fd = -1;
                    611:        fs = fsp(f);
                    612:        flags = fs->flags;
                    613:        if ((flags & (WONTREAD|WONTWRITE)) == 0)
                    614:                return;
                    615:        if ((flags & (CANTREAD|CANTWRITE)) == 0
                    616:        &&  mustwrite || (f->mode & ANYEXEC) == 0) {
                    617:                if ((fd = open(fs->name, 2)) >= 0)
                    618:                        flags &=~ (WONTREAD|WONTWRITE);
                    619:        }
                    620:        if ((flags & (CANTREAD|WONTREAD)) == WONTREAD) {
                    621:                flags &=~ WONTREAD;
                    622:                if ((fd = open(fs->name, 0)) < 0)
                    623:                        flags |= CANTREAD;
                    624:        }
                    625:        if (mustwrite && (flags & (CANTWRITE|WONTWRITE)) == WONTWRITE) {
                    626:                flags &=~ WONTWRITE;
                    627:                if ((fd = open(fs->name, 1)) < 0)
                    628:                        flags |= CANTWRITE;
                    629:        }
                    630:        if (fd >= 0) {  /* if we opened something */
                    631:                if ((flags & NOSTAT) == 0) {
                    632:                        if (fstat(fd, &st) < 0) {
                    633:                                rflog("maxopen: fstat %s err %d\n", fs->name, errno);
                    634:                                close(fd);
                    635:                                return;         /* flags and fd unchanged */
                    636:                        } else if (clientdev(st.st_dev) != f->dev || st.st_ino != f->ino) {
                    637:                                rflog("maxopen: wrong file %s\n", fs->name);
                    638:                                close(fd);
                    639:                                return;         /* flags and fd unchanged */
                    640:                        }
                    641:                }
                    642:                /* it's the same file, or we can't tell */
                    643:                fs->flags = flags;
                    644:                if (fs->fd >= 0)
                    645:                        close(fs->fd);
                    646:                fs->fd = fd;
                    647:        }
                    648: }
                    649: 
                    650: /*
                    651:  * is this the same file we opened?
                    652:  * call before operations that must use name rather than fd
                    653:  */
                    654: 
                    655: static int
                    656: samefile(f)
                    657: register Rfile *f;
                    658: {
                    659:        struct stat st;
                    660: 
                    661:        if (fsp(f)->flags & NOSTAT)
                    662:                return (1);     /* can't tell */
                    663:        if (stat(fsp(f)->name, &st) < 0)
                    664:                return (0);
                    665:        if (clientdev(st.st_dev) != f->dev || st.st_ino != f->ino)
                    666:                return (0);
                    667:        return (1);
                    668: }

unix.superglobalmegacorp.com

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