home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / sbin / quotacheck / quotacheck.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-04-16  |  14.2 KB  |  595 lines

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