Annotation of 43BSDReno/sbin/quotacheck/quotacheck.c, revision 1.1

1.1     ! root        1: /*
        !             2:  * Copyright (c) 1980, 1990 Regents of the University of California.
        !             3:  * All rights reserved.
        !             4:  *
        !             5:  * This code is derived from software contributed to Berkeley by
        !             6:  * Robert Elz at The University of Melbourne.
        !             7:  *
        !             8:  * Redistribution and use in source and binary forms are permitted
        !             9:  * provided that: (1) source distributions retain this entire copyright
        !            10:  * notice and comment, and (2) distributions including binaries display
        !            11:  * the following acknowledgement:  ``This product includes software
        !            12:  * developed by the University of California, Berkeley and its contributors''
        !            13:  * in the documentation or other materials provided with the distribution
        !            14:  * and in all advertising materials mentioning features or use of this
        !            15:  * software. Neither the name of the University nor the names of its
        !            16:  * contributors may be used to endorse or promote products derived
        !            17:  * from this software without specific prior written permission.
        !            18:  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
        !            19:  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
        !            20:  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
        !            21:  */
        !            22: 
        !            23: #ifndef lint
        !            24: char copyright[] =
        !            25: "@(#) Copyright (c) 1980, 1990 Regents of the University of California.\n\
        !            26:  All rights reserved.\n";
        !            27: #endif /* not lint */
        !            28: 
        !            29: #ifndef lint
        !            30: static char sccsid[] = "@(#)quotacheck.c       5.14 (Berkeley) 6/1/90";
        !            31: #endif /* not lint */
        !            32: 
        !            33: /*
        !            34:  * Fix up / report on disk quotas & usage
        !            35:  */
        !            36: #include <sys/param.h>
        !            37: #include <ufs/dinode.h>
        !            38: #include <ufs/fs.h>
        !            39: #include <ufs/quota.h>
        !            40: #include <fstab.h>
        !            41: #include <pwd.h>
        !            42: #include <grp.h>
        !            43: #include <stdio.h>
        !            44: #include <errno.h>
        !            45: 
        !            46: union {
        !            47:        struct  fs      sblk;
        !            48:        char    dummy[MAXBSIZE];
        !            49: } un;
        !            50: #define        sblock  un.sblk
        !            51: long dev_bsize = 1;
        !            52: long maxino;
        !            53: 
        !            54: struct quotaname {
        !            55:        long    flags;
        !            56:        char    grpqfname[MAXPATHLEN + 1];
        !            57:        char    usrqfname[MAXPATHLEN + 1];
        !            58: };
        !            59: #define        HASUSR  1
        !            60: #define        HASGRP  2
        !            61: 
        !            62: struct fileusage {
        !            63:        struct  fileusage *fu_next;
        !            64:        u_long  fu_curinodes;
        !            65:        u_long  fu_curblocks;
        !            66:        u_long  fu_id;
        !            67:        char    fu_name[1];
        !            68:        /* actually bigger */
        !            69: };
        !            70: #define FUHASH 1024    /* must be power of two */
        !            71: struct fileusage *fuhead[MAXQUOTAS][FUHASH];
        !            72: struct fileusage *lookup();
        !            73: struct fileusage *addid();
        !            74: struct dinode *getnextinode();
        !            75: 
        !            76: int    aflag;                  /* all file systems */
        !            77: int    gflag;                  /* check group quotas */
        !            78: int    uflag;                  /* check user quotas */
        !            79: int    vflag;                  /* verbose */
        !            80: int    fi;                     /* open disk file descriptor */
        !            81: u_long highid[MAXQUOTAS];      /* highest addid()'ed identifier per type */
        !            82: 
        !            83: main(argc, argv)
        !            84:        int argc;
        !            85:        char **argv;
        !            86: {
        !            87:        register struct fstab *fs;
        !            88:        register struct passwd *pw;
        !            89:        register struct group *gr;
        !            90:        int i, argnum, maxrun, errs = 0;
        !            91:        long auxdata, done = 0;
        !            92:        char ch, *name, *blockcheck();
        !            93:        int needchk(), chkquota();
        !            94:        extern char *optarg;
        !            95:        extern int optind;
        !            96: 
        !            97:        while ((ch = getopt(argc, argv, "aguvl:")) != EOF) {
        !            98:                switch(ch) {
        !            99:                case 'a':
        !           100:                        aflag++;
        !           101:                        break;
        !           102:                case 'g':
        !           103:                        gflag++;
        !           104:                        break;
        !           105:                case 'u':
        !           106:                        uflag++;
        !           107:                        break;
        !           108:                case 'v':
        !           109:                        vflag++;
        !           110:                        break;
        !           111:                case 'l':
        !           112:                        maxrun = atoi(optarg);
        !           113:                        break;
        !           114:                default:
        !           115:                        usage();
        !           116:                }
        !           117:        }
        !           118:        argc -= optind;
        !           119:        argv += optind;
        !           120:        if ((argc == 0 && !aflag) || (argc > 0 && aflag))
        !           121:                usage();
        !           122:        if (!gflag && !uflag) {
        !           123:                gflag++;
        !           124:                uflag++;
        !           125:        }
        !           126:        if (gflag) {
        !           127:                setgrent();
        !           128:                while ((gr = getgrent()) != 0)
        !           129:                        (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
        !           130:                endgrent();
        !           131:        }
        !           132:        if (uflag) {
        !           133:                setpwent();
        !           134:                while ((pw = getpwent()) != 0)
        !           135:                        (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
        !           136:                endpwent();
        !           137:        }
        !           138:        if (aflag)
        !           139:                exit(checkfstab(1, maxrun, needchk, chkquota));
        !           140:        if (setfsent() == 0) {
        !           141:                fprintf(stderr, "Can't open ");
        !           142:                perror(FSTAB);
        !           143:                exit(8);
        !           144:        }
        !           145:        while ((fs = getfsent()) != NULL) {
        !           146:                if (((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
        !           147:                    (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) &&
        !           148:                    (auxdata = needchk(fs)) &&
        !           149:                    (name = blockcheck(fs->fs_spec))) {
        !           150:                        done |= 1 << argnum;
        !           151:                        errs += chkquota(name, fs->fs_file, auxdata);
        !           152:                }
        !           153:        }
        !           154:        endfsent();
        !           155:        for (i = 0; i < argc; i++)
        !           156:                if ((done & (1 << i)) == 0)
        !           157:                        fprintf(stderr, "%s not found in %s\n",
        !           158:                                argv[i], FSTAB);
        !           159:        exit(errs);
        !           160: }
        !           161: 
        !           162: usage()
        !           163: {
        !           164: 
        !           165:        fprintf(stderr, "Usage:\n\t%s\n\t%s\n",
        !           166:                "quotacheck [-g] [-u] [-v] -a",
        !           167:                "quotacheck [-g] [-u] [-v] filesys ...");
        !           168:        exit(1);
        !           169: }
        !           170: 
        !           171: needchk(fs)
        !           172:        register struct fstab *fs;
        !           173: {
        !           174:        register struct quotaname *qnp;
        !           175:        char *qfnp;
        !           176: 
        !           177:        if (strcmp(fs->fs_vfstype, "ufs") ||
        !           178:            strcmp(fs->fs_type, FSTAB_RW))
        !           179:                return (0);
        !           180:        if ((qnp = (struct quotaname *)malloc(sizeof *qnp)) == 0) {
        !           181:                fprintf(stderr, "out of memory for quota structures\n");
        !           182:                exit(1);
        !           183:        }
        !           184:        qnp->flags = 0;
        !           185:        if (gflag && hasquota(fs, GRPQUOTA, &qfnp)) {
        !           186:                strcpy(qnp->grpqfname, qfnp);
        !           187:                qnp->flags |= HASGRP;
        !           188:        }
        !           189:        if (uflag && hasquota(fs, USRQUOTA, &qfnp)) {
        !           190:                strcpy(qnp->usrqfname, qfnp);
        !           191:                qnp->flags |= HASUSR;
        !           192:        }
        !           193:        if (qnp->flags)
        !           194:                return ((int)qnp);
        !           195:        free((char *)qnp);
        !           196:        return (0);
        !           197: }
        !           198: 
        !           199: /*
        !           200:  * Scan the specified filesystem to check quota(s) present on it.
        !           201:  */
        !           202: chkquota(fsname, mntpt, qnp)
        !           203:        char *fsname, *mntpt;
        !           204:        register struct quotaname *qnp;
        !           205: {
        !           206:        register struct fileusage *fup;
        !           207:        register struct dinode *dp;
        !           208:        int cg, i, mode, errs = 0;
        !           209:        ino_t ino;
        !           210: 
        !           211:        if ((fi = open(fsname, 0)) < 0) {
        !           212:                perror(fsname);
        !           213:                return (1);
        !           214:        }
        !           215:        if (vflag) {
        !           216:                fprintf(stdout, "*** Checking ");
        !           217:                if (qnp->flags & HASUSR)
        !           218:                        fprintf(stdout, "%s%s", qfextension[USRQUOTA],
        !           219:                            (qnp->flags & HASGRP) ? " and " : "");
        !           220:                if (qnp->flags & HASGRP)
        !           221:                        fprintf(stdout, "%s", qfextension[GRPQUOTA]);
        !           222:                fprintf(stdout, " quotas for %s (%s)\n", fsname, mntpt);
        !           223:        }
        !           224:        sync();
        !           225:        bread(SBOFF, (char *)&sblock, (long)SBSIZE);
        !           226:        dev_bsize = sblock.fs_fsize / fsbtodb(&sblock, 1);
        !           227:        maxino = sblock.fs_ncg * sblock.fs_ipg;
        !           228:        resetinodebuf();
        !           229:        for (ino = 0, cg = 0; cg < sblock.fs_ncg; cg++) {
        !           230:                for (i = 0; i < sblock.fs_ipg; i++, ino++) {
        !           231:                        if (ino < ROOTINO)
        !           232:                                continue;
        !           233:                        if ((dp = getnextinode(ino)) == NULL)
        !           234:                                continue;
        !           235:                        if ((mode = dp->di_mode & IFMT) == 0)
        !           236:                                continue;
        !           237:                        if (qnp->flags & HASGRP) {
        !           238:                                fup = addid((u_long)dp->di_gid, GRPQUOTA,
        !           239:                                    (char *)0);
        !           240:                                fup->fu_curinodes++;
        !           241:                                if (mode == IFREG || mode == IFDIR ||
        !           242:                                    mode == IFLNK)
        !           243:                                        fup->fu_curblocks += dp->di_blocks;
        !           244:                        }
        !           245:                        if (qnp->flags & HASUSR) {
        !           246:                                fup = addid((u_long)dp->di_uid, USRQUOTA,
        !           247:                                    (char *)0);
        !           248:                                fup->fu_curinodes++;
        !           249:                                if (mode == IFREG || mode == IFDIR ||
        !           250:                                    mode == IFLNK)
        !           251:                                        fup->fu_curblocks += dp->di_blocks;
        !           252:                        }
        !           253:                }
        !           254:        }
        !           255:        freeinodebuf();
        !           256:        if (qnp->flags & HASUSR)
        !           257:                errs += update(mntpt, qnp->usrqfname, USRQUOTA);
        !           258:        if (qnp->flags & HASGRP)
        !           259:                errs += update(mntpt, qnp->grpqfname, GRPQUOTA);
        !           260:        close(fi);
        !           261:        return (errs);
        !           262: }
        !           263: 
        !           264: /*
        !           265:  * Update a specified quota file.
        !           266:  */
        !           267: update(fsname, quotafile, type)
        !           268:        char *fsname, *quotafile;
        !           269:        register int type;
        !           270: {
        !           271:        register struct fileusage *fup;
        !           272:        register FILE *qfi, *qfo;
        !           273:        register u_long id, lastid;
        !           274:        struct dqblk dqbuf;
        !           275:        extern int errno;
        !           276:        static int warned = 0;
        !           277:        static struct dqblk zerodqbuf;
        !           278:        static struct fileusage zerofileusage;
        !           279: 
        !           280:        if ((qfo = fopen(quotafile, "r+")) == NULL) {
        !           281:                if (errno != ENOENT) {
        !           282:                        perror(quotafile);
        !           283:                        return (1);
        !           284:                }
        !           285:                if ((qfo = fopen(quotafile, "w+")) == NULL) {
        !           286:                        perror(quotafile);
        !           287:                        return (1);
        !           288:                }
        !           289:                fprintf(stderr, "Creating quota file %s\n", quotafile);
        !           290:                (void) fchown(fileno(qfo), getuid(), getquotagid());
        !           291:                (void) fchmod(fileno(qfo), 0640);
        !           292:        }
        !           293:        if ((qfi = fopen(quotafile, "r")) == NULL) {
        !           294:                perror(quotafile);
        !           295:                fclose(qfo);
        !           296:                return (1);
        !           297:        }
        !           298:        if (quotactl(fsname, QCMD(Q_SYNC, type), (u_long)0, (caddr_t)0) < 0 &&
        !           299:            errno == EOPNOTSUPP && !warned && vflag) {
        !           300:                warned++;
        !           301:                fprintf(stdout, "*** Warning: %s\n",
        !           302:                    "Quotas are not compiled into this kernel");
        !           303:        }
        !           304:        for (lastid = highid[type], id = 0; id <= lastid; id++) {
        !           305:                if (fread((char *)&dqbuf, sizeof(struct dqblk), 1, qfi) == 0)
        !           306:                        dqbuf = zerodqbuf;
        !           307:                if ((fup = lookup(id, type)) == 0)
        !           308:                        fup = &zerofileusage;
        !           309:                if (dqbuf.dqb_curinodes == fup->fu_curinodes &&
        !           310:                    dqbuf.dqb_curblocks == fup->fu_curblocks) {
        !           311:                        fup->fu_curinodes = 0;
        !           312:                        fup->fu_curblocks = 0;
        !           313:                        fseek(qfo, (long)sizeof(struct dqblk), 1);
        !           314:                        continue;
        !           315:                }
        !           316:                if (vflag) {
        !           317:                        if (aflag)
        !           318:                                printf("%s: ", fsname);
        !           319:                        printf("%-8s fixed:", fup->fu_name);
        !           320:                        if (dqbuf.dqb_curinodes != fup->fu_curinodes)
        !           321:                                fprintf(stdout, "\tinodes %d -> %d",
        !           322:                                        dqbuf.dqb_curinodes, fup->fu_curinodes);
        !           323:                        if (dqbuf.dqb_curblocks != fup->fu_curblocks)
        !           324:                                fprintf(stdout, "\tblocks %d -> %d",
        !           325:                                        dqbuf.dqb_curblocks, fup->fu_curblocks);
        !           326:                        fprintf(stdout, "\n");
        !           327:                }
        !           328:                /*
        !           329:                 * Reset time limit if have a soft limit and were
        !           330:                 * previously under it, but are now over it.
        !           331:                 */
        !           332:                if (dqbuf.dqb_bsoftlimit &&
        !           333:                    dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
        !           334:                    fup->fu_curblocks >= dqbuf.dqb_bsoftlimit)
        !           335:                        dqbuf.dqb_btime = 0;
        !           336:                if (dqbuf.dqb_isoftlimit &&
        !           337:                    dqbuf.dqb_curblocks < dqbuf.dqb_isoftlimit &&
        !           338:                    fup->fu_curblocks >= dqbuf.dqb_isoftlimit)
        !           339:                        dqbuf.dqb_itime = 0;
        !           340:                dqbuf.dqb_curinodes = fup->fu_curinodes;
        !           341:                dqbuf.dqb_curblocks = fup->fu_curblocks;
        !           342:                fwrite((char *)&dqbuf, sizeof(struct dqblk), 1, qfo);
        !           343:                (void) quotactl(fsname, QCMD(Q_SETUSE, type), id,
        !           344:                    (caddr_t)&dqbuf);
        !           345:                fup->fu_curinodes = 0;
        !           346:                fup->fu_curblocks = 0;
        !           347:        }
        !           348:        fclose(qfi);
        !           349:        fflush(qfo);
        !           350:        ftruncate(fileno(qfo),
        !           351:            (off_t)((highid[type] + 1) * sizeof(struct dqblk)));
        !           352:        fclose(qfo);
        !           353:        return (0);
        !           354: }
        !           355: 
        !           356: /*
        !           357:  * Check to see if target appears in list of size cnt.
        !           358:  */
        !           359: oneof(target, list, cnt)
        !           360:        register char *target, *list[];
        !           361:        int cnt;
        !           362: {
        !           363:        register int i;
        !           364: 
        !           365:        for (i = 0; i < cnt; i++)
        !           366:                if (strcmp(target, list[i]) == 0)
        !           367:                        return (i);
        !           368:        return (-1);
        !           369: }
        !           370: 
        !           371: /*
        !           372:  * Determine the group identifier for quota files.
        !           373:  */
        !           374: getquotagid()
        !           375: {
        !           376:        struct group *gr;
        !           377: 
        !           378:        if (gr = getgrnam(quotagroup))
        !           379:                return (gr->gr_gid);
        !           380:        return (-1);
        !           381: }
        !           382: 
        !           383: /*
        !           384:  * Check to see if a particular quota is to be enabled.
        !           385:  */
        !           386: hasquota(fs, type, qfnamep)
        !           387:        register struct fstab *fs;
        !           388:        int type;
        !           389:        char **qfnamep;
        !           390: {
        !           391:        register char *opt;
        !           392:        char *cp, *index(), *strtok();
        !           393:        static char initname, usrname[100], grpname[100];
        !           394:        static char buf[BUFSIZ];
        !           395: 
        !           396:        if (!initname) {
        !           397:                sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
        !           398:                sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
        !           399:                initname = 1;
        !           400:        }
        !           401:        strcpy(buf, fs->fs_mntops);
        !           402:        for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
        !           403:                if (cp = index(opt, '='))
        !           404:                        *cp++ = '\0';
        !           405:                if (type == USRQUOTA && strcmp(opt, usrname) == 0)
        !           406:                        break;
        !           407:                if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
        !           408:                        break;
        !           409:        }
        !           410:        if (!opt)
        !           411:                return (0);
        !           412:        if (cp) {
        !           413:                *qfnamep = cp;
        !           414:                return (1);
        !           415:        }
        !           416:        (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
        !           417:        *qfnamep = buf;
        !           418:        return (1);
        !           419: }
        !           420: 
        !           421: /*
        !           422:  * Routines to manage the file usage table.
        !           423:  *
        !           424:  * Lookup an id of a specific type.
        !           425:  */
        !           426: struct fileusage *
        !           427: lookup(id, type)
        !           428:        u_long id;
        !           429:        int type;
        !           430: {
        !           431:        register struct fileusage *fup;
        !           432: 
        !           433:        for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
        !           434:                if (fup->fu_id == id)
        !           435:                        return (fup);
        !           436:        return ((struct fileusage *)0);
        !           437: }
        !           438: 
        !           439: /*
        !           440:  * Add a new file usage id if it does not already exist.
        !           441:  */
        !           442: struct fileusage *
        !           443: addid(id, type, name)
        !           444:        u_long id;
        !           445:        int type;
        !           446:        char *name;
        !           447: {
        !           448:        struct fileusage *fup, **fhp;
        !           449:        int len;
        !           450:        extern char *calloc();
        !           451: 
        !           452:        if (fup = lookup(id, type))
        !           453:                return (fup);
        !           454:        if (name)
        !           455:                len = strlen(name);
        !           456:        else
        !           457:                len = 10;
        !           458:        if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL) {
        !           459:                fprintf(stderr, "out of memory for fileusage structures\n");
        !           460:                exit(1);
        !           461:        }
        !           462:        fhp = &fuhead[type][id & (FUHASH - 1)];
        !           463:        fup->fu_next = *fhp;
        !           464:        *fhp = fup;
        !           465:        fup->fu_id = id;
        !           466:        if (id > highid[type])
        !           467:                highid[type] = id;
        !           468:        if (name) {
        !           469:                bcopy(name, fup->fu_name, len + 1);
        !           470:        } else {
        !           471:                sprintf(fup->fu_name, "%u", id);
        !           472:        }
        !           473:        return (fup);
        !           474: }
        !           475: 
        !           476: /*
        !           477:  * Special purpose version of ginode used to optimize pass
        !           478:  * over all the inodes in numerical order.
        !           479:  */
        !           480: ino_t nextino, lastinum;
        !           481: long readcnt, readpercg, fullcnt, inobufsize, partialcnt, partialsize;
        !           482: struct dinode *inodebuf;
        !           483: #define        INOBUFSIZE      56*1024 /* size of buffer to read inodes */
        !           484: 
        !           485: struct dinode *
        !           486: getnextinode(inumber)
        !           487:        ino_t inumber;
        !           488: {
        !           489:        long size;
        !           490:        daddr_t dblk;
        !           491:        static struct dinode *dp;
        !           492: 
        !           493:        if (inumber != nextino++ || inumber > maxino) {
        !           494:                fprintf(stderr, "bad inode number %d to nextinode\n", inumber);
        !           495:                exit(1);
        !           496:        }
        !           497:        if (inumber >= lastinum) {
        !           498:                readcnt++;
        !           499:                dblk = fsbtodb(&sblock, itod(&sblock, lastinum));
        !           500:                if (readcnt % readpercg == 0) {
        !           501:                        size = partialsize;
        !           502:                        lastinum += partialcnt;
        !           503:                } else {
        !           504:                        size = inobufsize;
        !           505:                        lastinum += fullcnt;
        !           506:                }
        !           507:                bread(dblk, (char *)inodebuf, size);
        !           508:                dp = inodebuf;
        !           509:        }
        !           510:        return (dp++);
        !           511: }
        !           512: 
        !           513: /*
        !           514:  * Prepare to scan a set of inodes.
        !           515:  */
        !           516: resetinodebuf()
        !           517: {
        !           518: 
        !           519:        nextino = 0;
        !           520:        lastinum = 0;
        !           521:        readcnt = 0;
        !           522:        inobufsize = blkroundup(&sblock, INOBUFSIZE);
        !           523:        fullcnt = inobufsize / sizeof(struct dinode);
        !           524:        readpercg = sblock.fs_ipg / fullcnt;
        !           525:        partialcnt = sblock.fs_ipg % fullcnt;
        !           526:        partialsize = partialcnt * sizeof(struct dinode);
        !           527:        if (partialcnt != 0) {
        !           528:                readpercg++;
        !           529:        } else {
        !           530:                partialcnt = fullcnt;
        !           531:                partialsize = inobufsize;
        !           532:        }
        !           533:        if (inodebuf == NULL &&
        !           534:           (inodebuf = (struct dinode *)malloc((unsigned)inobufsize)) == NULL) {
        !           535:                fprintf(stderr, "Cannot allocate space for inode buffer\n");
        !           536:                exit(1);
        !           537:        }
        !           538:        while (nextino < ROOTINO)
        !           539:                getnextinode(nextino);
        !           540: }
        !           541: 
        !           542: /*
        !           543:  * Free up data structures used to scan inodes.
        !           544:  */
        !           545: freeinodebuf()
        !           546: {
        !           547: 
        !           548:        if (inodebuf != NULL)
        !           549:                free((char *)inodebuf);
        !           550:        inodebuf = NULL;
        !           551: }
        !           552: 
        !           553: /*
        !           554:  * Read specified disk blocks.
        !           555:  */
        !           556: bread(bno, buf, cnt)
        !           557:        daddr_t bno;
        !           558:        char *buf;
        !           559:        long cnt;
        !           560: {
        !           561: 
        !           562:        if (lseek(fi, bno * dev_bsize, 0) < 0) {
        !           563:                perror("lseek");
        !           564:                exit(1);
        !           565:        }
        !           566: 
        !           567:        if (read(fi, buf, cnt) != cnt) {
        !           568:                perror("read");
        !           569:                exit(1);
        !           570:        }
        !           571: }

unix.superglobalmegacorp.com

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